表达式组成:
(parameters)->expression
(parameters)->{statements;},例如:
示例1:(String s)->s.length()
具有一个String类型的参数并返回一个int类型。Lambda没有return语句,因为已经隐含了return。
示例2:(Apple a)->a.getWeight()>150
有一个Apple类型的参数并返回一个boolean类型。
示例3:(int x,int y)->{
System.out.println(“result:”+i);
System.out.println(x+y);
}具有两个int类型的参数而没有返回值,Lambda表达式可以包含多行语句。
示例4:()->42
表达式没有参数,返回一个int类型。
示例5:(Apple a1,Apple a2)->
a1.getWeight().compareTo(a2.getWeight())
表达式具有两个Apple类型的参数,并返回一个int类型。
Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。(函数式接口指的是仅仅有一个方法的interface接口,这里表达的意思是Lambda可以作为接口的匿名内部类)
函数式接口的抽象方法的签名基本上就是Lambda表达式的签名,我们将这种抽象方法叫做函数描述符。(意思就是interface中方法的参数类型、参数数量、返回值要跟Lambda表达式中的完全一致,想想也是,毕竟是要作为接口默认实现的,没办法)
Runnable r1 = ()->System.out.println
(“Hello World!”);
prcess(r1);
process(()->System.out.println(“Hello World!”));
由于Runnable是个接口且该接口只有一个方法,所以可以用Lambda来创建实例。
工具类
java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,该方法接受泛型T对象并返回一个boolean类型,该接口适用于涉及类型T的布尔表达式。(就是参数需要boolean值时可以用该接口,下面类似)
java.util.function.Consumer<T>定义一个accept抽象方法,接受泛型T的对象且没有返回void。
java.util.function.Function<T,R>定义一个叫做apply的方法,接受一个泛型T的对象并返回一个泛型R的对象。
Lambda引用外部变量
int port = 1337;
Runnable r = ()->System.out.println(port);
注意:局部变量必须是显式声明为final或事实上的final。
为什么会有这个限制?假如port变量在线程A中,Lambda则可能在线程B上,此时线程B去访问线程A的时候port可能已经不存在了,为了避免这种情况发生,线程B存有port变量的一份拷贝副本,既然是副本,那就存在着副本与原件的问题,采用final变量则能保证副本与原件的一致性。
方法引用
方法引用可以被看作仅仅调用特定方法的Lambda的一种快捷写法。基本思想就是如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。
使用方法引用时,目标引用放在分隔符::前面,方法名称放在::后面,例如:
(App a)->a.getW()等价于App::getW
(str,i)->str.substring(i)等价于String::substring
(String s)->System.out.println(s)等价于System.out::println
静态方法的引用
Lambda:
(args)->ClassName.staticMethod(args)
方法引用:
ClassName::staticMethod
实例方法的引用
①Lambda:
(arg,rest)->arg.instanceMethod(rest)
方法引用:arg是ClassName类型
ClassName::instanceMethod
②Lambda:(arg)->expr.instanceMethod(arg)
方法引用:
expr::instanceMethod
③注意:第一个与第二个区别在于第一个是调用参数的实例方法,第二个是调用外部变量的实例方法。
构造方法引用
用类名称和关键字new来创建一个应用:ClassName::new。
调用无参的构造方法
Supplier<App> c1=App::new;
App a1=c1.get();
通过::new来指向App的默认构造方法,然后通过Supplier的get方法来产生一个新的App。
调用一个参数的构造方法
java.util.function.Function<Integer,App> c1=App:new;
App a1=c1.apply(1);
由于java.util.function.Function<T,R>定义一个叫做apply的方法,接受一个泛型T的对象并返回一个泛型R的对象,也就是通过Function的apply来间接生成App对象。
调用两个参数的构造方法
java.util.function.BiFunction<String,Integer,App> c1=App:new;
App a1=c1.apply(“test”,10);
总结
静态方法调用为ClassName::staticMethod
构造方法调用为ClassName::new
假如某个ClassName的构造方法有N个参数的话,那就需要自己去创建一个与ClassName的构造方法的签名匹配的函数式接口,例如:App的构造方法是5个参数,可以这样:
public interface TFunction<T,U,V,App>{
App apply(T t,U u,V v);
}
(parameters)->expression中只要parameters数量、类型以及expression返回值与XX方法签名匹配就行,不在乎方法是哪个ClassName的。