Java|Lambda、函数式接口使用
目录
Lambda 是 JDK8 的新特性,Lambda 让 Java 支持了函数式的编程,如 JS 那样可以在参数中使用函数(JS 中函数是对象)。
这些特性是 Java8 的一些特性,使用这些特性能够简化我们的代码,让代码更清晰。
Lambda 使用示例
在支持回调的方法中使用,典型的 Runnable 接口的 run(),Comparator 的 compare()。
// 使用Lambda执行一个线程的run方法
new Thread(() -> {
try {
while (true) {
System.out.println("hello elltor!");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// 自定义compare实现
Arrays.sort(strs,(str1,str2)->{
// 根据首字母ascii差值判断大小
char c = str1.charAt(0);
char c2 = str2.charAt(0);
return c-c2;
});
for (String str : strs) {
System.out.println(str);
}
// 自定义compare实现2
Comparator<String> comparator = (str1,str2)->{
char c = str1.charAt(0);
char c2 = str2.charAt(0);
return c-c2;
};
// 使用
Arrays.sort(strs,comparator);
Lambda 语法与注意事项
首先,如果要使用 lambda,就要实现函数式接口,如 Runnable 的 run 方法,稍后讲解函数式接口。
Lambda 语法。
Lambda 的标志是 “->”, 前面接收参数的括号和后面的方法题在特殊的情况下都可以省略。
// 基本语法
() ->{}
// 省略括号,当且仅当只有一个参数时才允许这么做
ActionListener listener = e -> {
System.out.println(this.aa);
System.out.println("监听得到事件");
};
// 省略方法体,当且仅当方法体只有只有一个return+返回值时才允许这么做
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list.toString());
list.removeIf(val -> val <= 2); // 即省略了括号也省略了方法体
System.out.println(list.toString());
使用 Lambda 的注意事项:
- Lambda 方法体中不具有 this,它包含的 this 是包含 Lambda 的方法的 this
- Lambda 方法体中引用的变量不能改变,不能在方法体外边改变或者里面改变,这是 Lambda 的语法规定。从语言层面来讲,如果可以改变那将带来线程安全性问题。
- Lambda 函数回调有固定的类型,这些类型根据函数式接口确定,Runnable 的 run 接收的是一个无参的 Lambda,而 Predicate 接收的是一个参数 Lambda
函数式接口
实现 Lambda 依赖的是函数式接口,函数式接口规定了传入什么样格式的 Lambda(有无参数或返回值,参数个数等)。函数式接口(Function Interface)是 java.util.function 包下的一组抽象接口。
接口支持 Lambda 的原理——一方法参数使用了函数接口,二方法调用函数式接口的方法。
先看 ArrayList 的 removeIf 的源码。
// ArrayList的removeIf的源码
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
// Predicate 核心源码
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*/
boolean test(T t);
}
方法引用
方法引用的语法:
- object::instanceMethod
- Class::staticMethod
- Class::instanceMethod
示例
List<Integer> list = Arrays.asList(1,2,3,4,5);
list.forEach(System.out::println);
String[] strings = new String[]{"c","b","a"};
Arrays.sort(strings,String::compareToIgnoreCase);
System.out.println(Arrays.toString(strings));
方法的引用是传递一个方法的引用过去,它等价于 Lambda 的 x->System.out.println(x).
当被传入的方法有重载时,编译器会根据上下文推断应该调用的方法是什么类型,如 Math::max 方法有两个,传入 double 和传入 int 的,选择哪一个取决于 Math::max 函数式接口的具体参数, 关键在多态运行时的匹配。
public class RefTest {
public static void main(String[] args) {
RefTest.printMax(2,3,Math::max);
}
static void printMax(int a, int b, BiFunction<Integer,Integer, Integer> accept){
// apply将调用Math.max()方法,并返回比较结果
System.out.println("max = " + accept.apply(a, b));
}
}
// output
max = 3
构造器引用
构造器引用示例
List<String> names = Arrays.asList("zhangsan","lisi","wanger");
names.stream().map(Person::new).collect(Collectors.toList());
常用函数式接口
接口 | 参数 | 返回类型 | 抽象方法 | 描述 | 其他方法 |
---|---|---|---|---|---|
Runable | 无 | void | run | 作为无参或返回值的动作执行 | 无 |
Consumer |
|||||
BiComsumer<T,U> | |||||
Function<T,R> | |||||
BiFunction<T,U,R> | |||||
UnaryOperator<T> | |||||
BinaryOperator<T> | |||||
Predicate<T> | |||||
BiPredicate<T,U> |