方法引用
有时,可能已经有现成的方法可以完成我们想要传递到其他代码的某个动作,就可以使用方法引用。
package pers.zhang.syntax;
import pers.zhang.interfaces.LambdaSingleReturnSingleParameter;
/**
* @author zhang
* @date 2020/1/10 - 21:36
*/
public class Syntax3 {
public static void main(String[] args) {
/*
方法引用:可以快速的将一个Lambda表达式的实现指向一个已经实现的方法。
语法: 方法的隶属者::方法名 (静态方法隶属于类,普通方法隶属于对象)
注意:
1.参数数量和类型一定要和接口中定义的方法一致。
2.返回值类型一定要和接口中定义的方法一致。
*/
LambdaSingleReturnSingleParameter lambda1 = a -> change(a);
//方法引用:引用了change方法的实现
LambdaSingleReturnSingleParameter lambda2 = Syntax3::change;
}
private static int change(int a){
return a * 2;
}
}
从上面的例子可以看出,要用 :: 操作符 分隔方法名于对象或类名。
主要有3种情况:
- object::instanceMethod
- Class::staticMethod
- Class::instanceMethod
前2种情况中,方法引用等价于提供方法参数的lambda表达式。(即System.out::pritln等价于 x -> System.out.println(x)。)
对于第三种情况,第一个参数会成为方法的目标。例如String::compareToIgnoreCasse等价于 (x, y) -> x.compareToIgnoreCase(y)。
注意:如果有多个同名的重载方法,编译器会尝试从上下文中找出你指定的那一个方法。例如,Math.max方法有两个重载,一个用于int,一个用于double。选择哪一个取决于Math::max转换为哪个函数式接口的方法参数。类似于lambda表达式,方法引用不能独立存在,总是会转换为函数式接口的实例。
可以在方法引用中使用this参数。例如,this::equals 等同于 x -> this.equals(x)。使用super也是合法的。
例如:
super::instanceMethod
使用this作为目标,会调用给定方法的超类版本。
例子
class Greeter
{
public void greet()
{
System.out.println("hello world!");
}
}
class TimedGreeter extends Greeter
{
public void greet()
{
Timer t = new Timer(1000, super::greet);
}
}
TimedGreeter.greet方法开始执行时,会构造一个Timer,它会在每次定时器滴答时执行super::greet方法。这个方法会调用超类的greet方法。
构造器引用
构造器引用与方法引用类似,只不过方法名为new。例如:Person::new是Person构造器的一个引用。
可以使用数组类型建立构造器引用。例如,int[]::new 是一个构造器引用,他有一个参数即数组的长度。这等价于lambda表达式 x -> new int[x]。
package pers.zhang.syntax;
import pers.zhang.data.Person;
/**
* @author zhang
* @date 2020/1/10 - 21:55
*/
public class Syntax4 {
public static void main(String[] args) {
PersonCreater creater = () -> new Person();
/*
构造方法的引用:
*/
PersonCreater creater1 = Person::new;
Person a = creater1.getPerson();
System.out.println(a);
PersonCreater2 creater2 = Person::new;
Person b = creater2.getPerson("Tom", 18);
System.out.println(b);
}
}
//需求
interface PersonCreater{
Person getPerson();
}
interface PersonCreater2{
Person getPerson(String name, int age);
}
closure
从功能性上说lambda和closure(或是OC中的blocks)是一个东西,只是不同语言的不同称呼罢了,它们都是匿名函数。若匿名函数捕获了一个外部变量,那么它就是一个closure。
注意事项:
- lambda表达式中捕获的变量必须实际上是最终变量(effectively final)。
例如下面的做法是不合法的:线程不安全
public static void countDown(int start, int delay)
{
ActionListener listener = event ->
{
start--;//错误!!!
System.out.println(start);
};
new Timer(delay, listener).start();
}
- lambda表达式中也不能有同名的局部变量
在Lambda表达式中声明一个局部变量同名的参数或局部变量是不合法的:因为Lambda的体与嵌套块有相同的作用域。
Path first = Paths.get("/usr/bin");
Comparator<String> comp =
(first, second) -> first.length() - second.length();//错误!!first变量重复定义
- 在一个lambda表达式中使用this关键字时,是指向创建这个lambda表达式的方法的this参数。
示例:
public class Application(){
public void init(){
ActionListener listener = event ->
{
System.out.println(this.toString());
}
}
}
表达式this.toString()会调用Application对象的toString()方法,而不是ActionListener实例的方法。在Lambda表达式中,thsi的使用并没有任何特殊之处。lambda表达式的作用域嵌套在init方法中,与出现在这个方法中的其他位置一样,lambda表达式中this的含义并没有变化。