深入理解Java中的构造函数引用和方法引用

JDK 8 见证了一个特殊特性的出现:构造函数引用和方法引用。在本文中, Adrian D. Finlay 探讨了开发人员如何释放构造函数引用的真正潜力。

方法引用的一些背景

如果你还不知道 Java 构造函数本身就是特殊的方法,那么阅读方法引用的基本示例将对读者有所帮助,通过了解这些内容,可以了解构造函数引用是什么。

「方法引用为已经有名称的方法提供易读的 lambda 表达式。」

「它们提供了一种无需执行就可以引用方法的简单方式。」

以上引自《Java 8 编程参考官方教程(第 9 版)》,作者:Herbert Schildt

方法引用可以引用静态方法和实例方法,两者是通用的。方法引用是函数式接口的实例。虽然 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

使用方法引用的主要步骤有:

1.定义一个函数式接口

2.定义一个满足函数式接口抽象方法要求的方法

3.使用对步骤2中定义的 (x :: y ) 方法引用实例化函数式接口的实例。

译注:静态方法的方法引用格式为 类名 :: 方法名 ;实例方法的方法引用格式为 对象实例名 :: 方法名 。

4.使用函数式接口实例调用方法: 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 实例,但是为了概念的完整性,还是要提及。

使用构造函数的方法引用的主要步骤有:

1.定义一个只有抽象方法的函数式接口,该方法的返回类型与你打算使用该对象进行构造函数引用的对象相同。

2.创建一个类,该类的构造函数与函数式接口的抽象方法匹配。

3.使用对步骤 #2 中定义的构造函数的方法引用,实例化函数式接口的实例。

译注:构造函数的方法引用格式为 类名 :: new

4.在步骤 #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的一个非常有用的补充 ;)

