当前位置: 首页 > 面试题库 >

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

颛孙智勇
2023-03-14
问题内容

我的课叫做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班的测试班

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));

编译成功。

我无法理解为什么会出现这种编译错误。

我遇到了follwong链接装箱和拆箱,多个泛型类型和交集类型以及此eclipse编译器错误,但我仍然与所提到的原因无关。

我的问题是,verify当映射器String::valueOf传递给get方法时,是什么使编译器认为该方法的两个签名都匹配?

对于编译,使用以下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

  • 隐式类型的lambda表达式1。
  • 不精确的方法参考表达式2。
  • […]

因此:

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

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

相比之下,这是一些有效的示例,因为类型是明确指定的:

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

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

1-隐式类型的lambda表达式是lambda表达式,其中推断了其所有形式参数的类型。
2-不精确的方法参考-具有多个重载的参考。



 类似资料:
  • 我有一个采访问题-C#,是否可以在一个类中实现,从接口继承有两个具有相同名称和相同签名的方法?

  • 我改变了切入点的顺序,它总是排在第二位。关于如何解决这个问题有什么想法吗? 更新 一旦我发布了这个问题,我就有了一个想法。我这样更改了切入点: 现在异常消失了,但仍然有一个小问题(我想这个问题更容易解决):因为ArrayList实现了可序列化,所以至少在我使用ArrayList的测试用例中,两个切入点都被执行了。 我将对此进行研究,并发布我的发现,但也感谢您的帮助;) 我将代码改为只使用一个切入点

  • 问题内容: 假设我必须实现在两个不同的包中声明的两个不同的接口(在两个不同的独立项目中)。 我有包装 并包装 在我的包里 如何处理这种情况? 问题答案: 正如常见问题解答所提到的 其他语言的经验告诉我们,使用具有相同名称但签名不同的多种方法有时会很有用,但在实践中也可能会造成混淆和脆弱。 在Go的类型系统中,仅按名称进行匹配并要求类型一致是简化的主要决定 。 在您的情况下,您将满足两个接口。 您可

  • 我在一次面试中被问到以下问题: 问题:名称和签名相同但返回类型不同的方法。他问我,可能吗?这种类型叫什么。 有人能告诉我以下情况吗: > 上面的事情在任何情况下都是可能的(至少像一个在基类中,一个在派生类中?)如果是,是什么类型?比如编译或运行时多态? 在编译时多态性中,如果方法的返回类型与签名也不同,该怎么办?但只有函数的名称是相同的。还是编译时多态性吗? 在重写中,如果我有不同的返回类型,但方

  • 问题内容: 我有两个Java接口和一个实现类。 (我已经使用Eclipse直接运行程序,并且我没有尝试通过从命令行进行显式编译来检查任何编译器警告等)。 为什么它们运行没有问题?为什么Java允许这样做,即使它满足两个接口的“合同”,却在实现类时造成歧义? 更新了示例。 问题答案: Java语言规范的第8.1.5节专门允许这种情况: 类中的单个方法声明可以实现多个超级接口的方法。例如,在代码中:

  • 我有一个场景,其中有两个具有相同方法名称的不同标记。但它在Swagger编辑器中抛出了一个语法错误,称为“重复的映射键”。 在OpenAPI中编写此内容的正确方法是什么?