eclipse 引用与泛型不明确

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5361513/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 16:01:11  来源:igfitidea点击:

Reference is ambiguous with generics

javaeclipsegenericsjavacoverloading

提问by Lukas Eder

I'm having quite a tricky case here with generics and method overloading. Check out this example class:

我在这里遇到了一个非常棘手的案例,涉及泛型和方法重载。查看这个示例类:

public class Test {
    public <T> void setValue(Parameter<T> parameter, T value) {
    }

    public <T> void setValue(Parameter<T> parameter, Field<T> value) {
    }

    public void test() {
        // This works perfectly. <T> is bound to String
        // ambiguity between setValue(.., String) and setValue(.., Field)
        // is impossible as String and Field are incompatible
        Parameter<String> p1 = getP1();
        Field<String> f1 = getF1();
        setValue(p1, f1);

        // This causes issues. <T> is bound to Object
        // ambiguity between setValue(.., Object) and setValue(.., Field)
        // is possible as Object and Field are compatible
        Parameter<Object> p2 = getP2();
        Field<Object> f2 = getF2();
        setValue(p2, f2);
    }

    private Parameter<String> getP1() {...}
    private Parameter<Object> getP2() {...}

    private Field<String> getF1() {...}
    private Field<Object> getF2() {...}
}

The above example compiles perfectly in Eclipse (Java 1.6), but not with the Ant javac command (or with the JDK's javac command), where I get this sort of error message on the second invocation of setValue:

上面的示例在 Eclipse (Java 1.6) 中完美编译,但不能使用 Ant javac 命令(或使用 JDK 的 javac 命令),我在第二次调用时收到此类错误消息setValue

reference to setValue is ambiguous, both method setValue(org.jooq.Parameter,T) in Test and method setValue(org.jooq.Parameter,org.jooq.Field) in Test match

对 setValue 的引用不明确,测试中的方法 setValue(org.jooq.Parameter,T) 和测试中的方法 setValue(org.jooq.Parameter,org.jooq.Field) 都匹配

According to the specification and to my understanding of how the Java compiler works, the most specific method should always be chosen: http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#20448

根据规范以及我对 Java 编译器工作方式的理解,应始终选择最具体的方法:http: //java.sun.com/docs/books/jls/third_edition/html/expressions.html#20448

In any case, even if <T>is bound to Object, which makes both setValuemethods acceptable candidates for invocation, the one with the Fieldparameter always seems to be more specific. And it works in Eclipse, just not with the JDK's compiler.

在任何情况下,即使<T>绑定到Object,这使得两种setValue方法都可以成为调用的候选者,但带有Field参数的那个似乎总是更具体。它适用于 Eclipse,只是不适用于 JDK 的编译器。

UPDATE:

更新

Like this, it would work both in Eclipse and with the JDK compiler (with rawtypes warnings, of course). I understand, that the rules specified in the specsare quite special, when generics are involved. But I find this rather confusing:

像这样,它可以在 Eclipse 和 JDK 编译器中工作(当然还有 rawtypes 警告)。我知道,当涉及泛型时,规范中指定的规则非常特殊。但我觉得这很令人困惑:

    public <T> void setValue(Parameter<T> parameter, Object value) {
    }

    // Here, it's easy to see that this method is more specific
    public <T> void setValue(Parameter<T> parameter, Field value) {
    }

UPDATE 2:

更新 2

Even with generics, I can create this workaround where I avoid the type <T>being bound to Objectat setValueinvocation time, by adding an additional, unambiguous indirection called setValue0. This makes me think that the binding of Tto Objectis really what's causing all the trouble here:

即使使用泛型,我也可以创建这种解决方法,通过添加一个额外的、明确的间接调用,我可以避免在调用时<T>绑定类型。这让我认为to的绑定确实是造成所有麻烦的原因:ObjectsetValuesetValue0TObject

    public <T> void setValue(Parameter<T> parameter, T value) {
    }

    public <T> void setValue(Parameter<T> parameter, Field<T> value) {
    }

    public <T> void setValue0(Parameter<T> parameter, Field<T> value) {
        // This call wasn't ambiguous in Java 7
        // It is now ambiguous in Java 8!
        setValue(parameter, value);
    }

    public void test() {
        Parameter<Object> p2 = p2();
        Field<Object> f2 = f2();
        setValue0(p2, f2);
    }

Am I misunderstanding something here? Is there a known compiler bug related to this? Or is there a workaround/compiler setting to help me?

我在这里误解了什么吗?是否存在与此相关的已知编译器错误?或者是否有解决方法/编译器设置可以帮助我?

Follow-Up:

跟进:

For those interested, I have filed a bug report both to Oracle and Eclipse. Oracle has accepted the bug, so far, Eclipse has analysed it and rejected it! It looks as though my intuition is right and this is a bug in javac

对于那些感兴趣的人,我已经向 Oracle 和 Eclipse 提交了一份错误报告。Oracle 已经接受了这个bug,到目前为止,Eclipse 已经对其进行了分析并拒绝了它!看起来好像我的直觉是对的,这是一个错误javac

采纳答案by irreputable

JDK is right. The 2nd method is not more specific than the 1st. From JLS3#15.12.2.5

JDK 是对的。第二种方法并不比第一种方法更具体。来自 JLS3#15.12.2.5

"The informal intuition is that one method is more specific than another if any invocationhandled by the first method could be passed on to the other one without a compile-time type error."

“非正式的直觉是,如果第一个方法处理的任何调用都可以传递给另一个方法而没有编译时类型错误,那么一个方法比另一个方法更具体。”

This is clearly not the case here. I emphasized any invocation. The property of one method being more specific than the other purely depends on the two methods themselves; it doesn't change per invocation.

这显然不是这里的情况。我强调了任何调用。一种方法比另一种方法更具体的特性完全取决于两种方法本身;每次调用都不会改变。

Formal analysis on your problem: is m2 more specific than m1?

对你的问题的正式分析:m2 比 m1 更具体吗?

m1: <R> void setValue(Parameter<R> parameter, R value) 
m2: <V> void setValue(Parameter<V> parameter, Field<V> value) 

First, compiler needs to infer R from the initial constraints:

首先,编译器需要从初始约束中推断出 R:

Parameter<V>   <<   Parameter<R>
Field<V>       <<   R

The result is R=V, per inference rules in 15.12.2.7

结果是R=V,根据 15.12.2.7 中的推理规则

Now we substitute Rand check subtype relations

现在我们替换R并检查子类型关系

Parameter<V>   <:   Parameter<V>
Field<V>       <:   V

The 2nd line does not hold, per subtyping rules in 4.10.2. So m2 is not more specific than m1.

根据 4.10.2 中的子类型规则,第 2 行不成立。所以 m2 并不比 m1 更具体。

Vis not Objectin this analysis; the analysis considers all possible values of V.

V不在Object本分析中;分析考虑 的所有可能值V

I would suggest to use different method names. Overloading is never a necessity.

我建议使用不同的方法名称。重载从来都不是必需的。



This appears to be a significant bug in Eclipse. The spec quite clearly indicates that the type variables are not substituted in this step. Eclipse apparently does type variable substitution first, thencheck method specificity relation.

这似乎是 Eclipse 中的一个重大错误。规范非常清楚地表明在此步骤中未替换类型变量。Eclipse 显然首先进行类型变量替换,然后检查方法特异性关系。

If such behavior is more "sensible" in some examples, it is not in other examples. Say,

如果这种行为在某些示例中更“明智”,那么在其他示例中则不然。说,

m1: <T extends Object> void check(List<T> list, T obj) { print("1"); }
m2: <T extends Number> void check(List<T> list, T num) { print("2"); }

void test()
    check( new ArrayList<Integer>(), new Integer(0) );

"Intuitively", and formally per spec, m2 is more specific than m1, and the test prints "2". However, if substitution T=Integeris done first, the two methods become identical!

“直观地”,正式地根据规范,m2 比 m1 更具体,并且测试打印“2”。但是,如果先进行替换T=Integer,则两种方法变得相同!



for Update 2

对于更新 2

m1: <R> void setValue(Parameter<R> parameter, R value) 
m2: <V> void setValue(Parameter<V> parameter, Field<V> value) 

m3: <T> void setValue2(Parameter<T> parameter, Field<T> value)
s4:             setValue(parameter, value)

Here, m1 is not applicable for method invocation s4, so m2 is the only choice.

这里m1不适用于方法调用s4,所以m2是唯一的选择。

Per 15.12.2.2, to see if m1 is applicable for s4, first, type inference is carried out, to the conclusion that R=T; then we check Ai :< Si, which leads to Field<T> <: T, which is false.

根据15.12.2.2,看m1是否适用于s4,首先进行类型推断,得出R=T的结论;然后我们检查Ai :< Si,这导致Field<T> <: T,这是错误的。

This is consistent with the previous analysis - if m1 is applicable to s4, then any invocation handled by m2 (essentially same as s4) can be handled by m1, which means m2 would be more specific than m1, which is false.

这与前面的分析是一致的——如果m1适用于s4,那么任何由m2处理的调用(本质上与s4相同)都可以由m1处理,这意味着m2会比m1更具体,这是错误的。

in a parameterized type

在参数化类型中

Consider the following code

考虑以下代码

class PF<T>
{
    public void setValue(Parameter<T> parameter, T value) {
    }

    public void setValue(Parameter<T> parameter, Field<T> value) {
    }
}

void test()

    PF<Object> pf2 = null;

    Parameter<Object> p2 = getP2();
    Field<Object> f2 = getF2();

    pf2.setValue(p2,f2);

This compiles without problem. Per 4.5.2, the types of the methods in PF<Object>are methods in PF<T>with substitution T=Object. That is, the methods of pf2are

这编译没有问题。根据 4.5.2,方法的类型PF<Object>是方法 in PF<T>with替换T=Object。也就是说,方法pf2

    public void setValue(Parameter<Object> parameter, Object value) 

    public void setValue(Parameter<Object> parameter, Field<Object> value) 

The 2nd method is more specific than the 1st.

第二种方法比第一种方法更具体。

回答by Buhake Sindi

My guess is that the compiler is doing an method overloading resolution as per JLS, Section 15.12.2.5.

我的猜测是编译器正在按照JLS 第 15.12.2.5 节进行方法重载解析。

For this Section, the compiler uses strong subtyping(thus not allowing any unchecked conversion), so, T valuebecomes Object valueand Field<T> valuebecomes Field<Object> value. The following rules will apply:

对于本节,编译器使用强子类型化(因此不允许任何未经检查的转换),因此,T value变成Object valueField<T> value变成Field<Object> value。以下规则将适用:

The method mis applicable by subtyping if and only if both of the following conditions hold:

* For 1in, either:
      o Ai is a subtype (§4.10) of Si (Ai <: Si) or
      o Ai is convertible to some type *Ci* by unchecked conversion

(§5.1.9), and Ci <: Si. * If m is a generic method as described above then Ul <: Bl[R1 = U1, ..., Rp = Up], 1lp.

当且仅当以下两个条件都成立时,方法m通过子类型化适用:

* For 1in, either:
      o Ai is a subtype (§4.10) of Si (Ai <: Si) or
      o Ai is convertible to some type *Ci* by unchecked conversion

(§5.1.9) 和 Ci <: Si。* 如果 m 是上述通用方法,则 Ul <: Bl[R1 = U1, ..., Rp = Up], 1lp。

(Refer to bullet 2). Since Field<Object>is a subtype of Objectthen the most specific method is found. Field f2matches both methods of yours (because of bullet 2 above) and makes it ambiguous.

(请参阅项目符号 2)。因为Field<Object>是一个子类型,Object所以找到了最具体的方法。Fieldf2匹配您的两种方法(因为上面的第 2 条)并使其含糊不清。

For Stringand Field<String>, there is no subtype relationship between the two.

对于Stringand Field<String>,两者之间没有子类型关系。

PS. This is my understanding of things, don't quote it as kosher.

附注。这是我对事物的理解,不要将其引用为犹太洁食。

回答by Georgy Bolyuba

Edit: This answer is wrong. Take a look at accepted answer.

编辑:这个答案是错误的。看看接受的答案。

I think the issue comes down this: compiler does not see the type of f2 (i.e. Field) and the inferred type of formal parameter (i.e. Field -> Field) as the same type.

我认为问题归结为:编译器没有将 f2 的类型(即 Field)和形式参数的推断类型(即 Field -> Field)视为相同的类型。

In other words, it looks like type of f2 (Field) is considered to be a subtype of the type of formal parameter Field (Field). Since Field is at the same type a subtype of Object, compiler cannot pick one method over another.

换句话说,看起来 f2 的类型 (Field) 被认为是形式参数 Field (Field) 类型的子类型。由于 Field 与 Object 的子类型属于同一类型,因此编译器无法选择一种方法而不是另一种方法。

Edit: Let me expand my statement a bit

编辑:让我稍微扩展一下我的陈述

Both methods are applicableand it looks like the Phase 1: Identify Matching Arity Methods Applicable by Subtypingis used to decide which method to call and than rules from Choosing the Most Specific Methodapplied, but failed for some reason to pick second method over first one.

这两种方法都适用,看起来像第 1 阶段:通过子类型确定匹配的Arity 方法适用于决定调用哪个方法,而不是选择应用的最具体方法中的规则,但由于某种原因未能选择第二种方法而不是第一种方法.

Phase 1section uses this notation: X <: S(X is subtype of S). Based on my understanding of <:, X <: Xis a valid expression, i.e. the <:is not strict and includes the type itself (X is subtype of X) in this context. This explains the result of Phase 1: both methods are picked as candidates, since Field<Object> <: Objectand Field<Object> <: Field<Object>.

阶段 1部分使用此符号:X <: S(X 是 S 的子类型)。根据我对<:, 的理解,X <: X是一个有效的表达式,即 the<:不是严格的并且在此上下文中包括类型本身(X 是 X 的子类型)。这解释了阶段 1 的结果:两种方法都被选为候选方法,因为Field<Object> <: ObjectField<Object> <: Field<Object>

Choosing the Most Specific Methodsection uses same notation to say that one method is more specific than another. The interesting part the paragraph that starts with "One fixed-arity member method named m is more specific than another member...". It has, among other things:

选择最具体的方法部分使用相同的符号表示一种方法比另一种方法更具体。有趣的部分是以“一个名为 m 的固定数量成员方法比另一个成员更具体......”开头的段落。除其他外,它具有:

For all j from 1 to n, Tj <: Sj.

对于从 1 到 n 的所有 j,Tj <: Sj。

This makes me think that in our case second method mustbe chosen over the first one, because following holds:

这让我认为在我们的情况下必须选择第二种方法而不是第一种方法,因为以下内容成立:

  • Parameter<Object> <: Parameter<Object>
  • Field<Object> <: Object
  • Parameter<Object> <: Parameter<Object>
  • Field<Object> <: Object

while the other way around does not hold due to Object <: Field<Object>being false (Object is not a subtype of Field).

而相反的方式由于为Object <: Field<Object>假而不成立(Object 不是 Field 的子类型)。

Note: In case of String examples, Phase 1 will simply pick the only method applicable: the second one.

注意:对于字符串示例,阶段 1 将简单地选择唯一适用的方法:第二个。

So, to answer your questions: I think this is a bug in compiler implementation. Eclipse has it is own incremental compiler which does not have this bug it seems.

所以,回答你的问题:我认为这是编译器实现中的一个错误。Eclipse 有它自己的增量编译器,它似乎没有这个错误。