英文链接: jaxenter

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java 8新特性方法引用详细介绍

    Java 8新特性方法引用 对于引用来说我们一般都是用在对象,而对象引用的特点是:不同的引用对象可以操作同一块内容! Java 8的方法引用定义了四种格式: 引用静态方法     ClassName :: staticMethodName 引用对象方法:  Object:: methodName 引用特定类型方法: ClassName :: methodName 引用构造方法: ClassName  :: new  静态方法引用示例 /** * 静态方法引用 * @param <P> 引用方法

  • Java编程中的构造函数详细介绍

    本文主要是为新手.对java语言感兴趣的人和那些没有系统学习过java基础知识的人进行一个总结,在文章中对构造函数进行了较为详细的说明和讨论,也包含了我个人对于java面向对象中构造函数的一些看法.希望走在java学习道路上的同行者可以有一个较为清晰的认知和理解.当然仅为个人观点,水平有限,不足之处,还请大家多多指出,互相交流学习. 1.构造函数的概念 很多java新手谈到构造函数就会犯晕,我们先来看看什么是构造函数. 首先,构造函数是函数的一种特殊形式,特殊在哪里?构造函数中不需要定义返回类型

  • 详解Java弱引用(WeakReference)的理解与使用

    看到篇帖子, 国外一个技术面试官在面试senior java developer的时候, 问到一个weak reference相关的问题. 他没有期望有人能够完整解释清楚weak reference是什么, 怎么用, 只是期望有人能够提到这个concept和java的GC相关. 很可惜的是, 20多个拥有5年以上java开发经验的面试者中, 只有两人知道weak reference的存在, 而其中只有一人实际用到过他. 无疑, 在interviewer眼中, 对于weak reference的理

  • 30分钟入门Java8之方法引用学习

    前言 之前两篇文章分别介绍了Java8的lambda表达式和默认方法和静态接口方法.今天我们继续学习Java8的新语言特性--方法引用(Method References). 在学习lambda表达式之后,我们通常使用lambda表达式来创建匿名方法.然而,有时候我们仅仅是调用了一个已存在的方法.如下: Arrays.sort(stringsArray,(s1,s2)->s1.compareToIgnoreCase(s2)); 在Java8中,我们可以直接通过方法引用来简写lambda表达式中已

  • Java构造函数的相互调用代码示例

    在Java中,当为一个类创建了多个构造函数时,有时想在一个构造函数中调用另一个构造函数以减少代码量.这时可以使用this关键字来实现. 有关构造函数的相关内容,大家可以参阅:Java编程中的构造函数详细介绍 通常,当使用this关键字时,它意味着"这个对象"或者"当前对象",并且它自身产生对当前对象的引用.在一个构造函数中,当给传递给它一个参数列表时,它就有了不同的意义. 它将直接的调用能够匹配这个参数列表的构造函数.因此,我么可以直接的调用其它构造函数: pack

  • 如何更好的使用Java8中方法引用详解

    前言 方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法.方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文.计算时,方法引用会创建函数式接口的一个实例. 当Lambda表达式中只是执行一个方法调用时,不用Lambda表达式,直接通过方法引用的形式可读性更高一些.方法引用是一种更简洁易懂的Lambda表达式. 注意:方法引用是一个Lambda表达式,其中方法引用的操作符是双冒号"::". 在Java8中,使用方法引用非常简单,如String

  • Java构造函数与普通函数用法详解

    函数也被称为方法! 函数的作用及特点: 1.用于定义功能,将功能封装. 2.可以提高代码的复用性. 函数注意事项: 1.不能进行函数套用(不可以在函数内定义函数). 2.函数只有被调用才能被执行. 3.基本数据类型(String.int.-.)修饰的函数类型,要有return返回值. 4.void修饰的函数,函数中的return语句可以省略不写. 5.函数名可以根据需求进行命名. 代码示例:(有无函数/方法的区别) 无函数/方法代码例子: public class NoFunc { public

  • 深入理解Java中的构造函数引用和方法引用

    JDK 8 见证了一个特殊特性的出现:构造函数引用和方法引用.在本文中, Adrian D. Finlay 探讨了开发人员如何释放构造函数引用的真正潜力. 方法引用的一些背景 如果你还不知道 Java 构造函数本身就是特殊的方法,那么阅读方法引用的基本示例将对读者有所帮助,通过了解这些内容,可以了解构造函数引用是什么. 「方法引用为已经有名称的方法提供易读的 lambda 表达式.」 「它们提供了一种无需执行就可以引用方法的简单方式.」 以上引自<Java 8 编程参考官方教程(第 9 版)>

  • 10分钟带你理解Java中的弱引用

    前言 本文尝试从What.Why.How这三个角度来探索Java中的弱引用,帮助大家理解Java中弱引用的定义.基本使用场景和使用方法. 一. What--什么是弱引用? Java中的弱引用具体指的是java.lang.ref.WeakReference<T>类,我们首先来看一下官方文档对它做的说明: 弱引用对象的存在不会阻止它所指向的对象被垃圾回收器回收.弱引用最常见的用途是实现规范映射(canonicalizing mappings,比如哈希表). 假设垃圾收集器在某个时间点决定一个对象是

  • 深入理解Java中的final关键字_动力节点Java学院整理

    Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什么是final关键字?将变量,方法和类声明为final代表了什么?使用final的好处是什么?最后也有一些使用final关键字的实例.final经常和static一起使用来声明常量,你也会看到final是如何改善应用性能的. final关键字的含义? final在Java中是一个保留的关键字,可以声明成员变量.方法.类以及本地变量.一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如

  • 彻底理解Java中的ThreadLocal

    ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量.  ThreadLocal是什么 早在JDK 1.2的版本中就提供Java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编写出优美的多线程程序. 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本. 从线程的角度看,目标变

  • 理解java中的深复制和浅复制

    Java语言的一个优点就是取消了指针的概念,但也导致了许多程序员在编程中常常忽略了对象与引用的区别,本文会试图澄清这一概念.并且由于Java不能通过简单的赋值来解决对象复制的问题,在开发过程中,也常常要要应用clone()方法来复制对象.本文会让你了解什么是影子clone与深度clone,认识它们的区别.优点及缺点. 看到这个标题,是不是有点困惑:Java语言明确说明取消了指针,因为指针往往是在带来方便的同时也是导致代码不安全的根源,同时也会使程序的变得非常复杂难以理解,滥用指针写成的代码不亚于

  • 探讨Java中函数是值传递还是引用传递问题

    相信有些同学跟我一样,曾经对这个问题很疑惑.在网上也看了一些别人说的观点,评论不一.有说有值传递和引用传递两种,也有说只有值传递的,这里只说下个人见解 先给大家介绍下概念 值传递:(形式参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值. 引用传递:(形式参数类型是引用数据类型参数):也称为传地址.方法调用时,实际参数是对象(或数组),这时实际参数与

  • 深入理解java中的null“类型”

    本文研究的主要是java中的null"类型"的相关实例,具体介绍如下. 先给出一道简单的null相关的题目,引发我们对null的探讨,后面会根据官方语言手册对null"类型"进行解读. 题目:下面程序能正确运行吗? 解析: 输出应该为 :haha 因为null 是可以强转为任何类类型的,所以前面((NULL)null)是合法的,但是null强转以后是无效对象,其返回值为null,(后面会作解释) 而haha方法是静态方法,静态方法使用静态绑定,不会抛出空指针异常.

  • 深入理解Java中的HashMap

    一.HashMap的结构图示 ​本文主要说的是jdk1.8版本中的实现.而1.8中HashMap是数组+链表+红黑树实现的,大概如下图所示.后面还是主要介绍Hash Map中主要的一些成员以及方法原理. ​那么上述图示中的结点Node具体类型是什么,源码如下.Node是HashMap的内部类,实现了Map.Entery接口,主要就是存放我们put方法所添加的元素.其中的next就表示这可以构成一个单向链表,这主要是通过链地址法解决发生hash冲突问题.而当桶中的元素个数超过阈值的时候就换转为红黑

  • 一文带你真正理解Java中的内部类

    目录 概述 内部类介绍和分类 常规内部类 局部内部类 匿名内部类 静态内部类 静态内部类和普通内部类的区别 内部类的作用 概述 不知道大家在平时的开发过程中或者源码里是否留意过内部类,那有思考过为什么要有内部类,内部类都有哪几种形式,静态内部类和普通内部类有什么区别呢?本篇文章主要带领大家理解下这块内容. 内部类介绍和分类 顾名思义,内部类是指一个类在另外一个类的内部,是定义在另一个类中的类.根据类的位置和属性不同,可以分为下面几种. 常规内部类 @Data public class Tree

  • 深入理解java中的重载和覆盖

    说到java中的重载和覆盖呢,大家都很熟悉了吧,但是呢我今天就要写这个. 本文主题: 一.什么是重载 二.什么是覆盖 三.两者之间的区别 重载(overload): 在一个类中,如果出现了两个或者两个以上的同名函数,只要它们的参数的个数,或者参数的类型不同,即可称之为该函数重载了. 即当函数同名时,只看参数列表.和返回值类型没关系. 重载使用的时候需要注意: 1.在使用重载时只能通过不同的参数样式.例如,不同的参数类型,不同的参数个数,不同的参数顺序. 2.方法的异常类型和数目不会对重载造成影响

随机推荐