深入理解Java中的构造函数引用和方法引用
JDK8见证了一个特殊特性的出现:构造函数引用和方法引用。在本文中,AdrianD.Finlay探讨了开发人员如何释放构造函数引用的真正潜力。
方法引用的一些背景
如果你还不知道Java构造函数本身就是特殊的方法,那么阅读方法引用的基本示例将对读者有所帮助,通过了解这些内容,可以了解构造函数引用是什么。
「方法引用为已经有名称的方法提供易读的lambda表达式。」
「它们提供了一种无需执行就可以引用方法的简单方式。」
以上引自《Java8编程参考官方教程(第9版)》,作者:HerbertSchildt
方法引用可以引用静态方法和实例方法,两者是通用的。方法引用是函数式接口的实例。虽然Lambda表达式允许你动态创建方法实现,但通常情况下,一个方法最终会调用Lambda表达式中的另一个方法来完成我们想要完成的工作。更直接的方法是使用方法引用。当你已经有一个方法来实现这个函数式接口时,这是非常有用的。
让我们看一个使用静态方法及实例方法的示例。
//step#1-Createafunnctionalinterface. interfaceFuncInt{ //containsoneandonlyabstractmethod Stringanswer(Stringx,booleany); } //step#2-Classprovidingmethod(s)thatmatchFuncInt.answer()'sdefinition. classAnswer{ staticStringans_math_static(Stringx,Booleany){ return"\""+x+"\""+"\t=\t"+y.toString().toUpperCase(); } Stringans_math_inst(Stringx,Booleany){ 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:IsChengduinSichuan?",true); Answer.ans_math_static("-1%0.2=0?",false); Answer.ans_math_static("T/F:DoesDwyneWadeplayfortheKnicks?",false);
得到与原文举例相同的输出结果:
"9>11?"=FALSE "987.6<1.1?"=FALSE "1>0.9?"=TRUE "T/F:IsChengduinSichuan?"=TRUE "-1%0.2=0?"=FALSE "T/F:DoesDwyneWadeplayfortheKnicks?"=FALSE
使用方法引用的主要步骤有:
1.定义一个函数式接口
2.定义一个满足函数式接口抽象方法要求的方法
3.使用对步骤2中定义的(x::y)方法引用实例化函数式接口的实例。
译注:静态方法的方法引用格式为类名::方法名;实例方法的方法引用格式为对象实例名::方法名。
4.使用函数式接口实例调用方法:Instance.AbstractMethod();
这提供了一种创建方法实现的可插拔方式。Lambda表达式和方法引用为Java编程带来了一个功能方面的提升。
构造函数的方法引用
让我们开始详细讨论吧。
构造函数和其他方法一样是方法。对吗?错。它们有点特殊,它们是对象初始化方法。尽管如此,它们仍然是一个方法,没有什么能阻止我们像其他方法引用一样创建构造函数的方法引用。
//step#1-Createafunnctionalinterface. interfaceFuncInt{ //containsoneandonlyabstractmethod Automobileauto(Stringmake,Stringmodel,shortyear); } //step#2-Classprovidingmethod(s)thatmatchFuncInt.answer()'sdefinition. classAutomobile{ //TrunkMemberVariables privateStringmake; privateStringmodel; privateshortyear; //AutomobileConstructor publicAutomobile(Stringmake,Stringmodel,shortyear){ this.make=make; this.model=model; this.year=year; } protectedvoidwhat(){ System.out.println("ThisAutomobileisa"+year+""+make+""+model+"."); } } //Step#3-Classmakinguseofmethodreference publicclassConstrRef{ staticvoidcreateInstance(){ } publicstaticvoidmain(String[]args){ System.out.println(); //Remember,aMethodReferenceisaninstanceofaFunctionalInterface.Therefore.... FuncIntauto=Automobile::new;//Wereallydon'tgainmuchfromthisexample //Example#1 Automobilehonda=auto.auto("honda","Accord",(short)2006); honda.what(); //Example#1 Automobilebmw=auto.auto("BMW","530i",(short)2006); bmw.what(); System.out.println(); } }
输出结果
ThisAutomobileisa2006hondaAccord. ThisAutomobileisa2006BMW530i.
说明
用户应该清楚的第一件事是这个基本示例没有那么实用。这是一种相当迂回的创建对象实例的方式。实际上,几乎可以肯定,你不会经历所有这些麻烦来创建一个Automobile实例,但是为了概念的完整性,还是要提及。
使用构造函数的方法引用的主要步骤有:
1.定义一个只有抽象方法的函数式接口,该方法的返回类型与你打算使用该对象进行构造函数引用的对象相同。
2.创建一个类,该类的构造函数与函数式接口的抽象方法匹配。
3.使用对步骤#2中定义的构造函数的方法引用,实例化函数式接口的实例。
译注:构造函数的方法引用格式为类名::new
4.在步骤#2中使用构造函数引用实例化类的实例,例如MyClassx=ConstructorReference.AbstractMethod(x,y,z…)
构造函数引用与泛型一起使用的时候变得更有用。通过使用泛型工厂方法,可以创建各种类型的对象。
让我们看一看。
//step#1-Createafunnctionalinterface. interfaceFuncInt{ //containsoneandonlyabstractmethod Obfunc(Xmake,Ymodel,Zyear); } //step#2-CreateaGenericclassprovidingaconstructorcompatiblewithFunInt.func()'sdefinition classAutomobile { //AutomobileMemberVariables privateXmake; privateYmodel; privateZyear; //AutomobileConstructor publicAutomobile(Xmake,Ymodel,Zyear){ this.make=make; this.model=model; this.year=year; } protectedvoidwhat(){ System.out.println("ThisAutomobileisa"+year+""+make+""+model+"."); } } //step#3-CreateaNon-GenericclassprovidingaconstructorcompatiblewithFunInt.func()'sdefinition classPlane{ //AutomobileMemberVariables privateStringmake; privateStringmodel; privateintyear; //PlaneConstructor publicPlane(Stringmake,Stringmodel,intyear){ this.make=make; this.model=model; this.year=year;//Automaticunboxing } protectedvoidwhat(){ System.out.println("ThisPlaneisa"+year+""+make+""+model+"."); } } //Step#3-Classmakinguseofmethodreferencewithgenerics publicclassConstrRefGen{ //Hereiswherethemagichappens static Obfactory(FuncInt obj,Xp1,Yp2,Zp3){ returnobj.func(p1,p2,p3); } publicstaticvoidmain(String[]args){ System.out.println(); //Example#1 FuncInt ,String,String,Integer>auto_cons=Automobile ::new; Automobile honda=factory(auto_cons,"Honda","Accord",2006); honda.what(); //Example#2 FuncInt plane_cons=Plane::new; Planecessna=factory(plane_cons,"Cessna","Skyhawk",172); cessna.what(); System.out.println(); } }
输出结果
ThisAutomobileisa2006HondaAccord. ThisPlaneisa172CessnaSkyhawk.
说明
这里有很多东西需要消化。事实上,如果你以前从未深入研究过泛型,那么这些代码看上去可能相当晦涩。让我们分解一下。
我们做的第一件事是创建一个通用的函数式接口。注意细节。我们有四个泛型类型参数:Ob、X、Y、Z。
- Ob代表要引用其构造函数的类。
- X,Y,Z代表该类的构造函数的参数。
如果我们替换泛型方法占位符,抽象方法可能是这样的:SomeClassfunc(Stringmake,Stringmodel,intyear)。注意,由于我们使接口具有了泛型,所以可以指定任何返回类型或我们希望返回的类实例。这释放了构造函数引用的真正潜力。
接下来的两个部分相对简单,我们创建了相同的类,一个泛型类和一个非泛型类,以演示它们与在公共类中定义的工厂方法的互操作性。注意,这些类的构造函数与FuncInt.func()的方法签名是兼容的。
进入公共类的文件。这个方法就是奇迹发生的地方。
//Hereiswherethemagichappens staticObfactory(FuncInt obj,Xp1,Yp2,Zp3){ returnobj.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的一个非常有用的补充;)
英文链接:jaxenter
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。