Lambda表达式
1 什么是Lambda表达式
Lambda表达式是JDK1.8开始之后的新技术,是一种代码的新语法,其核心目的是为了简化匿名内部类的代码写法。
2 Lambda表达式的使用前提
Lambda表达式只能简化函数式接口的匿名内部类写法。函数式接口指的是只有一个抽象方法的接口。
java
使用@FunctionalInterface
注解来表示函数式接口:
一旦某个接口加上了这个注解,这个接口只能有且仅有一个抽象方法。
这个接口就可以被Lambda表达式简化。
比如说java
多线程编程中常用的Runnable
接口:
3 Lambda表达式写法
Lambda表达式的标准格式为:
(参数类型 参数名称) -> { 代码语句 }
格式说明:
- 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
->
是新引入的语法格式,代表指向动作。- 大括号内的语法与传统方法体要求基本一致。
Runnable 代码示例1:
public class RunnableDemo {
public static void main(String[] args) {
// 传统的匿名内部类写法
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":执行~~~");
}
});
t.start();
// Lambda表达式写法
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName()+":执行~~~");
});
t1.start();
}
}
Comparator 代码示例2:
public class CollectionsDemo {
public static void main(String[] args) {
List<Student> lists = new ArrayList<>();
Student s1 = new Student("李铭",18,'男');
Student s2 = new Student("冯龙",23,'男');
Student s3 = new Student("王乐乐",21,'男');
Collections.addAll(lists , s1 , s2 , s3);
// 传统的匿名内部类写法,升序排序!
Collections.sort(lists, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
return s1.getAge() - s2.getAge();
}
});
// Lambda表达式简化写法
Collections.sort(lists ,(Student t1, Student t2) -> {
return t1.getAge() - t2.getAge();
});
System.out.println(lists);
}
}
4 Lambda表达式的省略写法
在Lambda标准格式的基础上,使用省略写法的规则为:
- 小括号内参数的类型可以省略;
- 如果小括号内有且仅有一个参,则小括号可以省略;
- 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
Lambda强调的是“做什么”而不是“怎么做” ,所以凡是可以根据上下文推导得知的信息,都可以省略。
例如上面两个例子还可以使用Lambda的省略写法:
Runnable接口简化:
1. () -> System.out.println(Thread.currentThread().getName()+":执行~~~")
Comparator接口简化:
3. Arrays.sort(array, (s1, s2) -> s1.getAge() - s2.getAge());
下面再补充一个例子,以巩固知识点,你可以根据给出的匿名内部类写法,尝试着自己写一写Lambda表达式的写法,以及各种省略写法。
Consumer 代码示例:
public class ConsumerDemo {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("张三");
names.add("李四");
names.add("王五");
// 传统的匿名内部类写法
names.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
names.forEach((String s) -> {
System.out.println(s);
});
// 省略形参类型
names.forEach((s) -> {
System.out.println(s);
});
// 省略形参括号
names.forEach(s -> {
System.out.println(s);
});
// 省略方法体大括号
names.forEach(s -> System.out.println(s) );
// 方法引用的写法
names.forEach(System.out::println);
}
}
方法引用
1 方法引用概述
方法引用使得开发者可以直接引用现存的方法、Java类的构造方法或者实例对象。
方法引用是为了进一步简化Lambda表达式的写法,通常和Lambda表达式配合使用。
2 方法引用基本使用
基本格式:类型或者对象::引用的方法
下面,我们在 Car 类中定义了 4 个方法作为例子来区分 Java 中 4 种不同方法的引用。
public static class Car {
public static Car create( final Supplier< Car > supplier ) {
return supplier.get();
}
public static void collide( final Car car ) {
System.out.println( "Collided " + car.toString() );
}
public void follow( final Car another ) {
System.out.println( "Following the " + another.toString() );
}
public void repair() {
System.out.println( "Repaired " + this.toString() );
}
}
第一种方法引用的类型是构造器引用,语法是Class::new,或者更一般的形式:Class::new。注意:这个构造器没有参数。
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
第二种方法引用的类型是静态方法引用,语法是Class::static_method。注意:这个方法接受一个Car类型的参数。
cars.forEach( Car::collide );
第三种方法引用的类型是某个类的成员方法的引用,语法是Class::method,注意,这个方法没有定义入参:
cars.forEach( Car::repair );
第四种方法引用的类型是某个实例对象的成员方法的引用,语法是instance::method。注意:这个方法接受一个Car类型的参数:
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
方法引用有四种形式:
- 构造器引用
- 静态方法的引用
- 特定类型方法的引用
- 实例方法的引用
第一种方法引用的类型是构造器引用,语法是Class::new,或者更一般的形式:Class::new。注意:前后参数一致的情况下,又在创建对象就可以使用构造器引用:
List<String> lists = new ArrayList<>();
lists.add("java1");
lists.add("java2");
lists.add("java3");
// 集合默认只能转成Object类型的数组。
Object[] objs = lists.toArray();
// 我们想指定转换成字符串类型的数组!!
// 最新的写法可以结合构造器引用实现 。
// default <T> T[] toArray(IntFunction<T[]> generator)
String[] strs = lists.toArray(new IntFunction<String[]>() {
@Override
public String[] apply(int value) {
return new String[value];
}
});
String[] strs1 = lists.toArray(s -> new String[s] );
String[] strs2 = lists.toArray(String[]::new);
第二种方法引用的类型是静态方法引用,语法是Class::static_method。注意:被引用的方法的参数列表要和函数式接口中的抽象方法的参数列表一致:
Collections.sort(lists, ( o1, o2) -> o1.getAge() - o2.getAge());
// 使用静态方法进行简化
Collections.sort(lists, ( o1, o2) -> Student.compareByAge(o1 , o2));
// 如果前后参数是一样的,而且方法是静态方法,既可以使用静态方法引用
Collections.sort(lists, Student::compareByAge);
第三种方法引用的类型是特定类型(java自己定义的类型如String、Data等)方法的引用,语法是特定类型::方法,注意,如果第一个参数列表中的形参中的第一个参数作为了后面的方法的调用者,并且其余参数作为后面方法的形参,那么就可以用特定类型方法引用了 :
String[] strs = new String[]{"James", "AA", "John",
"Patricia","Dlei" , "Robert","Boom", "Cao" ,"black" ,
"Michael", "Linda","cao","after","sBBB"};
// public static <T> void sort(T[] a, Comparator<? super T> c)
// 需求:按照元素的首字符(忽略大小写)升序排序!!!
Arrays.sort(strs, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareToIgnoreCase(s2);// 按照元素的首字符(忽略大小写)比较。
}
});
Arrays.sort(strs, (String s1, String s2) -> {
return s1.compareToIgnoreCase(s2);// 按照元素的首字符(忽略大小写)比较。
});
Arrays.sort(strs, ( s1, s2 ) -> s1.compareToIgnoreCase(s2));
// 特定类型的方法引用:
Arrays.sort(strs, String::compareToIgnoreCase);
System.out.println(Arrays.toString(strs));
第四种方法引用的类型是实例方法的引用,语法是instance::method。注意:被引用的方法的参数列表要和函数式接口中的抽象方法的参数列表一致:
// 对象是 System.out = new PrintStream();
// 实例方法:println()
// 前后参数正好都是一个
lists.forEach(s -> System.out.println(s));
lists.forEach(System.out::println);