【JDK】聊聊Java 8的新特性之Lambda表达式

Java 8的新特性之Lambda表达式

jdk1.8 自从其发布以来一直都是 Java 开发的一个主要版本,其新增了非常多的特性,支持函数式编程
而 Lambda 表达式则是 jdk1.8 的一个核心之一

1. Lambda表达式简介

Lambda 表达式是一个匿名函数,可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更加简洁、灵活的代码。作为一种更为紧凑的代码风格,使 Java 语言表达能力得到提升

语法糖:指使用更加f方便,但是原理不变的代码语法。例如在遍历集合时使用的 for-each 语法,其底层的实现原理仍然是迭代器。从应用层面上讲,Java 中d的 Lambda 表达式可以被当作匿名内部类的语法糖,但是二者在原理上是不同的。

以下是 Lambda 表达式的重要特征

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值

语法对比
匿名内部类demo

//原来的匿名内部类
Comparator com = new Comparator() {
	@Override
	public int compare(Integer o1, Integer o2) {
		// TODO Auto-generated method stub
		return Integer.compare(o1, o2);
	}
};
		
TreeSet ts = new TreeSet<>(com);

使用 lambda 表达式改写

Comparator com = (x,y) -> Integer.compare(x, y);
TreeSet ts = new TreeSet<>(com);

2. Lambda优化场景

需求,按按条件查询数据
如:获取学生中年龄大于 20 的信息
创建实体类Student

package com.jiker.bean;

public class Student {
	
	private Integer id;
	private String sName;
	private Integer age;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getsName() {
		return sName;
	}
	public void setsName(String sName) {
		this.sName = sName;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Student [id=" + id + ", sName=" + sName + ", age=" + age + "]";
	}
		public Student(Integer id, String sName, Integer age) {
		super();
		this.id = id;
		this.sName = sName;
		this.age = age;
	}
	
}

由于本次实验不涉及数据库的操作,可以将学生信息存储在一个 List 中

java.util.List list = Arrays.asList(
			new Student(1, "zhangsan", 19),
			new Student(2, "lisi", 22),
			new Student(3, "wangwu", 20),
			new Student(4, "zhaoliu", 17),
			new Student(5, "liuqi", 21)
		);

1、原始方法

public java.util.List filterStudent(java.util.List list){
	java.util.List stu = new ArrayList<>();
		
	for (Student student : list) {
		if (student.getAge() > 20) {
			stu.add(student);
		}
	}
	return stu;
}

若此时,还有一个需求即查找 id 大于 2 的学生信息,则代码为

public java.util.List filterStudent2(java.util.List list){
	java.util.List stu = new ArrayList<>();
		
	for (Student student : list) {
		if (student.getId() > 20) {
			stu.add(student);
		}
	}
	return stu;
}

可以看到,两者仅有 if 判断条件中的语句不一样
此时,若项目中有多种类似需求时,则会出现大量的冗余代码

2、优化方式1:定义接口(策略设计模式)

//接口定义
package com.jiker.predicate;

public interface MyPredicate {
	public boolean test(T t);
}

实现类过滤器
package com.jiker.predicate;

import com.jiker.bean.Student;

public class FilterStudentByAge implements MyPredicate {

	@Override
	public boolean test(Student student) {
		// TODO Auto-generated method stub
		return student.getAge() > 20;
	}

}
public List filerStud(List list,MyPredicate mp){
	List students = new ArrayList<>();
	for (Student student : list) {
		if (mp.test(student)) {
			students.add(student);
		}
	}
	return students;
}

//测试
List students =  filerStud(list, new FilterStudentByAge());
for (Student student : students) {
	System.out.println(student);
}

不足:每次实现一个新的需求时,都需要新创建一个 MyPredicate 的实现类

3、优化方式2:匿名内部类

List students = filerStud(list, new MyPredicate() {

	@Override
	public boolean test(Student t) {
		// TODO Auto-generated method stub
		return t.getAge() > 20;
	}
});
for (Student student : students) {
	System.out.println(student);
}

4、优化方式3:Lambda 表达式

//Lambda表达式
List students = filerStud(list, (e) ->e.getAge() > 20);
list.forEach(System.out::println);

3. Lambda表达式基础语法

Java 8 中引入了一个新的操作符:"->",该操作符称为箭头操作符或 Lambda 操作符
箭头操作符将 Lambda 表达式拆分成两部分
左侧:Lambda 表达式的参数列表
右侧:Lambda 表达式中所需执行的功能,即: Lambda 体

语法格式一:无参数、无返回值

