Java8引入了Lambda表达式特性,这些是通过java.util.function
这个包实现的。所有的Lambda表达式都是这个包下的其中一类。
我们来看下这个包java.util.function
:
可以看到很多有@FunctionalInterFace
的接口,@FunctionalInterFace
代表这个接口只有一个抽象方法。从命名,我们可以猜测出一些接口的作用。
比如,Predicate就是只包含输入是一个对象,输出是true或者false布尔值的方法的这么一个接口。
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
Consumer就是只包含输入一个对象,返回void的方法的这么一个接口。
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
类似的BiPredicate就是输入是两个对象,输出是true或者false布尔值的方法的这么一个接口;IntToDoubleFunction就是包含一个输入是int类型的输出为double类型的方法的接口
我们可以尝试写一个简单的lambda表达式:
public class LambdaTest {
public static int transfer(double a, Function<Double, Integer> fn) {
return fn.apply(a);
}
public static void main(String[] args) {
System.out.println(transfer(9.9, a -> BigDecimal.valueOf(a).intValue()));
}
}
同时,tansfer函数还可以这么定义:
public class LambdaTest {
public static int transfer(double a, DoubleToIntFunction fn) {
return fn.applyAsInt(a);
}
public static void main(String[] args) {
System.out.println(transfer(9.9, a -> BigDecimal.valueOf(a).intValue()));
}
}
但是,两种定义出现在一起,同样调用的时候,就会编译错误。因为:
Ambiguous method call. Both
transfer (double,DoubleToIntFunction)in LambdaTest
and transfer (double, Function<Double, Integer>) in LambdaTest match
由此可见,只有一个方法的interface,其实都可以用Lambda表达式代替。
使用Lambda表达式的一些tips
对于只有一个方法的interface,使用@FunctionalInterface注解
这样可以限制这个接口只会有一个抽象方法,防止在大型项目中合作,修改接口导致lambda表达式全部失效。加上这个注解,只要对于这个接口增加新的方法导致抽象方法不止一个,就会编译错误
不要重载以FunctionalInterface的方法
例如之前举的例子:
public class LambdaTest {
public static int transfer(double a, DoubleToIntFunction fn) {
return fn.applyAsInt(a);
}
public static int transfer(double a, Function<Double, Integer> fn) {
return fn.apply(a);
}
public static void main(String[] args) {
System.out.println(transfer(9.9, a -> BigDecimal.valueOf(a).intValue()));
}
}
解决办法一是换个名字,另一个是调用时,加上强制类型转换,但不推荐这么做
不要将lambda表达式作为内部类:
public class Test implements SimpleJob {
private String value = "Origin Value";
interface Foo {
String method(String string);
}
public String test() {
Foo fooIC = new Foo() {
String value = "Inner class value";
@Override
public String method(String string) {
return this.value;
}
};
String resultIC = fooIC.method("");
Foo fooLambda = parameter -> {
String value = "Lambda value";
return this.value;
};
String resultLambda = fooLambda.method("");
return "Results: resultIC = " + resultIC +
", resultLambda = " + resultLambda;
}
public static void main(String[] args) {
System.out.println(new Test().test());
}
}
lambda表达式实际上是直接填写方法里面的内容,所以无法像内部类那样可以添加field。
这里输出是:
Results: resultIC = Inner class value, resultLambda = Origin Value
保持Lambda表达式简洁明了
避免大块代码,可以抽象为方法
例如:
Foo foo = parameter -> { String result = "Something " + parameter;
//many lines of code
return result;
};
就最好写成:
Foo foo = parameter -> buildString(parameter);
private String buildString(String parameter) {
String result = "Something " + parameter;
//many lines of code
return result;
}
避免指定参数类型
编译器可以通过类型指针识别出参数的类型,所以不用强制指定参数类型,例如:
(String a, String b) -> a.toLowerCase() + b.toLowerCase();
可以写成:
(a, b) -> a.toLowerCase() + b.toLowerCase();
单个参数不用加括号
(a) -> a.toLowerCase();
可以写成:
a -> a.toLowerCase();
避免return和大括号
a -> {return a.toLowerCase()};
可以写成:
a -> a.toLowerCase();
使用方法指针
对于仅仅是调用类方法的lambda表达式,例如
a -> a.toLowerCase();
可以替换成:
String::toLowerCase;
应该使用“Effectively Final”的变量
lambda表达式用的外部变量,不用是final的,但应该都是Effectively Final的,就是在lambda表达式里面不会修改这个变量