在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿什么参数做什么操作。那么考虑
一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案
2.1 冗余的Lambda场景
@FunctionalInterface
interface Printable {
void print(String s);
}
public class Repent_Lambda {
private static void printString(Printable data) {
data.print("Hello, World!");
}
public static void main(String[] args) {
printString((str)->System.out.println(str));
printString(System.out::println);// 使用方法引用直接调用
}
}
其中 printString
方法只管调用 Printable
接口的 print 方法,而并不管 print 方法的具体实现逻辑会将字符串
打印到什么地方去。而 main 方法通过Lambda表达式指定了函数式接口 Printable
的具体操作方案为:拿到String(类型可推导,所以可省略)数据后,在控制台中输出它
2.2 方法引用符
双冒号 ::
为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。
语义分析
例如上例中, System.out
对象中有一个重载的 println(String)
方法恰好就是我们所需要的。那么对于printString
方法的函数式接口参数,对比下面两种写法,完全等效:
- Lambda表达式写法:
s -> System.out.println(s);
- 方法引用写法:
System.out::println
第一种语义是指:拿到参数之后经Lambda之手,继而传递给 System.out.println 方法去处理。
第二种等效写法的语义是指:直接让 System.out 中的 println 方法来取代Lambda, 根据已有参数进行推导 损略了传递参数 以及多余的代码()->。两种写法的执行效果完全一样,而第二种方法引用的写法复用了已有方案,更加简洁。
注:Lambda 中 传递的参数 一定是方法引用中 的那个方法可以接收的类型,否则会抛出异常
推导与省略
如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式——它们都将被自动推导。而如果使用方法引用,也是同样可以根据上下文进行推导。
@FunctionalInterface
interface Printable {
//void print(String s);
void print(int s);
}
public class Repent_Lambda {
private static void printString(Printable data) {
data.print(2);
}
public static void main(String[] args) {
printString((int str)->System.out.println(str)); // 参数类型 可以推断
printString(System.out::println); // 方法引用 连 方法重载也推导了
}
}
总结:
- Lambda 可以推导参数类型 直接处理业务逻辑代码
::
方法引用 可以推导函数重载
2.3 通过对象名引用成员方法
如果一个类中已经存在了一个成员方法:可以吧这个方法实现通过引用直接传递过去
解释:
public class Obj_Lambda {
private static void printString(Printable lambda) {
lambda.print("Hello");
}
public static void main(String[] args) {
MethodRefObject obj = new MethodRefObject();
printString((str)->System.out.println(str));
printString(obj::printUpperCase);// 具体代码
/**
printString 需要一个Printable函数接口,这里传统方法就是将自己实现的Lambda传递过去,
如果引用 MethodRefObject 类的对象实例,则可以通过对象名引用成员方法 将实现传递过去,
*/
}
}
class MethodRefObject {
public void printUpperCase(String str) {
System.out.println(str.toUpperCase());
}
}
2.4 通过类名称引用静态方法
调用: java.lang.Math
类中已经存在了静态方法 abs
public class Static_Lambda {
private static void method(int num, Calcable lambda) {
System.out.println(lambda.calc(num));
}
public static void main(String[] args) {
method(-10, num->Math.abs(num));
method(-10, Math::abs); // 静态调用方式
}
}
@FunctionalInterface
interface Calcable {
int calc(int num);
}
下面两种方法等效
- Lambda表达式:
() -> Math.abs(n)
- 方法引用:
Math : : abs
2.5 通过super引用成员方法
@FunctionalInterface
interface Greetable {
void useToolsEat();
}
class Animal {
void eat() {
System.out.println("Animal eat----");
}
}
class FemalAnimal extends Animal {
protected int age;
public FemalAnimal(int age) {
this.age = age;
}
void eat() {
System.out.println(" FemalAnimal eat----");
}
}
class Cat extends FemalAnimal {
private String name;
public Cat(int age, String name) {
super(age);
this.name = name;
}
@Override
void eat() {
System.out.println(super.age + " " + this.name + " " + "吃饭");
}
public static void method(Greetable g) {
g.useToolsEat();
}
void show(){
method(super::eat); // 调用直接父类的方法
method(()->new FemalAnimal(0).eat()); //
}
}
下面两种方法等效
- Lambda表达式:
() -> super.buyHouse()
- 方法引用:
super : : buyHouse
2.6 通过this引用成员方法
public class This_Lambda {
@FunctionalInterface
interface Richable{
void buy();
}
private void buyHouse() {
System.out.println("买套房子");
}
private static void marry(Richable lambda) {
lambda.buy();
}
public void beHappy() {
marry(()->buyHouse());
marry(this::buyHouse); // 直接引用本类存在的方法
}
}
下面两种方法等效
- Lambda表达式:
() -> this.buyHouse()
- 方法引用:
this : : buyHouse
2.7 类的构造器引用
public class Construtor_Lambda {
@FunctionalInterface
interface PersonBuilder{
P buildPerson(String name);
}
public static void printName(String name, PersonBuilder builder) {
System.out.println(builder.buildPerson(name).getName());
}
public static void main(String[] args) {
printName("Tom", (name)->new P(name)); // 传递的参数name 将会被自动推导
printName("Tom", P::new);
}
}
下面两种方法等效
- Lambda表达式:
name -> new Person(name)
- 方法引用:
Person : : new
2.8 数组的构造器引用
数组 List Set Map 等其他类似的数据结构都可以进行推导
public class Array_Lambda {
@FunctionalInterface
interface GenerateIntArr{
int[] BuilderArr(int length);
}
@FunctionalInterface
interface GenerateIntList{
ArrayList<Integer> BuilderArr(int length);
}
private static void method(int length,GenerateIntArr gArr) {
System.out.println(Arrays.toString(gArr.BuilderArr(length)));
}
private static void method1(int length,GenerateIntList gArr) {
System.out.println(gArr.BuilderArr(length));
}
public static void main(String[] args) {
method(10, (num)->new int[num]);
method(10,int[]::new);
method1(10, ArrayList<Integer>::new);
}
}
下面两种方法等效
- Lambda表达式:
length -> new int[length]
- 方法引用:
int[] : : new