文章目录
1 Lambda表达式
1.1 Lambda表达式
可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。可以把一个Lambda表达式看成一个接口的实现类(更准确的说是一个函数式接口的实现类,函数式接口是一个只拥有一个抽象方法的接口)。因为一个函数式接口的实现类用很多冗余代码,使用Lambda表达式可以简化代码,而只保留核心的代码。
1.2 组成部分
- 形参列表:允许省略形参类型,如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略。
- 箭头(->):必须通过英文中画线和大于符号组成。
- 代码块:
- 如果代码块只包含一条语句,可以省略代码块的花括号;
- 如果只有一条return语句,可以省略return关键字;
- 如果表达式需要返回值,而它的代码块中仅有一条省略了return的语句,Lambda表达式会自动返回这条语句的值。
2 函数式接口
2.1 函数式接口
函数式接口指的是有且仅有一个抽象方法的接口。
因为在JDK 8中新增了Lambda表达式,所使用的接口必须只能有一个抽象方法。所以,函数式接口就是专为Lambda而产生的特殊接口。要定义一个函数式接口,那么该接口只能有一个抽象方法。为了确保只有一个抽象方法,同时也可以告诉别人这是一个抽象方法,我们通常在接口上添加一个注解:@FunctionalInterface。
2.2 JDK内置函数式接口
- Supplier接口:Supplier接口是java.util.function包下的一个函数式接口,该接口的抽象方法是一个无参的方法:T get(),用来获取一个泛型参数指定类型的对象数据。
- Consumer接口:Consumer接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。它提供了一个抽象方法:void accept(T t),作用是使用一个指定泛型的数据。
- Predicate接口:Predicate接口用来对某种类型的数据进行判断,该接口包含一个抽象方法:boolean test(T t)。此外,Predicate接口还提供了三个默认方法,and()、or()、negate()
- Function接口:Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。Function接口提供的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。
2.2.1 Supplier接口
public class TestSupplier1 {
public static void main(String[] args) {
Supplier<String> supplier = getUuidProvider(8);
System.out.println(supplier.get());
System.out.println(supplier.get());
System.out.println(supplier.get());
}
public static Supplier<String> getUuidProvider(int length) {
return new Supplier<String>() {
@Override
public String get() {
String uuid = UUID.randomUUID().toString();
return uuid.substring(0, length);
}
};
}
}
2.2.2 Consumer接口
public class TestConsumer {
public static void main(String[] args) {
Consumer<List<Integer>> calculator = getMax();
calculator.accept(Arrays.asList(1, 2, 3));
}
public static Consumer<List<Integer>> getMax() {
return new Consumer<List<Integer>>() {
@Override
public void accept(List<Integer> integers) {
System.out.println(Collections.max(integers));
}
};
}
}
2.2.3 Predicate接口
public class TestPredicate {
public static void main(String[] args) {
Predicate<String> predicate = getLongNamePredicate();
List<String> names = Arrays.asList("1", "22", "333");
names.stream().filter(predicate)
.forEach(System.out::println);
}
public static Predicate<String> getLongNamePredicate() {
return new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length() > 2;
}
};
}
}
2.2.4 Function接口
public class TestFunction {
public static void main(String[] args) {
Function<Integer, String> transformer = getTransformer(1);
String target = transformer.apply(1234);
System.out.println(target);
}
public static <T> Function<T, String> getTransformer(T source) {
return new Function<T, String>() {
@Override
public String apply(T t) {
if (source instanceof Integer) {
return String.valueOf(t);
}
return null;
}
};
}
}
3 方法引用
当要传递给Lambda表达式的方法体已经有实现了,便可以使用方法引用。方法引用可以看做是Lambda表达式的更深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
- 静态方法的方法引用
- 对象的实例方法的方法引用
- 类的实例方法的方法引用
- 无参构造器引用
- 有参构造器引用
3.1 静态方法的方法引用
@Test
public void test(){
Comparator<String> comparator = String::compareTo;
int result = comparator.compare("abc", "cba");
System.out.println("result = " + result);
}
3.2 实例方法的方法引用
@Test
public void test(){
PrintStream ps = System.out;
Consumer<String> consumer = ps::println;
consumer.accept("使用方法引用");
}
3.3 类的实例方法的方法引用
@Test
public void test(){
Student student = new Student();
student.setName("Tom");
Function<Student, String> function = Student::getName;
String name = function.apply(student);
System.out.println("name = " + name);
}
3.4 无参构造器引用
@Test
public void test4() {
// 无参构造器的方法引用
Supplier<Student> supplier = Student::new;
Student student = supplier.get();
student.setName("小红");
System.out.println("student = " + student);
// 有参构造器的方法引用
BiFunction<Integer, String, Student> function = Student::new;
Student student1 = function.apply(24, "小红");
System.out.println("student = " + student1);
}