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参数类型(列表)是散列构造函数的参数。

