Java编译器:两个具有相同名称和不同签名的方法如何匹配方法调用?

2023-11-21

我这门课叫Container:

public class Container {

    private final Map<String, Object> map = new HashMap<>();

    public void put(String name, Object value) {
        map.put(name, value);
    }

    public Container with(String name, Object value) {
        put(name, value);
        return this;
    }

    public Object get(String name) {
        return map.get(name);
    }

    public <R> R get(String name, Function<Object, R> mapper) {

        Object value = get(name);

        if (null == value) {
            return null;
        }

        return mapper
            .apply(value);
    }

    public <R> R get(String name, Class<R> type) {

        Object value = get(name);

        if (null == value) {
            return null;
        }

        if (type.isAssignableFrom(value.getClass())) {
            return type
                .cast(value);
        }

        throw new ClassCastException(String
            .format("%s -> %s", value.getClass(), type));
    }
}

班级叫Token:

public class Token {

    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public Token withValue(String value) {
        setValue(value);
        return this;
    }
}

最后是一个测试类Token class

public class TokenTest {

    @Test
    public void verifyToken() {
        verify("bar", new Token()
            .withValue("bar"));
    }

    @Test
    public void verifyContainer() {
        Container tokens = new Container()
            .with("foo", "bar")
            .with("baz", "bat");

        verify("bar", tokens.get("foo", String.class));
        verify("bat", tokens.get("baz", String::valueOf));  // line 21
    }

    private void verify(String expected, String actual) {
        verify(expected, new Token()
            .withValue(actual));
    }

    private void verify(String expected, Token actual) {
        Assert
            .assertEquals(expected, actual.getValue());
    }
}

测试在 eclipse 中编译并运行得很好。

在命令行上构建时

mvn clean test

出现编译错误:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.0:testCompile (default-testCompile) on project ambiguous: Compilation failure
[ERROR] /C:/data/projects/java/ambiguous/src/test/java/ambiguous/TokenTest.java:[21,9] reference to verify is ambiguous
[ERROR]   both method verify(java.lang.String,java.lang.String) in ambiguous.TokenTest and method verify(java.lang.String,ambiguous.Token) in ambiguous.TokenTest match

当我换行时编译也失败21到其中之一

verify("bat", tokens.get("baz", e -> String.valueOf(e)));
verify("bat", tokens.get("baz", e -> e.toString));

当我将线路更改为其中之一时

verify("bat", tokens.get("baz", String.class));
verify("bat", tokens.get("baz", Object::toString));

编译成功。

我不明白为什么会出现这个编译错误。

我发现了以下链接装箱和拆箱, 多种泛型类型和交集类型和这个Eclipse 编译器错误但我仍然无法理解上述原因。

我的问题是,是什么让编译器认为verify映射器时方法匹配String::valueOf被传递到get method?

使用以下 jdk 进行编译(使用 maven 和 gradle):

$ java -version
openjdk version "1.8.0_201-1-ojdkbuild"
OpenJDK Runtime Environment (build 1.8.0_201-1-ojdkbuild-b09)
OpenJDK 64-Bit Server VM (build 25.201-b09, mixed mode)

根据JLS §15.12.2.2:

考虑参数表达式与适用性相关为一个 潜在适用的方法m除非有下列情况之一 形式:

  • An implicitly typed lambda expression1.
  • An inexact method reference expression2.
  • [...]

所以:

verify("bar", tokens.get("foo", e -> String.valueOf(e)));

隐式类型的 lambda 表达式e -> String.valueOf(e)在重载解析期间跳过适用性检查 - 两者verify(...)方法变得适用 - 因此存在歧义。

相比之下,以下是一些可行的示例,因为类型是明确指定的:

verify("bar", tokens.get("foo", (Function<Object, String>) e -> String.valueOf(e)));

verify("bar", tokens.get("foo", (Function<Object, String>) String::valueOf));

1 - An implicitly typed lambda expression is a lambda expression, where the types of all its formal parameters are inferred.
2 - An inexact method reference - one with multiple overloads.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java编译器:两个具有相同名称和不同签名的方法如何匹配方法调用? 的相关文章

随机推荐