方法引用
它主要是对Lambda表达式的一种优化,在我们使用Lambda表达式的时候,我们实际上传递进去的代码是一种解决方案,比如拿什么参数做什么。
但是有一种情况:
如果我们在Lambda中所使用的一种方案,在其他地方已经存在相同的方案,那么我们还需要再写重复的逻辑了嘛???
举个例子
我们创建一个函数式接口,这个接口专门打印字符串,那么我们使用Lambda时需要的代码量就比较多
代码示例(反例)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @FunctionalInterface public interface Printable { void print(String str); }
public class Demo01PrintSimple { private static void printString(Printable data) { data.print("Hello, World!"); }
public static void main(String[] args) { printString(s ‐> System.out.println(s)); } }
|
使用Lambda中的方法引用优化上例的代码
1 2 3 4 5 6 7 8 9 10 11
| public class Demo02PrintRef { private static void printString(Printable data) { data.print("Hello, World!"); }
public static void main(String[] args) { printString(System.out::println); } }
|
方法引用符:“::”
双冒号::
为引用运算符,它所在的表达式被称为**方法引用
**。如果Lambda要表达的函数方案已经存在于某个方法的实现中,我们就可以通过双冒号来引用该方法作为Lambda的代替者
上面两个案列的语义分析
使用对象的引用名来引用成员方法
1 2 3 4 5
| @FunctionalInterface public interface Printable { void print(String str); }
|
1 2 3 4 5
| public class MethodRefObject { public void printUpperCase(String str) { System.out.println(str.toUpperCase()); } }
|
- 当我们使用lambda,并且在Lambda中具体实现功能时,需要创建MethodRefObject类时,我们可以直接先创建类,然后直接引用它
1 2 3 4 5 6 7 8 9 10
| public class Demo04MethodRef { private static void printString(Printable lambda) { lambda.print("Hello"); } public static void main(String[] args) { MethodRefObject obj = new MethodRefObject(); printString(obj::printUpperCase); } }
|
通过类目引用静态成员
由于在 java.lang.Math 类中已经存在了静态方法 abs ,所以当我们需要通过Lambda来调用该方法时,有两种写
法。
1 2 3 4 5
| @FunctionalInterface public interface Calcable { int calc(int num); }
|
1 2 3 4 5 6 7 8
| public class Demo05Lambda { private static void method(int num, Calcable lambda) { System.out.println(lambda.calc(num)); } public static void main(String[] args) { method(‐10, n ‐> Math.abs(n)); } }
|
1 2 3 4 5 6 7 8
| public class Demo06MethodRef { private static void method(int num, Calcable lambda) { System.out.println(lambda.calc(num)); } public static void main(String[] args) { method(‐10, Math::abs); } }
|
- 在这两个例子中,下面两种写法是相等的
- Lambda表达式:
n -> Math.abs(n)
- 方法引用:
Math::abs
通过super引用成员方法
1 2 3 4 5
| @FunctionalInterface public interface Greetable { void greet(); }
|
1 2 3 4 5
| public class Human { public void sayHello() { System.out.println("Hello!"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Man extends Human { @Override public void sayHello() { System.out.println("大家好,我是Man!"); } public void method(Greetable g){ g.greet(); } public void show(){ method(super::sayHello); } }
|
通过this引用成员方法
类的构造器的引用(构造方法,也就是通过引用创建对象)
以构造器引用使用 类名称::new
的格式表示。
首先有个Person类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Person { private String name;
public Person(String name) { this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; } }
|
1 2 3
| public interface PersonBuilder { Person buildPerson(String name); }
|
1 2 3 4 5 6 7 8 9 10
| public class Demo10ConstructorRef { public static void printName(String name, PersonBuilder builder) { System.out.println(builder.buildPerson(name).getName()); }
public static void main(String[] args) { printName("赵丽颖", Person::new); } }
|
- 下面两种写法是等效的:
- Lambda表达式:
name -> new Person(name)
- 方法引用:
Person::new
数组构造器的引用(创建数组)
数组也是 Object 的子类对象,所以同样具有构造器,只是语法稍有不同
1 2 3 4
| @FunctionalInterface public interface ArrayBuilder { int[] buildArray(int length); }
|
1 2 3 4 5 6 7 8
| public class Demo12ArrayInitRef { private static int[] initArray(int length, ArrayBuilder builder) { return builder.buildArray(length); } public static void main(String[] args) { int[] array = initArray(10, int[]::new); } }
|
- 下面两种写法是等效的:
- Lambda表达式:
length -> new int[length]
- 方法引用:
int[]::new