Java中的构造函数引用和方法引用

方法引用的一些背景
如果你还不知道 Java 构造函数本身就是特殊的方法,那么阅读方法引用的基本示例将对读者有所帮助,通过了解这些内容,可以了解构造函数引用是什么。
方法引用可以引用静态方法和实例方法,两者是通用的。方法引用是函数式接口的实例。虽然 Lambda 表达式允许你动态创建方法实现,但通常情况下,一个方法最终会调用 Lambda 表达式中的另一个方法来完成我们想要完成的工作。更直接的方法是使用方法引用。当你已经有一个方法来实现这个函数式接口时,这是非常有用的。
让我们看一个使用静态方法及实例方法的示例。
//step #1 - Create a funnctional interface.
interface FuncInt {
//contains one and only abstract method
String answer(String x, boolean y);
}

//step #2 - Class providing method(s)that match FuncInt.answer()‘s definition.
class Answer {
static String ans_math_static(String x, Boolean y) {
return "\"" + x + "\"" + "\t = \t" + y.toString().toUpperCase();
}

String ans_math_inst(String x, Boolean y) {
return "\"" + x + "\"" + "\t = \t" + y.toString().toUpperCase();
}
}
译注:以上代码的测试用例如下,因静态方法与实例方法结果相同,仅以静态方法为例。
Answer.ans_math_static("9 > 11 ?", false);
Answer.ans_math_static("987.6 < 1.1 ?", false);
Answer.ans_math_static("1 > 0.9 ?", true);
Answer.ans_math_static("T/F: Is Chengdu in Sichuan?", true);
Answer.ans_math_static("-1 % 0.2=0 ?", false);
Answer.ans_math_static("T/F: Does Dwyne Wade play for the Knicks?", false);
得到与原文举例相同的输出结果:
"9 > 11 ?" = FALSE
"987.6 < 1.1 ?" = FALSE
"1 > 0.9 ?" = TRUE
"T/F: Is Chengdu in Sichuan?" = TRUE
"-1 % 0.2=0 ?" = FALSE
"T/F: Does Dwyne Wade play for the Knicks?" = FALSE
使用方法引用的主要步骤有:
定义一个函数式接口
定义一个满足函数式接口抽象方法要求的方法
使用对步骤2中定义的 (x :: y ) 方法引用实例化函数式接口的实例。
译注:静态方法的方法引用格式为 类名 :: 方法名 ;实例方法的方法引用格式为 对象实例名 :: 方法名 。
使用函数式接口实例调用方法: Instance.AbstractMethod();
这提供了一种创建方法实现的可插拔方式。Lambda 表达式和方法引用为 Java 编程带来了一个功能方面的提升
构造函数的方法引用
让我们开始详细讨论吧。
构造函数和其他方法一样是方法。对吗?错。它们有点特殊,它们是对象初始化方法。尽管如此,它们仍然是一个方法,没有什么能阻止我们像其他方法引用一样创建构造函数的方法引用。
//step #1 - Create a funnctional interface.
interface FuncInt {
//contains one and only abstract method
Automobile auto(String make, String model, short year);
}

//step #2 - Class providing method(s)that match FuncInt.answer()‘s definition.
class Automobile {

//Trunk Member Variables
private String make;
private String model;
private short year;

//Automobile Constructor
public Automobile(String make, String model, short year) {
this.make = make;
this.model = model;
this.year = year;
}

protected void what() {
System.out.println("This Automobile is a" + year + " " + make + " " + model + ".");
}
}

//Step #3 - Class making use of method reference
public class ConstrRef {

static void createInstance() {
}

public static void main(String[] args) {
System.out.println();

//Remember, a Method Reference is an instance of a Functional Interface. Therefore....
FuncInt auto = Automobile::new;//We really don‘t gain much from this example

//Example #1
Automobile honda = auto.auto("honda", "Accord", (short) 2006);
honda.what();

//Example #1
Automobile bmw = auto.auto("BMW", "530i", (short) 2006);
bmw.what();

System.out.println();
}
}
输出结果This Automobile is a2006 honda Accord.
This Automobile is a2006 BMW 530i.
说明
用户应该清楚的第一件事是这个基本示例没有那么实用。这是一种相当迂回的创建对象实例的方式。实际上,几乎可以肯定,你不会经历所有这些麻烦来创建一个 Automobile 实例,但是为了概念的完整性,还是要提及。
使用构造函数的方法引用的主要步骤有:
定义一个只有抽象方法的函数式接口,该方法的返回类型与你打算使用该对象进行构造函数引用的对象相同。
创建一个类,该类的构造函数与函数式接口的抽象方法匹配。
使用对步骤 #2 中定义的构造函数的方法引用,实例化函数式接口的实例。
译注:构造函数的方法引用格式为 类名 :: new
在步骤 #2 中使用构造函数引用实例化类的实例,例如 MyClass x = ConstructorReference.AbstractMethod (x, y, z…)
构造函数引用与泛型一起使用的时候变得更有用。通过使用泛型工厂方法,可以创建各种类型的对象。
让我们看一看。
//step #1 - Create a funnctional interface.
interface FuncInt<Ob, X, Y, Z> {
//contains one and only abstract method
Ob func(X make, Y model, Z year);
}

