Java 8方法引用

时间:2020-02-23 14:34:53  来源:igfitidea点击:

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;

在方法引用中,我们之前有类或者对象 ::和方法名称后 ::没有论据。

我们是否注意到方法引用没有参数?
是的,我们不需要将参数传递给方法引用,基于方法引用的类型,参数在内部自动传递参数。

以下图表将更清晰。
我们可以使用方法引用:

让我们说你想转换 countryuppercase在打印之前。
你可以使用它 anonymous classlambda expression但不是方法引用。

我们不能使用方法引用,如下所示:

我们可以显然使用Stream的Map()方法来转换

countryuppercase在打印之前。
我只是想在方法引用无法使用时演示。

方法类型引用

有四种类型的方法引用。

  • 引用静态方法
  • 对象类型的实例方法引用
  • 引用现有对象的实例方法
  • 引用构造函数

引用静态方法

当我们有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&lt;Integer,Integer&gt; function2 = (num) -&gt; PowerFunctions.power(num);是类型 (args) -> className.someStaticMethod(args)
其中

  • PowerFunctions是类名
  • someStaticMethod是 power的方法
  • num是power方法的参数。

我们呼叫静态方法 power课堂 PowerFunctions在Lambda表达式中,这就是为什么我们可以将其用作方法引用。

而不是 Function&lt;Integer,Integer> function2 = (num) -> PowerFunctions.power(num);我们可以用 Function&lt;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&lt;String,Integer,String> bf2 = (t,u) -> t.substring(u);是类型 (obj,args) -> obj.someInstanceMethod(args)这里

  • obj是字符串类型。
  • 某种子义务是字符串的 substring()方法。
  • args是 beginIndex为了 substring()方法参数。

所以 BiFunction&lt;String,Integer,String> bf2 = (t,u) -> t.substring(u);可以转换为 BiFunction&lt;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&lt;Long> popCons2 = (population) -&gt; c.setPopulation(population);是类型 (args) -> obj.someInstanceMethod(args)这里

  • obj是国家类型,并在其他地方声明。
  • 某种主义的是国家的setPopulation方法。
  • args是 population用于setPopulation方法参数。

所以 Consumer&lt;Long&gt; popCons2= (population) -&gt; c.setPopulation(population);可以转换为 Consumer&lt;Long&gt; popCons3 = c::setPopulation;其中

  • 第一个消费者参数类型(long)是setPopulation方法的参数。

引用构造函数

当Lambda表达式用于创建新对象/不带参数时,可以使用引用方法构造函数。
lambda表达语法 (args) -> new ClassName(args)可以转换为 ClassName::new让我们在举例的帮助下看看。
其中我们将转换列表以使用方法引用设置。 Function&lt;List&lt;String>,Set&lt;String>>是功能接口,它将列出一个参数,并通过调用hashset构造函数来返回设置

public HashSet(Collection&lt;? 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&lt;List&lt;String&gt;,Set&lt;String&gt;&gt; f2 = (nameList) -&gt; new HashSet&lt;&gt;(nameList);是类型 (args) -> new ClassName(args)这里

  • args是类型的 list
  • classname是 HashSet所以 Function&lt;List&lt;String&gt;,Set&lt;String&gt;&gt; f2 = (nameList) -&gt; new HashSet&lt;&gt;(nameList);可以转换为 Function&lt;List&lt;String&gt;,Set&lt;String&gt;&gt; f3= HashSet::new;其中
  • 第一的 Function参数类型(列表)是散列构造函数的参数。