Java自定义Function接口

x33g5p2x  于2022-09-23 转载在 Java  
字(8.4k)|赞(0)|评价(0)|浏览(1380)

本文将介绍 Java 自定义Function接口示例。 Java 提供了 @FunctionalInterface 注解来创建Function接口。 @FunctionalInterface 从 Java 8 开始可用。函数式接口只有一个抽象方法。可以使用 lambda 表达式、方法引用或构造函数引用来初始化Function接口。Function接口可以有默认方法。也可以通过继承另一个Function接口来创建Function接口。 Java提供了SupplierConsumerPredicate等内置Function接口。
在本文中,我们将使用 @FunctionalInterface 注释创建自定义Function接口。在我们的示例中,我们将使用泛型、默认方法和继承来创建Function接口。我们还将提供使用 lambda 表达式、方法引用或构造函数引用来初始化Function接口的示例。现在让我们逐步讨论创建自定义Function接口。

@FunctionalInterface

1.@FunctionalInterface 注解用于创建Function接口。
2. 一个函数式接口只有一个抽象方法。
3. 接口的默认方法不计为抽象,因为它们有实现。
4. 如果函数式接口声明了一个覆盖 Java Object 类的公共方法之一的抽象方法,那也不会被计算在内。
5. 函数式接口的实例可以使用 lambda 表达式、方法引用或构造函数引用来创建。

创建Function接口

要创建我们的函数式接口,我们需要创建一个带有 @FunctionalInterface 注释的接口和一个抽象方法。接口中的抽象方法后跟分号,但没有大括号。
计算器.java

package com.concretepage;

@FunctionalInterface
public interface Calculator {
   long calculate(long num1, long num2);
}

在这里,我们使用抽象方法 calculate 创建了 Calculator 接口。接口 Calculator@FunctionalInterface 注释,这样我们就创建了一个Function接口,即 Calculator。我们可以使用 lambda 表达式、方法引用或构造函数引用来实例化一个函数式接口。

使用 Lambda 表达式实例化Function接口

在这里,我们将使用 lambda 表达式实例化一个函数式接口。查找 lambda 表达式语法。

(Argument  part)  -> Body part

现在我们将实例化我们的Function接口 Calculator,如下所示。

Calculator calc = (n1, n2) -> n1 + n2;

在上面的 lambda 表达式中,参数的数量是两个,因为抽象方法 calculate 已经定义了两个参数。为了得到结果,我们将调用函数接口的方法。

System.out.println(calc.calculate(30, 50));

输出将是 80。

使用 Lambda 表达式将Function接口作为方法参数传递:

现在在一个类中创建一个方法,其参数将是我们的Function接口类型,如下所示。

public long process(Calculator calc) {
    return calc.calculate(this.firstNum, this.secondNum);
}

该类将如下所示。
MyNumber.java

package com.concretepage;
public class MyNumber {
    private long firstNum;
    private long secondNum;
    public MyNumber(long firstNum, long secondNum) {
	   this.firstNum = firstNum;
	   this.secondNum = secondNum;
    }
    public long process(Calculator calc) {
       return calc.calculate(this.firstNum, this.secondNum);
    }
    //setters getters
}

我们可以直接将 lambda 表达式作为参数或函数接口的实例传递给上述类中的 process 方法。假设我们有一个如下的 MyNumber 列表。

List&ltMyNumber> list = new ArrayList<>();
list.add(new MyNumber(100, 40));
list.add(new MyNumber(300, 60));
list.add(new MyNumber(60, 20));

我们可以通过以下方式运行我们的Function接口。
示例 1:
在这里,我们正在创建函数接口的对象,然后将其作为求和的参数传递。

Calculator calc = (n1, n2) -> n1 + n2;
for(MyNumber myNumber: list) {
   System.out.println(myNumber.process(calc));
}

输出。

140
360
80

示例 2:
在这里,我们直接将 lambda 表达式作为乘法参数传递。

for(MyNumber myNumber: list) {
   System.out.println(myNumber.process((n1, n2) -> n1 * n2));
}

输出

4000
18000
1200

示例 3:
这里我们进行除法。

for(MyNumber myNumber: list) {
   System.out.println(myNumber.process((n1, n2) -> n1 / n2));
}

输出。

2
5
3

使用方法参考实例化Function接口

方法引用使用 (::) 符号调用方法。假设我们有一个类 MyNumber 和一个静态方法 add,那么我们可以使用类名来调用它。

MyNumber::add