//step #2 - Create a Generic class providing a constructor compatible with FunInt.func()‘s definition
class Automobile<X, Y, Z> {

//Automobile Member Variables
private X make;
private Y model;
private Z year;

//Automobile Constructor
public Automobile(X make, Y model, Z year) {
this.make = make;
this.model = model;
this.year = year;
}

protected void what() {
System.out.println("This Automobile is a " + year + " " + make + " " + model + ".");
}
}

//step #3 - Create a Non-Generic class providing a constructor compatible with FunInt.func()‘s definition
class Plane {

//Automobile Member Variables
private String make;
private String model;
private int year;

//Plane Constructor
public Plane(String make, String model, int year) {
this.make = make;
this.model = model;
this.year = year;//Automatic unboxing
}

protected void what() {
System.out.println("This Plane is a " + year + " " + make + " " + model + ".");
}
}

//Step #3 - Class making use of method reference with generics
public class ConstrRefGen {

//Here is where the magic happens
static <Ob, X, Y, Z> Ob factory(FuncInt<Ob, X, Y, Z> obj, X p1, Y p2, Z p3) {
return obj.func(p1, p2, p3);
}

public static void main(String[] args) {
System.out.println();

//Example #1
FuncInt<Automobile<String, String, Integer>, String, String, Integer> auto_cons = Automobile<String, String, Integer>::new;
Automobile<String, String, Integer> honda = factory(auto_cons, "Honda", "Accord", 2006);
honda.what();

//Example #2
FuncInt<Plane, String, String, Integer> plane_cons = Plane::new;
Plane cessna = factory(plane_cons, "Cessna", "Skyhawk", 172);
cessna.what();

System.out.println();
}
}
输出结果
This Automobile is a 2006 Honda Accord.
This Plane is a 172 Cessna Skyhawk.
说明
这里有很多东西需要消化。事实上,如果你以前从未深入研究过泛型,那么这些代码看上去可能相当晦涩。让我们分解一下。
我们做的第一件事是创建一个通用的函数式接口。注意细节。我们有四个泛型类型参数:Ob、X、Y、Z。
Ob 代表要引用其构造函数的类。
X,Y,Z 代表该类的构造函数的参数。
如果我们替换泛型方法占位符,抽象方法可能是这样的: SomeClass func (String make, String model, int year)。注意,由于我们使接口具有了泛型,所以可以指定任何返回类型或我们希望返回的类实例。这释放了构造函数引用的真正潜力。
接下来的两个部分相对简单,我们创建了相同的类,一个泛型类和一个非泛型类,以演示它们与在公共类中定义的工厂方法的互操作性。注意,这些类的构造函数与 FuncInt.func() 的方法签名是兼容的。
进入公共类的文件。这个方法就是奇迹发生的地方。
//Here is where the magic happens
static <Ob, X, Y, Z> Ob factory(FuncInt<Ob, X, Y, Z> obj, X p1, Y p2, Z p3) {
return obj.func(p1, p2, p3);
}
我们将该方法标记为静态的,所以我们可以不使用 ConstRefGen 实例,毕竟它是一个工厂方法。注意,factory 方法具有与函数式接口相同的泛型类型参数。注意,方法的返回类型是 Ob,它可以是由我们决定的任何类。当然,X、Y、Z是 Ob 中方法的方法参数。请注意,该函数以 FuncInt 的一个实例作为参数(类类型和方法参数作为类型参数),同时也接受 Ob 类型的类作为方法的参数。
在方法体中,它调用方法引用并将在 factory() 中传递的参数提供给它。
我们的第一个任务是创建一个符合 FuncInt<> 的方法引用。
这里我们分别引用 Automobile 类和 Plane 类的构造函数。
我们的下一个任务是创建一个带有方法引用的对象。
为此,我们调用 factory() 并将它需要的构造函数引用以及 factory() 定义的有关构造函数的参数提供给它。factory() 可以灵活地创建对各种方法的构造函数引用,因为它是通用的。因为 Plane 类和 Automobile 类的构造函数匹配 FuncInt.func() 的方法签名,所以它们可作为 FuncInt.func() 的方法引用使用。factory() 通过调用 obj.func(x,y,z) 返回类的一个实例,这是一个构造函数方法引用,当求值时,它将为你提供指定为其参数的类的一个实例。
斟酌这个问题一段时间,会发现它是Java的一个非常有用的补充 ;)

