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

一元静态方法的方法引用在函数和双函数参数类型之间不明确

雷曜灿
2023-03-14

考虑以下简化的测试用例:

import java.util.AbstractList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
public final class Example {
    static class PairList<A, B> {
        public void replaceAllSecond(Function<? super B, ? extends B> secondFunction) {}
        public void replaceAllSecond(BiFunction<? super A, ? super B, ? extends B> secondFunction) {}
    }

    static class ImmutableList<E> extends AbstractList<E> {
        public static <E> ImmutableList<E> copyOf(Iterable<? extends E> elements) {return null;}
        public static <E> ImmutableList<E> copyOf(Collection<? extends E> elements) {return null;}
        public static <E> ImmutableList<E> copyOf(Iterator<? extends E> elements) {return null;}
        public static <E> ImmutableList<E> copyOf(E[] elements) {return null;}

        @Override public E get(int index) {return null;}
        @Override public int size() {return 0;}
    }

    public static void foo() {
        PairList<Integer, List<Integer>> list = new PairList<>();
        list.replaceAllSecond(x -> ImmutableList.copyOf(x)); //accepted
        list.replaceAllSecond(ImmutableList::copyOf); //error
    }
}

使用来自Oracle JDK 8u40的javac编译时,可以使用lambda调用replaceAllSecond,但传递方法引用的调用被拒绝,错误如下:

Example.java:26: error: reference to replaceAllSecond is ambiguous
                list.replaceAllSecond(ImmutableList::copyOf); //error
                    ^
  both method replaceAllSecond(Function<? super B,? extends B>) in PairList and method replaceAllSecond(BiFunction<? super A,? super B,? extends B>) in PairList match
  where B,A are type-variables:
    B extends Object declared in class PairList
    A extends Object declared in class PairList
1 error

我不明白为什么重载BiFunction在这里可能适用。来自JLS15.12.2.1(省略了一些项目符号):

成员方法可能适用于方法调用当且仅当以下所有为真:

  • 如果成员是一个固定的属性方法,属性为n,则方法调用的属性等于n,并且对于所有i(1≤i≤n),方法调用的第i个参数可能与方法的第i个参数的类型兼容,如下所述。

根据以下规则,表达式可能与目标类型兼容:

>

  • 方法引用表达式的形式为[TypeArguments]标识符e:: [TypeArguments],并且至少有一个可能适用的方法是i)静态的,并且支持属性n,或者ii)非静态的,并且支持属性n-1。

按照我的解释,BiFunction的函数类型arity是2,但是copyOf的所有重载都是静态的,并且具有arity 1,因此方法引用可能与BiFunction参数不兼容,因此replaceAllSecond(BiFunction)可能不适用。

我是误解了JLS,还是这是一个javac错误?JDK-8026231描述了更新javac以实现该规范,但该错误在2013年Java 8首次发布(2014年3月)之前得到解决。

共有1个答案

公羊嘉
2023-03-14

您的示例可以进一步简化为以下内容:

import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;

public final class Example {
    static class PairList<A, B> {
        public void replaceAllSecond(Function<? super B, ? extends B> secondFunction) {}
        public void replaceAllSecond(BiFunction<? super A, ? super B, ? extends B> secondFunction) {}
    }

    public static <E> List<E> copyOf(Iterable<? extends E> elements) {return null;}
    public static <E> List<E> copyOf(Collection<? extends E> elements) {return null;}

    public static void foo() {
        PairList<Integer, List<Integer>> list = new PairList<>();
        list.replaceAllSecond(x -> Example.copyOf(x)); //accepted
        list.replaceAllSecond(Example::copyOf); //error
    }
}

我想这是一个javac问题,因为这段代码可以编译Java-9早期访问版本(甚至像9ea57这样的非常旧的版本),而在Java-8版本(即使是最新的更新)中失败。

 类似资料:
  • Kotlin中的参数与Java中有些不同。如你所见,我们先写参数的名字再写它的类型: fun add(x: Int, y: Int) : Int { return x + y } 我们可以给参数指定一个默认值使得它们变得可选,这是非常有帮助的。这里有一个例子,在Activity中创建了一个函数用来toast一段信息: fun toast(message: String, length: I

  • 问题内容: 什么时候应该使用构造函数,什么时候应该使用静态方法? 您能用小片段解释一下吗?我略读了一些线程,但是我仍然不清楚。 问题答案: 当您只想返回该类型的新对象并且希望简单时,请使用公共构造函数。 一个很好的例子是StringBuilder,因为它是可变的,您可能每次都想要一个新对象。 当您可能想重用对象(尤其是不可变的对象),返回子类或描述结构时,请使用静态因子方法。一个很好的例子是Enu

  • 现在的问题是,我是否可以创建这些函数一次,并像助手方法一样重用它们。 一个威胁是线程安全。我用一个简单的测试来检查这个JUnit测试: 我用的是VMLENS。我可以通过更改变量来优化测试,这样可以检查随机性。目的是查看这些使用相同函数的多个线程是否因为并发访问而混淆了它们的输入。测试没有返回任何阴性结果。请对测试是否符合目标做出评论。 上面使用的结果是否也可以应用于和? 对函数和字节码的更多熟悉可

  • 本文向大家介绍python类和函数中使用静态变量的方法,包括了python类和函数中使用静态变量的方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了python类和函数中使用静态变量的方法。分享给大家供大家参考。具体分析如下: 在python的类和函数(包括λ方法)中使用静态变量似乎是件不可能[Nothing is impossible]的事, 但总有解决的办法,下面通过实现一个类或函数

  • 这行不通,因为premiumStrings::contains可以接受任何对象,而不仅仅是字符串。可以将其替换为<代码>(字符串s)- (具体来说,问题是

  • 问题内容: 用修饰的功能和用修饰的功能有什么区别? 问题答案: 也许有点示例代码将有助于:发现其中的差别在调用签名,并且: 以下是对象实例调用方法的常用方法。对象实例,a作为第一个参数隐式传递。 使用时,对象实例的类作为第一个参数而不是隐式传递。 你也可以使用该类进行呼叫。实际上,如果你将某些东西定义为类方法,则可能是因为你打算从类而不是从类实例调用它。本来会引发,但效果很好: 人们发现类方法的一