如果 add 不是静态方法,那么我们可以使用类的实例调用此方法。假设 myNumberMyNumber 类的实例并且 add 是非静态方法,那么我们使用下面给出的实例来调用它。

myNumber::add

要使用方法引用创建Function接口的实例,我们需要创建一个方法声明与抽象方法相同的方法。我们的Function接口 Calculator 中的方法如下。

long calculate(long num1, long num2);

现在我们在实用程序类中创建了两个静态方法 addmultiply,其声明与Function接口的抽象方法相同。找到实用程序类。
实用程序.java

package com.concretepage;
public class Utility {
    public static long add(long num1, long num2) {
    	return num1 + num2;
    }
    public static long multiply(long num1, long num2) {
    	return num1 * num2;
    }  
}

现在使用 Utility 类的静态方法实例化Function接口,如下所示。

Calculator calc = Utility::add;
System.out.println(calc.calculate(30, 50));

输出将是 80。

使用方法参考将Function接口作为方法参数传递:

现在让我们使用带有方法引用的 MyNumber 类。我们已经在上面创建了 MyNumber 类及其对象列表。现在找到使用方法参考。首先,我们使用实用程序 add 方法。

for(MyNumber myNumber: list) {
   Calculator calc = Utility::add;
   System.out.println(myNumber.process(calc));
}

我们也可以将引用方法直接传递给下面给出的方法。

System.out.println(myNumber.process(Utility::add));

输出

140
360
80

现在我们正在使用实用程序 multiply 方法。

for(MyNumber myNumber: list) {
   System.out.println(myNumber.process(Utility::multiply));
}

输出

4000
18000
1200

现在让我们了解上述代码是如何工作的。要理解它,请查看 MyNumber 类的 process 方法的定义。

public long process(Calculator calc) {
   return calc.calculate(this.firstNum, this.secondNum);
}

当我们调用 process(Utility::add)process(Utility::multiply) 时,我们的Function接口 Calculator 分别使用 Utility 类的 addmultiply 方法的定义来实例化。当使用给定参数调用 calculate 方法时,我们会得到结果。

使用构造函数引用实例化Function接口

在这里,我们将使用构造函数引用实例化一个Function接口。我们需要使用 new 关键字作为构造函数引用。查找 Utility 类的构造函数参考。

Utility::new

我们知道构造函数没有返回类型。因此,我们将创建一个具有抽象方法的Function接口,该方法没有返回类型,但具有与构造函数相同数量的参数和类型。找到一个Function接口。
TaskHandler.java

package com.concretepage;

@FunctionalInterface
public interface TaskHandler {
  void get(String tname);
}

我们在 Utility 类中创建了构造函数,如下所示。
实用程序.java

public class Utility {
  public Utility(String taskName) {
    System.out.println(taskName);
  }
  ------
}

现在让我们实例化我们的Function接口并运行它。

TaskHandler taskHandler = Utility::new;
taskHandler.get("Task 1");

输出将是“任务 1”。

具有默认方法的Function接口

我们可以在Function接口中创建默认方法。找到Function接口Worship
崇拜.java

package com.concretepage;

import java.util.Objects;

@FunctionalInterface
public interface Worship {
  void chant(String name);
  
  default Worship again(Worship w) {
	  return (name) -> {
		Objects.requireNonNull(w);  
		chant(name);
		w.chant(name);
	  };
  }
}

我们创建了一个名为 again 的默认方法。参数的类型是 Worship 本身。返回类型也是 Worship。现在我们必须定义我们的默认方法。由于默认方法返回 Worship,我们需要返回一个定义其抽象方法的函数,即 chant。现在查看默认方法的定义。

default Worship again(Worship w) {
  return (name) -> {
	Objects.requireNonNull(w);  
	chant(name);
	w.chant(name);
  };
}

Objects.requireNonNull 检查指定的对象引用不为空。在上面的代码中,方法 chant(name) 是调用者 Worship 实例的方法。 w.chant(name) 属于参数 Worship 实例。如果我们多次调用 again 方法,结果将被链接。现在让我们运行这个例子。

Worship worship = (name) -> System.out.println(name);

worship.again(worship).again(worship).chant("Ram");

输出

Ram
Ram
Ram

现在让我们通过一些更改来实例化 Worship,然后运行它。

Worship worship = (name) -> {
   System.out.println(name);
   System.out.println(name);
}; 

worship.again(worship).again(worship).chant("Ram");

输出

Ram
Ram
Ram
Ram
Ram
Ram

具有通用和默认方法的Function接口

