1.什么是Lambda表达式
Lambda表达式实际上是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更加简洁,灵活的代码。作为一种更紧凑的代码风格,使得Java的语言表达能力得到提升。
@Test
public void test1() {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello, World!");//Hello, World!
}
};
r.run();
//Lambda表达式
Runnable r2 = () -> System.out.println("Hello, Lambda!");//Hello, Lambda!
r2.run();
}
2.Lambda表达式语法
Lambda表达式在Java语言中引入了一个新的语法元素操作符 -> ,该操作符被称为Lambda操作符或箭头操作符。它将Lambda分割成为两部分:左侧和右侧。
左侧:指定了Lambda表达式所需要的参数列表。
右侧:指定了Lambda体,即Lambda表达式执行的代码功能。
Lambda表达式采用了上线文类型推断,可以在左侧参数列表中
3.什么函数式接口
只包含一个抽象方法的接口称为函数式接口,使用@FunctionalInteface注解标识。函数式接口也是可以有default方法的,但是,只能有一个未实现的方法。
4.Java内置核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
java.util.function.Consumer<T> | T | void | 对类型为T的对象应用操作,包含方法void accept(T t); 消费型 |
java.util.function.Supplier<T> | 无 | T | 返回类型为T的对象,包含方法 T get(); 供给型 |
java.util.function.Function<T,R> | T | R | 参数类型T,返回类型R,包含方法 R apply(T t); 函数型 |
java.util.function.Predicate<T> | T | boolean | 参数类型T,返回类型boolean,包含方法 boolean test(T t); 断言型 |
@Test
public void test2() {
Consumer<String> consumer = x -> System.out.println(x);
consumer.accept("我是一个带参数无返回类型消费型Consumer");//我是一个带参数无返回类型Consumer
Supplier<String> supplier = () -> "我是一个无参有返回值的供给型Supplier";
System.out.println(supplier.get());//我是一个无参有返回值的供给型Supplier
Function<String, String> function = (x) -> x.toUpperCase();
System.out.println(function.apply("我是一个带有参数和返回值的函数式接口,lambda!"));//我是一个带有参数和返回值的函数式接口,LAMBDA!
Predicate<String> predicate = x -> x.contains("Lambda");
System.out.println(predicate.test("Test,Lambda"));//true
}
5.其他内置函数式接口
在java.util.function.* 包下可以看到内置的其他函数式接口
6.自定义函数式接口
基本上内置的函数式接口通常会满足我们的需求,当不能满足我们需求的时候需要自己自定义函数接口。
1.新建MyFun.class的接口。
2.添加注解@FunctionalInteface。
3.此接口只能有一个抽象方法,多了则会编译报错。
@FunctionalInterface
public interface MyFun<T> {
void print(T t);
}
@Test
public void test3() {
MyFun<String> fun = x -> System.out.println(x);
fun.print("自定义函数式接口");//自定义函数式接口
}
7.方法引用与构造器引用
方法引用:若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用(可以将方法引用理解为 Lambda 表达式的另外一种表现形式)主要有三种情况: 1.对象::实例方法
2.类::静态方法
3.类::实例方法
1.对象::实例方法例子
@Test
public void test4() {
PrintStream ps = System.out;
Consumer<String> consumer = (x) -> ps.println(x);
consumer.accept("对象引用::实例方法名");//对象引用::实例方法名
System.out.println("-----------------------");
Consumer<String> consumer2 = ps::println;
consumer2.accept("对象引用::实例方法名");//对象引用::实例方法名
System.out.println("-----------------------");
Consumer<String> consumer3 = System.out::println;
consumer3.accept("对象引用::实例方法名");//对象引用::实例方法名
}
注意:
①方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!
②若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
Employee emp = new Employee(101, "张三", 18, 9999.99);
Supplier<String> sup = () -> emp.getName();
System.out.println(sup.get());//张三
System.out.println("------------------------");
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());//张三
2.类::静态方法
@Test
public void test5() {
BiFunction<Double, Double, Double> fun = (x,y) -> Math.max(x, y);
System.out.println(fun.apply(1D,0D));
BiFunction<Double, Double, Double> fun2 = Math::max;
System.out.println(fun2.apply(1D,0D));
Comparator<Integer> com = (x, y) -> Integer.compare(x,y);
System.out.println("--------------------------");
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(1,2));
}
3.类::实例方法
@Test
public void test6() {
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
System.out.println(bp.test("abcde", "abcde"));
System.out.println("-----------------------------------------");
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("abc", "abc"));
System.out.println("-----------------------------------------");
Function<Employee, String> fun = (e) -> e.show();
System.out.println(fun.apply(new Employee()));
System.out.println("-----------------------------------------");
Function<Employee, String> fun2 = Employee::show;
System.out.println(fun2.apply(new Employee()));
}
构造器引用 :构造器的参数列表,需要与函数式接口中参数列表保持一致!
@Test
public void test7(){
Supplier<Employee> sup = () -> new Employee();
System.out.println(sup.get());//Employee [id=0, name=null, age=0, salary=0.0]
System.out.println("------------------------------------");
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());//Employee [id=0, name=null, age=0, salary=0.0]
Function<String, Employee> fun = Employee::new;
System.out.println(fun.apply("aaa"));//Employee [id=0, name=aaa, age=0, salary=0.0]
BiFunction<String, Integer, Employee> fun2 = Employee::new;
System.out.println(fun2.apply("aaa",1));//Employee [id=0, name=aaa, age=1, salary=0.0]
}
数组引用
@Test
public void test8(){
Function<Integer, String[]> fun = (args) -> new String[args];
String[] strs = fun.apply(10);
System.out.println(strs.length);//10
System.out.println("--------------------------");
Function<Integer, Employee[]> fun2 = Employee[] :: new;
Employee[] emps = fun2.apply(20);
System.out.println(emps.length);//20
}
8.Lambda表达式本质探寻
前面,我们讲了一些Lambda表达的一些语法,我们说Lambda的本质实际上就是一个匿名函数,比如Runnable接口的run方法,在jdk7以前,通常的写法是new一个匿名的内部类在run方法里面实现功能,然而我们发现run方法里面真正有用的代码实际上就只有一行System.out.println("Hello World!" + num);
为了实现这个功能,从而写了很多不必要的代码,代码的可读性降低了,java的工程师为了解决这样的繁琐的代码问题而实现了一个Lambda表达式,Lambda表达式代替了匿名内部类真正要实现的代码功能。如下
@Test
public void test1(){
int num = 0;//jdk 1.7 前,必须是 final
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!" + num);
}
};
r.run();
System.out.println("-------------------------------");
Runnable r1 = () -> System.out.println("Hello Lambda!");
r1.run();
}
Runnable r1 = () -> System.out.println("Hello Lambda!");
一行代码就此解决,从代码的角度来说,可读性增加了,更加简洁。我们注意观察Runnable接口
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
1.它是一个接口
2.接口中出现了@FunctionalInterface
3.抽象方法run无参数无返回值
我们在来看这个,Lambda表达式是由三部分组成而来,左侧参数值列表 ->Lambda操作符 和 Lambda体。
Runnable r1 = () -> System.out.println("Hello Lambda!");
左边的参数对应run中的无参数方法,Lambda体直接输出一句话Hello Lambda 对应无返回值的run。Lambda表达式刚好与接口中抽象方法保持一致。要是接口中有多个抽象方法,怎么调用? @FunctionalInterface注解标志着这个接口中只能有一个未实现的抽象方法,超过两个则会编译出错。
结论:要实现一个Lambda表达式需要与函数式接口中的抽象方法保持一致。
9.Lambda表达式练习
根据结论,要实现一个Lambda表达式需要与函数式接口中的抽象方法保持一致。我们来找找一些函数式接口。
@Test
public void test9() {
//打印一句话
Runnable r = () -> System.out.println("hello,lambda!");
Consumer<String> con = (x)-> System.out.println("hello,lambda!");
//如果参数列表只有一个值,()可以省略
Consumer<String> con2 = x-> System.out.println("hello,lambda!");
//1.如果有多行Lambda体需要用{}包起来
Comparator<Integer> com = (x,y)-> {
if (x > y) {
return 1;
} else if (x<y) {
return -1;
} else {
return 0;
}
};
//2.如果只有一行可以不用写{}并且return也可以省略
Comparator<Integer> com2 = (x,y)-> Integer.compare(x,y);
//3.使用方法引用
Comparator<Integer> com3 = Integer::compare;
//创建对象
Function<String,String> fun = String::new;
Function<Integer,String[]> fun2 = String[]::new;
Supplier<Object> sup = Object::new;
}