Java 8方法引用
Java 8引入了许多新功能,如Lambda表达式,流,方法引用等。
在本教程中,我们将看到什么是方法引用以及我们如何使用它。
我会尽力提供更多的例子而不是理论。
方法引用介绍
Method references
是仅执行一种方法的特殊类型的Lambda表达式。
方法引用的一般语法:
object::methodName
我们可能已经猜到了我们需要先理解Lambda表达式。
如果我们对Lambda表达式感到满意,那么让我们前进。
Lambda expression in java 8 Functional interface in java 8
让我们在举例的帮助下了解这一点。
让我们创建程序 MethodReferecesLambdaExpressionMain
其中我们将使用Stream的Foreach方法打印列表。
package org.igi.theitroad; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; public class MethodReferecesLambdaExpressionMain { public static void main(String args[]) { List<String> countryList=Arrays.asList(new String[] {"Netherlands", "China","Nepal","Russia"}); System.out.println("======================="); System.out.println("Using anonymous class"); System.out.println("======================="); //Using anonymous class countryList.stream().forEach( new Consumer<String>() { @Override public void accept(String country) { System.out.println(country); } }); System.out.println("======================="); System.out.println("Using lambda expression"); System.out.println("======================="); //Using lambda expression countryList.stream().forEach( country -> System.out.println(country) ); System.out.println("======================="); System.out.println("Using Method references"); System.out.println("======================="); //Using method reference countryList.stream().forEach( System.out::println ); } }
stream.foreach()
方法将消费者功能接口作为agrument。
消费者是功能接口,它采用单个参数,并没有返回。
我们使用3种方式使用了消费者功能接口。
- 使用匿名程序
Consumer<String> consumer1 = new Consumer<String>() { @Override public void accept(String country) { System.out.println(country); } };
- 使用lambda表达式
Consumer<String> consumer2 = country -> System.out.println(country);
- 使用方法引用
Consumer<String> consumer3 = System.out::println;
You might already know that you can use lambda expression instead of an anonymous class, but You can use method reference only when the lambda expression just calls to a method.
所以,如果你看下面的语法:
Consumer<String> consumer3 = System.out::println;
在方法引用中,我们之前有类或者对象 ::
和方法名称后 ::
没有论据。
我们是否注意到方法引用没有参数?
是的,我们不需要将参数传递给方法引用,基于方法引用的类型,参数在内部自动传递参数。
以下图表将更清晰。
我们可以使用方法引用:
让我们说你想转换 country
到 uppercase
在打印之前。
你可以使用它 anonymous class
和 lambda expression
但不是方法引用。
我们不能使用方法引用,如下所示:
我们可以显然使用Stream的Map()方法来转换
country
到 uppercase
在打印之前。
我只是想在方法引用无法使用时演示。
方法类型引用
有四种类型的方法引用。
- 引用静态方法
- 对象类型的实例方法引用
- 引用现有对象的实例方法
- 引用构造函数
引用静态方法
当我们有Lambda表达式呼叫静态方法时,那么我们可以对静态方法的方法引用。
lambda表达语法 (args) -> ClassName.someStaticMethod(args)
可以转换为 ClassName::someStaticMethod
让我们在举例的帮助下看到这个。
创建类名称 PowerFunctions
package org.igi.theitroad; import java.util.ArrayList; import java.util.List; import java.util.function.Function; class PowerFunctions { //This is the method we will call in method reference public static Integer power(int a) { return a*a; } //Function is functional interface which will be target for method reference public static List<Integer> calculatePowOf2ForList(List<Integer> list, Function<Integer,Integer> function) { List<Integer> powerNumbers = new ArrayList<>(); for(Integer num:list) { Integer powOf2 = function.apply(num); powerNumbers.add(powOf2); } return powerNumbers; } }
功能是采用单个输入T的功能接口,并返回单个输出R.
我们可以调用 calculatePowOf2ForList()
如下:
package org.igi.theitroad; import java.util.Arrays; import java.util.List; import java.util.function.Function; public class StaticMethodReferenceMain { public static void main(String args[]) { List<Integer> list=Arrays.asList(new Integer[] {1,2,3,4,5}); //using anonymous class Function<Integer,Integer> function1=new Function<Integer, Integer>() { @Override public Integer apply(Integer num) { return PowerFunctions.power(num); } }; List<Integer> calculatePowForList1 = PowerFunctions.calculatePowOf2ForList(list, function1); System.out.println(calculatePowForList1); //Using lambda expression Function<Integer,Integer> function2 = (num) -> PowerFunctions.power(num); List<Integer> calculatePowForList2 = PowerFunctions.calculatePowOf2ForList(list, function2); System.out.println(calculatePowForList2); //Using Method reference Function<Integer,Integer> function3 = PowerFunctions::power; List<Integer> calculatePowForList3 = PowerFunctions.calculatePowOf2ForList(list, function3); System.out.println(calculatePowForList3); } }
你是否注意到, Function<Integer,Integer> function2 = (num) -> PowerFunctions.power(num);
是类型 (args) -> className.someStaticMethod(args)
其中
PowerFunctions
是类名- someStaticMethod是
power
的方法 num
是power方法的参数。
我们呼叫静态方法 power
课堂 PowerFunctions
在Lambda表达式中,这就是为什么我们可以将其用作方法引用。
而不是 Function<Integer,Integer> function2 = (num) -> PowerFunctions.power(num);
我们可以用 Function<Integer,Integer> function3 = PowerFunctions::power;
其中
- 第一个类型参数
Function
(整数)是静态方法的第一个参数power()
。 - 第二类参数
Function
(整数)是返回类型的静态方法power()
。
对象类型的实例方法引用
当我们有Lambda表达式时,将对象的实例传递并呼叫具有/不参数的实例方法,然后可以使用方法引用对对象类型的实例方法。
lambda表达语法 (obj,args) -> obj.someInstanceMethod(args)
可以转换为 objectType::someInstanceMethod
让我们在举例的帮助下看到这个。
package org.igi.theitroad; import java.util.function.BiFunction; public class MethodReferenceObjectType { public static void main(String[] args) { //Using anonymous class BiFunction<String,Integer,String> bf1=new BiFunction<>() { @Override public String apply(String t, Integer u) { return t.substring(u); } }; String subString1 = getSubstring("theitroad",2,bf1); System.out.println(subString1); //Using lambda expression BiFunction<String,Integer,String> bf2 = (t,u) -> t.substring(u); String subString2 = getSubstring("theitroad",2,bf2); System.out.println(subString2); //Using Method reference BiFunction<String,Integer,String> bf3 = String::substring; String subString3 = getSubstring("theitroad",2,bf3); System.out.println(subString3); } public static String getSubstring(String str1,int beginIndex,BiFunction<String,Integer,String> p) { return p.apply(str1, beginIndex); } }
BiFunction
是功能接口,采用两个参数并返回单个输出。
如果你注意到, BiFunction<String,Integer,String> bf2 = (t,u) -> t.substring(u);
是类型 (obj,args) -> obj.someInstanceMethod(args)
这里
obj
是字符串类型。- 某种子义务是字符串的
substring()
方法。 - args是
beginIndex
为了substring()
方法参数。
所以 BiFunction<String,Integer,String> bf2 = (t,u) -> t.substring(u);
可以转换为 BiFunction<String,Integer,String> bf3 = String::substring;
其中
- 第一的
BiFunction
参数类型(String)是String对象本身。 - 第二
BiFunction
参数类型(整数)是参数substring()
方法 - 第三
BiFunction
参数类型(String)是返回类型substring()
方法
引用现有对象的实例方法
当我们有Lambda表达式时,使用Object的实例用/不具有参数调用实例方法时,我们可以使用方法引用对具有现有对象的实例方法。
lambda表达语法 (args) -> obj.someInstanceMethod(args)
可以转换为 objectType::someInstanceMethod
这里 obj
在其他地方定义,并不是lambda表达的参数的一部分。
让我们在举例的帮助下了解。
创建一个名为的类 Country.java
。
package org.igi.theitroad; public class Country { String name; long population; Country(String name) { this.name=name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getPopulation() { return population; } public void setPopulation(long population) { this.population = population; } @Override public String toString() { return "[ name = "+name+" population = "+population+" ]"; } }
创造另一个程序 MethodReferenceExistingObjectMain.java
package org.igi.theitroad; import java.util.function.Consumer; public class MethodReferenceExistingObjectMain { public static void main(String[] args) { Country c=new Country("Netherlands"); //Using anonymous class Consumer<Long> popCons1=new Consumer<Long>() { @Override public void accept(Long t) { c.setPopulation(t); } }; popCons1.accept(20000L); System.out.println(c); //Using Lambda expression Consumer<Long> popCons2= (population) -> c.setPopulation(population); popCons2.accept(30000L); System.out.println(c); //Using method reference Consumer<Long> popCons3 = c::setPopulation; popCons3.accept(40000L); System.out.println(c); } }
输出:
[ name = Netherlands population = 20000 ] [ name = Netherlands population = 30000 ] [ name = Netherlands population = 40000 ]
消费者是功能接口,它采用单个参数并没有返回。
如果你注意到, Consumer<Long> popCons2 = (population) -> c.setPopulation(population);
是类型 (args) -> obj.someInstanceMethod(args)
这里
- obj是国家类型,并在其他地方声明。
- 某种主义的是国家的setPopulation方法。
- args是
population
用于setPopulation方法参数。
所以 Consumer<Long> popCons2= (population) -> c.setPopulation(population);
可以转换为 Consumer<Long> popCons3 = c::setPopulation;
其中
- 第一个消费者参数类型(long)是setPopulation方法的参数。
引用构造函数
当Lambda表达式用于创建新对象/不带参数时,可以使用引用方法构造函数。
lambda表达语法 (args) -> new ClassName(args)
可以转换为 ClassName::new
让我们在举例的帮助下看看。
其中我们将转换列表以使用方法引用设置。 Function<List<String>,Set<String>>
是功能接口,它将列出一个参数,并通过调用hashset构造函数来返回设置
public HashSet(Collection<? extends E> c)
package org.igi.theitroad; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Function; public class MethodReferenceConstructorMain { public static void main(String[] args) { ArrayList<String> list=new ArrayList<>(); list.add("Rohan"); list.add("Andy"); list.add("Sneha"); list.add("Rohan"); //Anonymous class Function<List<String>,Set<String>> f1= new Function<List<String>, Set<String>>() { @Override public Set<String> apply(List<String> nameList) { return new HashSet<>(nameList); } }; Set<String> set1 = f1.apply(list); System.out.println(set1); //Using lambda expression Function<List<String>,Set<String>> f2 = (nameList) -> new HashSet<>(nameList); Set<String> set2 = f2.apply(list); System.out.println(set2); //Using Method reference Function<List<String>,Set<String>> f3= HashSet::new; Set<String> set = f3.apply(list); System.out.println(set); } }
如果你注意到, Function<List<String>,Set<String>> f2 = (nameList) -> new HashSet<>(nameList);
是类型 (args) -> new ClassName(args)
这里
- args是类型的
list
- classname是
HashSet
所以Function<List<String>,Set<String>> f2 = (nameList) -> new HashSet<>(nameList);
可以转换为Function<List<String>,Set<String>> f3= HashSet::new;
其中 - 第一的
Function
参数类型(列表)是散列构造函数的参数。