我们将在这里创建一些带有泛型的Function接口。我们还将添加默认方法来使用这些Function接口。
Function接口一:
DataCombiner.java

package com.concretepage;

@FunctionalInterface
public interface DataCombiner&ltT> {
   String combine(T t);
}

Function接口二:
ExtraInfoProvider.java

package com.concretepage;

@FunctionalInterface
public interface ExtraInfoProvider&ltR> {
   R provideMore(R r);
}

Function界面3:
现在找到将在其默认方法中使用 DataCombinerExtraInfoProviderInfoProvider Function接口。
InfoProvider.java

package com.concretepage;

import java.util.Objects;

@FunctionalInterface
public interface InfoProvider&ltT, R> {
  R provide(T t);
  
  default InfoProvider&ltT, R> addMore(ExtraInfoProvider&ltR> more) {
	  return (T t) -> {
		 Objects.requireNonNull(more); 
		 R r = provide(t);
		 return more.provideMore(r);
	  };
  }
  
  default DataCombiner&ltT> addCombiner(DataCombiner&ltR> combiner) {
	  return (T t) -> {
		  Objects.requireNonNull(combiner);
		  return combiner.combine(provide(t));
	  };
  }
}

在上面的代码中,我们创建了一个抽象方法 provide 和两个默认方法 addMoreaddCombiner。默认方法 addMore 是返回 InfoProvider,所以在 addMore 中我们将返回 InfoProvider 函数接口的 provide 抽象方法的函数定义。 Objects.requireNonNull 检查指定的对象引用不为空。
addCombiner 方法中,我们返回的是 DataCombiner,所以在这个方法中,我们将返回 DataCombiner 函数接口的抽象方法 combine 的函数定义。

假设我们有两个类 EmployeeProject,如下所示。
项目.java

public class Project {
    private String pname;
    private String teamLead;
    private String location;
    public Project(String pname, String teamLead) {
	    this.pname = pname;
	    this.teamLead = teamLead;
    }
    //getters and setters
}

员工.java

public class Employee {
    private int id;
    private String name;
    public Employee(int id, String name) {
	    this.id = id;
	    this.name = name;
    }
    //getters and setters
}

现在我们将使用 lambda 表达式初始化我们的函数式接口。初始化 DataCombinerExtraInfoProviderInfoProvider 并运行它。

DataCombiner&ltProject> dataCombiner = (Project p) -> {
	if(p.getLocation() == null) {
		return p.getPname()+" : " + p.getTeamLead();
	} else {
		return p.getPname()+" : " + p.getTeamLead() + " : " + p.getLocation();
	}
};

InfoProvider&ltEmployee, Project> infoProvider = (Employee emp) -> {
	if(emp.getId() > 100) {
		return new Project("ABCD", emp.getName());
	} else {
		return new Project("PQRS", emp.getName());
	}
};

InfoProvider&ltEmployee, Project> infoProvider = (Employee emp) -> {
	if(emp.getId() > 100) {
		return new Project("ABCD", emp.getName());
	} else {
		return new Project("PQRS", emp.getName());
	}
};

String s = infoProvider.addMore(extraInfoProvider)
		.addCombiner(dataCombiner).combine(new Employee(50, "Mahesh"));

System.out.println(s);

输出

PQRS : Mahesh : Noida

Function接口继承

我们可以通过继承现有的接口来创建Function接口。假设我们有如下Function接口。
DataCombiner.java

package com.concretepage;

@FunctionalInterface
public interface DataCombiner&ltT> {
   String combine(T t);
}

现在我们将通过扩展 DataCombiner 并添加一个默认方法来创建 DataReceiver Function继承。
DataReceiver.java

package com.concretepage;

import java.util.Objects;

@FunctionalInterface
public interface DataReceiver&ltT> extends DataCombiner&ltT> {
	
	default void receive(TaskHandler handler, T t) {
		Objects.requireNonNull(handler);
		handler.get(combine(t));
	}
}

在默认方法 receive 中,我们传递了 TaskHandler Function接口。 找到 TaskHandler
TaskHandler.java

package com.concretepage;

@FunctionalInterface
public interface TaskHandler {
  void get(String tname);
}

实例化 DataReceiverTaskHandler 然后运行它。

DataReceiver&ltEmployee> dataReceiver = (Employee emp) -> emp.getId() + "-" + emp.getName();

TaskHandler tskHandler = (res) -> System.out.println(res); 

dataReceiver.receive(tskHandler, new Employee(101, "Krishna"));

输出

101-Krishna

参考

Java doc: @FunctionalInterface

相关文章