原文地址:http://blog.51cto.com/14158311/2347192

时间: 2024-10-12 18:59:14

Java中的构造函数引用和方法引用的相关文章

Java数组小知识和UML知识 java中的参数传递——值传递、引用传递

数组声明后必须用new为其分配空间 java中数组有没有length()方法,求数组的长度可以使用数组的length属性. 1 2 int[] arr={1,2,3,4,5}; int length=arr.length;//求数组的长度 String 有length()方法,用来求字符串的长度 1 2 String  str="Hello"; int length=str.length(): //求字符串的长度 UML是一个结构图,常用来描述一个系统的静态结构,统一建模语言. Jav

Java中带包(创建及引用)的类的编译与调试

Java中带包(创建及引用)的类的编译与调试 java源程序的编译大家都知道,也就是cmd中到源文件所在目录下javac **.java即可,当程序中有包声明还能简简单单的直接javac **.java吗?答案当然是no,下面举个简单的例子证明一下直接javac **.java会怎么样. 如下:F:\javaweb2班\20160531目录下有A.java文件,注意源文件中有包声明 package mypack; public class A { String name; int age; pu

Java 中extends与implements使用方法 (转载)

转自:http://blog.csdn.net/chen_chun_guang/article/details/6323201 初学Java语言, 代码中的extends和implements让我感到很迷惑,现在终于弄明白它们之间的区别和用法了. //定义一个Runner接口 public inerface Runner { int ID = 1; void run (); } //定义一个interface Animal,它继承于父类Runner interface Animal extend

将java中数组转换为ArrayList的方法实例(包括ArrayList转数组)

方法一:使用Arrays.asList()方法 1 2 String[] asset = {"equity", "stocks", "gold", "foreign exchange","fixed income", "futures", "options"}; List<String> assetList = Arrays.asList(asset);

Java中有关构造函数的一道笔试题解析

Java中有关构造函数的一道笔试题解析 1.具体题目如下 下列说法正确的有() A. class中的constructor不可省略 B. constructor必须与class同名,但方法不能与class同名 C. constructor在一个对象被new时执行 D.一个class只能定义一个constructor 2.解析说明 (1)class中的构造函数是可以省略的 /** * @Title:User.java * @Package:com.you.user.model * @Descrip

Java中的构造函数和重载

一.Java中的构造函数 构造函数是对象被创建时初始化对象的成员方法,它具有和它所在的类完全一样的名字.构造函数只能有入口参数,没有返回类型,因为一个类的构造方法的返回类就是类本身.构造函数定义后,创建对象时就会自动调用它,对新创建的对象分配内存空间和初始化.在 Java 中,构造函数也可以重载,当创建一个对象时, JVM 会自动根据当前对方法的调用形式在类的定义中匹配形式符合的构造方法,匹配成功后执行该构造方法. eg: public Class Dog { private int age:

JAVA中Runtime类以及exec()方法,Process的使用

package ioTest.io1; /* * Runtime:每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接. * 这也是jvm实现跨平台的一个重要原因. * 可以通过 getRuntime 方法获取当前运行时. * API分析: * public class Runtime extends Object * 每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接. * 可以通过 getRuntime 方法获

Java中各种(类、方法、属性)访问修饰符与修饰符的说明

类: 访问修饰符 修饰符 class 类名称 extends 父类名称 implement 接口名称 (访问修饰符与修饰符的位置可以互换) 访问修饰符 名称 说明 备注 public 可以被本项目的所有类访问(使用),其他项目若想使用本项目中的类,必须将本项目打包为jar包,然后加入到classpath中 public类必须定义在和类名相同的同名文件中 (default) 只能被同一个包中的类访问(使用) 默认的访问权限,可以省略此关键字,可以定义在和public类的同一个文件中 修饰符 名称

为什么Java中有些接口没有任何方法

由于Java不支持多重继承,即一个类只能有一个父类,为了克服单继承的缺点,Java语言引入了接口这一概念.接口是抽象方法定义的集合(接口中也可以定义一些常量值),是一种特殊的抽象类.接口中只包含方法的定义,没有方法的实现.接口中的所有方法都是抽象的.接口中成员的作用域修饰符都是public,接口中的常量值默认使用public static final修饰.由于一个类可以实现多个接口,因此通常可以采用实现多个接口的方式来间接的达到多重继承的目的. 在Java语言中,有些接口内部没有声明任何方法,也