() -> System.out.println("无返回值");

如:

//原始方式
Runnable r = new Runnable(){
	@Override
	public void run(){
		System.out.print("run ...");
	}
}
r.run();

//改写
Runnable r = () -> System.out.print("run ...");
r.run();

注意事项:若在局部内部类中应用了一个同级别的变量,在 JDK1.7 中,该变量需要是 final 类型
而在 JDK1.8 中,不需要定义变量为 final 类型,但仍然不能修改变量的值,同时 Lambda 表达式也得遵守这一规则

int num = 0;
Runnable r = () -> System.out.print("run ..." + num); //编译通过

Runnable r = () -> System.out.print("run ..." + num++); //编译报错

语法格式二:有参数,无返回值
若只有一个参数,小括号可以不写

Consumer con = (x) -> System.out.print(X);//推荐使用
Consumer con = x -> System.out.print(X);

con.accept("Lambda语法")

语法格式三:有参数,有返回值
若 Lambda 体中有多条语句,则需要使用大括号
若仅有一条返回语句,则大括号和 return 关键字都可省略

Comparator com = (x,y) -> {
	System.out.println("使用大括号");
	return Integer.compare(x,y); 
}

语法格式四:Lambda 表达式的数据类型可以省略不写
因为 JVM 的编译器可以通过上下文推断出数据类型,称为 “类型推断”
相同原理:

String[] str = {"a","b","c"};

List list = new ArrayList<>(); //JDK1.7

//JDK1.8
public void show(Map map){}
show(new HashMap<>());
... ...

4. Lambda表达式与函数式接口

函数式接口 (Functional Interface) 也是 Java 8 的另一个新特性
函数式接口:在接口中有且只有一个抽象方法,但可以有多个非抽象方法的接口(默认、静态、私有)
函数式接口,即适用于函数式编程场景的接口。而 Java 中的函数式编程体现就是 Lambda,所以函数式接口就是可以适用于 Lambda 使用的接口。只有确保接口中有且仅有一个抽象方法,Java 中的 Lambda 表达式才能顺利地进行推导

格式:
只要确保接口中有且仅有一个抽象方法即可

修饰符 interface 接口名称{
	public abstract 返回值类型 方法名称(可选参数信息)l

	//其他非抽象方法内容
}

由于接口中抽象方法的 public abstract 是可以省略的,所以定义一个函数式接口非常简单

public interface MyFunctionalInterface{
	void myMethod();
}

同时,可以使用注解 @FunctionalInterface 加在某一接口上,检查该接口是否为一个函数式接口

/**
 * An informative annotation type used to indicate that an interface
 * type declaration is intended to be a functional interface as
 * defined by the Java Language Specification.
 * /

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

Java 内置四大核心函数式接口

函数式接口 参数类型 返回类型 用途
Consumer< T> 消费型接口 T void 对类型为 T 的对象应用操作,包含方法 void accept(T t)
Supplier< T> 供给型接口 T 返回类型为 T 的对象,包含方法 T get()
Function< T,R> 函数型接口 T R 对类型为 T 的对象应用操作,并返回结果。结果是 R 类型的对象,包含方法 R apply(T t)
Predicate< T> 断定型接口 T boolean 确定类型为 T 的对象是否满足某约束,并返回 boolean 值,包含方法 boolean testt(T t)

Example1:消费型接口

@Test
public void test1(){
	cons(1000,(m) -> System.out.println("消费 " + m));
}

public void cons(Integer money,Consumer con){
	con.accept(money)
}

Example2:供给型接口

//产生一些整数,放入集合
public List getList(int num Supplier sup){
	List list = new ArrayList<>();
	for(int i = 0;i < num;i++){
		Interger n = sup.get();
		list.add(n);
	}
	return list;
}

@Test
public void test2(){
    List list = getList(10,() -> (int)(Math.random() * 100));
}

Example3:函数型接口

//处理字符串
public String strHandler(String str,Function fun){
	return fun.apply(str);
}

@Test
public void test3(){
	String str = strHandler("\t\t\t  Lambda ",(str) -> str.trim);
}

Example4:断定型接口

//将满足条件的字符串f放入集合
public List filterStr(List list,Predicate pre){
	List strList = new ArrayList<>();
	for(String str : list){
		strList.add(str);
	}
	return strList;
}

@Test
public void test4(){
	List list = Arrays,asList("Hello","Jiker","Lambda");
	List list2 = filterStr(list,(s) -> s.length() > 3);
}

时间:2019.6.28 19:27

你可能感兴趣的:(java,java,jdk8)