JDK1.8之Lambda表达式,方法引用及构造引用
Lambda表达式
Lambda 是一个匿名函数,我们可以把 Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。
举例:在对集合中的数据进行排序时,需要一个Comparator接口的匿名内部类,这里,匿名内部类就可以用Lambda表达式进行简写
public class MyTest1 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(20);
list.add(0);
list.add(2);
list.add(222);
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return b-a;
}
});
System.out.println(list);
//对传入的匿名内部类,就可以使用Lambda做简化
list.sort((a,b)->a-b);
System.out.println(list);
}
}
程序运行结果:
[222, 20, 2, 0]
[0, 2, 20, 222]
Lambda表达式的语法
JDK1.8 引入了一个箭头符号 ->
这个箭头符号 将Lambda表达式分为 左右两部分。 左边->右边
左边:写的是你实现的这个接口中的抽象方法的 形参列表
右边:写的是你对接口中这个抽象方法的具体实现逻辑。
public class MyTest2 {
public static void main(String[] args) {
//方式1:我们就采用匿名内部类的方式
MyInterface myInterface = new MyInterface() {
@Override
public void show(int a, int b) {
System.out.println(a + b);
}
};
//采用Lambda表达式,对匿名内部类进行简写。
//第一步简写。抽象方法的参数列表->具体的实现逻辑
MyInterface myInterface1=(int a,int b)->{
System.out.println(a + b);
};
//第二步简写:可以省略掉形参列表的参数的数据类型
MyInterface myInterface2=( a, b)->{
System.out.println(a + b);
};
//第三步简写:我们对抽象方法的实现逻辑,只有一行代码那么方法体的大括号可以省略不写
MyInterface myInterface3=( a, b)-> System.out.println(a + b);
}
}
这里注意:如果形参只有一个参数的数据类型和()括号都可以省略不写
public interface MyInterface3 {
public abstract int show(int a);
}
public class MyTest2 {
public static void main(String[] args) {
MyInterface3 myInterface3 = new MyInterface3() {
@Override
public int show(int a) {
return a + 100;
}
};
MyInterface3 myInterface4=(int a) ->{
return a + 100;
};
MyInterface3 myInterface5=( a) -> a + 100;
//如果形参只有一个参数的数据类型和()括号都可以省略不写
MyInterface3 myInterface6=a -> a + 100;
}
}
快速生成Lambda表达式的方法:
先写成匿名内部类。然后你按alt+enter一提示,可以让IDEA给你转换成Lambda表达式
Lambda表达式的使用限制
Lambda表达式的使用限制:Lambda表达式,需要函数式接口的支持。
函数式接口:这个接口中,只有一个抽象方法,这种接口我们就称之为函数式接口。
有一个注解 @FunctionalInterface 可以检测这个接口是不是一个函数式接口
JDK1.8之后,他提供了很多的函数式接口来作为参数传递。
举例:Consumer接口
import java.util.function.Consumer;
public class MyTest2 {
public static void main(String[] args) {
//JDK1.8之后,他提供了很多的函数式接口。来作为参数传递。
Consumer consumer = new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o);
}
};
Consumer consumer2=o-> System.out.println(o);
}
}
方法引用
方法引用其实是对Lambda表达式的进一步简写
先看一个例子
public class MyTest1 {
public static void main(String[] args) {
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("hello");
System.out.println("=============");
Consumer<String> consumer2=x-> System.out.println(x);
consumer2.accept("hello2222");
System.out.println("============");
//方法引用
Consumer<String> consumer3=System.out::println;
consumer3.accept("hello33333");
}
}
方法引用:对接口中的抽象方法 public void accept(String s) 的具体实现逻辑,就是System.out.println(x);
观察接口中的抽象方法 accept(String s) 这个抽象方法 返回值是void ,也是有一个参数。而这个抽象方法的具体实现,System.out.println(x); 实现逻辑是用了一个对象PrintStream调用了一个println(x)方法,那么println(x)观察发现,这个方法返回值也是void ,也是一个参数。
那么println(x)方法的返回值和参数列表 正好跟重写的这个accept(String s)方法的返回值和参数列表个数一致,那么就可以使用方法引用进行简写
方法引用的第一种语法是 对象::实例方法
例子:
public class MyTest2 {
public static void main(String[] args) {
Consumer consumer2= x->{
PrintStream out = System.out;
out.println(x);
};
consumer2.accept("hello22222");
System.out.println("===============");
Consumer consumer3=System.out::println;
consumer3.accept("hello3");
}
}
方法引用的第二种语法是 类名::静态方法
public class MyTest3 {
public static void main(String[] args) {
BinaryOperator<Double> binaryOperator = new BinaryOperator<Double>(){
@Override
public Double apply(Double a, Double b) {
double max = Math.max(a, b);
return max;
}
};
System.out.println("===================");
//使用Lambda表达式进行简写
BinaryOperator<Double> binaryOperator1 = (a, b) -> Math.max(a, b);
System.out.println("=======================");
//类名::静态方法
BinaryOperator<Double> binaryOperator2=Math::max;
Double apply = binaryOperator2.apply(5.0, 20.0);
System.out.println(apply);
}
}
方法引用的第三种语法是 类名::实例方法
使用这种方式进行方法引用简写时,有一个特点,在重写接口中的方法中,传入的两个参数。一个参数作为了调用者,一个参数作为了传入者。
看一个例子:
public class MyTest6 {
public static void main(String[] args) {
Comparator<String> stringComparator = new Comparator<String>(){
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
};
System.out.println("================");
Comparator<String> comparator=(s1,s2)->s1.compareTo(s2);
System.out.println("================");
//方法引用: 类名::实例方法
//我们重写接口中的方法,传入的两个参数。一个参数作为了调用者,一个参数作为了传入者。
Comparator<String> comparator2=String::compareTo;
}
}
构造引用
对抽象方法get() 的重写逻辑是 用new 调用了一个空参构造方法 Student()
这个空参构造方法 Student() 没有参数,而且new完之后,会返回一个对象了
正好和 get() 方法的 参数列表和返回值类型能对应上,那么就可以使用构造引用
看一个例子:找一个函数式接口,这个接口中的抽象方法没有形参, 这个方法有一个泛型返回值。这时就可以使用空参的构造引用
构造引用的语法: 类名::new
public class MyTest1 {
public static void main(String[] args) {
Supplier<Student> supplier = new Supplier<Student>() {
@Override
public Student get() {
return new Student();
}
};
System.out.println("==================");
//构造引用
Supplier<Student> supplier2=Student::new;
}
}
当使用有参的构造引用时,找一个函数式接口,这个接口中的抽象方法要两个参数 ,同时这个方法还得有一个返回值。
BiFunction<T, U, R> T是方法的第一个形参类型,U是方法的第二个形参类型 R是方法的返回 值类型
有参的构造引用的语法和无参的一样,也是:类名::new
public class MyTest2 {
public static void main(String[] args) {
BiFunction<String, Integer, Student> biFunction = new BiFunction<String, Integer, Student>() {
@Override
public Student apply(String name, Integer age) {
return new Student(name, age);
}
};
Student student = biFunction.apply("张三", 23);
System.out.println("=================");
//使用构造引用进行简化
BiFunction<String, Integer, Student> biFunction1=Student::new;
Student student = biFunction1.apply("张三", 23);
}
}