当前位置: 首页 > 知识库问答 >
问题:

Java方法参考解析[重复]

王昊
2023-03-14

我试图理解方法引用在java中是如何工作的。乍一看,这很简单。但当涉及到这些事情时:

Foo类中有一个方法:

public class Foo {
    public Foo merge(Foo another) {
        //some logic
    }
}

在另一个类Bar中有这样一个方法:

public class Bar {
    public void function(BiFunction<Foo, Foo, Foo> biFunction) {
       //some logic
    }
}

并使用方法参考:

new Bar().function(Foo::merge);

它符合并工作,但我不明白它如何匹配这个:

Foo merge(Foo another)

到BiFunction方法:

R apply(T t, U u);

???

共有2个答案

东郭海阳
2023-03-14

我发现使用不同的类型更容易理解:

public class A {

    public void test(){
        function(A::merge);
    }

    public void function(BiFunction<A, B, C> f){

    }

    public C merge(B i){
        return null;
    }

    class B{}
    class C{}
}

我们可以知道,使用方法引用测试::合并而不是实例上的引用将隐式地使用this作为第一个值。

15.13.3方法参考的运行时评估

如果形式是ReferenceType:[TypeArguments]Identifier如果编译时声明是实例方法,则目标引用是调用方法的第一个形式参数。否则,就没有目标引用。

我们可以在以下主题中找到使用此行为的一些示例:
JLS-15.13.1。方法参考的编译时声明提到:

形式为Reector ceType::[TypeArguments]标识符的方法引用表达式可以以不同的方式解释。
-如果标识符指的是一个实例方法,那么隐式lambda表达式有一个额外的参数[...]
-如果标识符指的是一个静态方法。参考类型可能有两种适用的方法,因此上述搜索算法分别识别它们,因为每种情况都有不同的参数类型。

然后,它显示了这种行为可能存在的一些模糊性:

class C {
    int size() { return 0; }
    static int size(Object arg) { return 0; }

    void test() {
        Fun<C, Integer> f1 = C::size;
          // Error: instance method size() 
          // or static method size(Object)?
    }
}
尚鸿才
2023-03-14

实例方法上有一个隐式参数。JVM规范第3.7节对此进行了定义:

通过首先将对当前实例的引用推送到操作数堆栈来设置调用。然后推送方法调用的参数int值12和13。创建addTwo方法的框架时,传递给该方法的参数成为新框架局部变量的初始值。也就是说,调用程序推送到操作数堆栈上的这两个参数的引用将成为被调用方法的局部变量0、1和2的初始值。

为了理解为什么方法调用是这样进行的,我们需要了解JVM如何将代码存储在内存中。对象的代码和数据是分离的。事实上,一个类(静态和非静态)的所有方法都存储在同一个位置,即方法区域(JVM规范§2.5.4)。这允许只存储每个方法一次,而不是为类的每个实例反复存储它们。当一种方法像

someObject.doSomethingWith(someOtherObject);

被称为,它实际上被编译成更像

doSomething(someObject, someOtherObject);

大多数Java程序员都同意someObject。doSomethingWith(someOtherObject)具有“较低的认知复杂性”:我们使用涉及someOtherObject的someObject做某事。这个动作的中心是someObject,其中someOtherObject只是达到目的的一种手段。

使用do某物与(某物对象,某物对象),您不会传输某物对象作为操作中心的这种语义学。

所以本质上,我们写第一个版本,但计算机更喜欢第二个版本。

正如@Federicoperaltachaffner所指出的那样,自Java 8以来,您甚至可以显式地编写隐式参数。确切的定义在JLS,§8.4.1中给出:

receiver参数是实例方法或内部类的构造函数的可选语法设备。对于实例方法,receiver参数表示调用该方法的对象。对于内部类的构造函数,receiver参数表示新构造对象的直接封闭实例。无论如何,接收器参数的存在只是为了允许在源代码中表示所表示对象的类型,以便可以对类型进行注释。接收器参数不是形式参数;更准确地说,它不是任何类型变量的声明(§4.12.3),它从不绑定到作为方法调用表达式或限定类实例创建表达式中的参数传递的任何值,并且在运行时没有任何影响。

接收器参数必须是类的类型,并且必须命名为this。

这意味着

public String doSomethingWith(SomeOtherClass other) { ... }

public String doSomethingWith(SomeClass this, SomeOtherClass other) { ... }

将具有相同的语义,但后者允许注释等。

 类似资料:
  • 我正在REST API中执行一个方法,您可以通过其ID获取出版物上的注释。CommentServiceImplementation中出现错误,因为它无法解析方法等于(long) 评论服务实施 我首先找到了出版物。然后我找到了评论。最后,如果与发布相关联的注释ID与发布的ID不同,则该注释不属于所述发布。如果是,我会将注释作为DTO返回。据我所知,问题一定是出版物。getId()返回一个长的和注释。

  • 问题内容: 假设有一个典型的Java Bean: 我想在BiConsumer的帮助下创建一种更抽象的调用设置器的方式: 有没有一种方法,以取代拉姆达用链式方法的参考,像或者还是其他什么东西? 问题答案: 不,方法引用不支持链接。在您的示例中,尚不清楚这两种方法中的哪种应接收第二个参数。 但是如果你坚持下去…… … 该方法的命名建议将其视为已存在(此处为),并在其第一个参数之前添加一个函数(此处为)

  • 问题内容: 这是我对Java中的重载解析的了解: 编译器尝试从给定的重载方法定义中解析方法调用的过程称为重载解析。如果编译器找不到确切的匹配项,则仅通过使用向上转换来查找最接近的匹配(永远不会进行向下转换)。 这是一堂课: 如预期的那样,输出为10。 但是,如果我稍微更改类定义并更改第二个重载方法。 输出为8。 在这里我很困惑。如果从不使用向下转换,那么为什么只打印8个?为何编译器会选择以参数为参

  • 我对这个方法引用语法有点困惑。 需要一个双函数,然而,尽管温度较高,lessThanTemp仍然是有效的参数。lessThanTemp()只接受一个参数。 这一行到底发生了什么:? MCVE:

  • 问题内容: 我正在使用Twitter4J。但是我要问的问题比较笼统。我想访问给定推文的用户ID。目前,我有以下两种选择: 我不喜欢第一个选项中的lambda表达式,也不喜欢在第二个选项中被迫调用两个。有没有办法建立方法引用链?我知道这行不通,但是我想知道是否还有其他选择。 问题答案: 不,这是两种方法。其他任何事情最终都只会变得不清楚。 但是,自您提出要求以来,这里有一些选择。 要么

  • 方法引用有助于通过名称指向方法。 使用“::”符号描述方法参考。 方法参考可用于指出以下类型的方法 - 静态方法 实例方法 使用new运算符的构造函数(TreeSet :: new) 方法参考示例 使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。 Java8Tester.java import java.util.List; import java.util.ArrayList