引用是泛型的 [英] Reference is ambiguous with generics

查看:169
本文介绍了引用是泛型的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在这里有一个非常棘手的例子,就是泛型和方法重载。查看这个示例类:

  public class Test {
public< T> void setValue(参数< T>参数,T值){
}

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

public void test(){
//这样做完美。 < T>绑定到String
// setValue(..,String)和setValue(..,Field)之间的模糊度
//不可能,因为String和Field不兼容
参数< String> p1 = getP1();
字段< String> f1 = getF1();
setValue(p1,f1);

//这会导致问题。 < T>绑定到Object
// setValue(..,Object)和setValue(..,Field)之间的模糊度
//可以作为对象和字段兼容
参数< Object> p2 = getP2();
字段<对象> f2 = getF2();
setValue(p2,f2);
}

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

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

上面的例子在Eclipse Java 1.6),但不是使用Ant javac命令(或使用JDK的javac命令),在第二次调用 setValue 时,我会收到此类错误消息:


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


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



无论如何,即使 绑定到 Object ,其中使 setValue 方法可接受的调用候选者,具有 Field 参数的方法总是似乎更具体。而且它在Eclipse中工作,只是不用JDK的编译器。



更新



像这样,它将在Eclipse和JDK编译器(当然是rawtypes警告)中工作。据了解,规范中指定的规则当涉及泛型时,它们是非常特别的。但是我觉得这很混乱:

  public< T> void setValue(参数< T>参数,Object value){
}

//这里很容易看到这种方法更为具体
public< T> void setValue(参数< T>参数,字段值){
}

更新2



即使是泛型,我可以创建此解决方法,避免类型< T> 被绑定到 Object setValue 调用时间,通过添加一个额外的,明确的间接称为 setValue0 。这使我认为, T Object 的绑定真的是造成所有麻烦的原因:

  public< T> void setValue(参数< T>参数,T值){
}

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

public< T> void setValue0(Parameter< T> parameter,Field< T> value){
// Java 7中这个调用并不模糊
// Java 8中现在含糊不清了!
setValue(parameter,value);
}

public void test(){
参数< Object> p2 = p2();
字段<对象> f2 = f2();
setValue0(p2,f2);
}

我误会了吗?是否有与之相关的已知编译器错误?还是有一个解决方法/编译器设置来帮助我?



后续:



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




解决方案

JDK是对的。第二种方法并不比第1种方法更具体。从JLS3#15.12.2.5



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



这显然不是这样。我强调任何调用。一种比另一种方法更具体的属性纯粹取决于两种方法本身;



正确分析您的问题:m2比m1更具体?

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

首先,编译器需要推断来自初始约束的R:

 参数< V> <<参数< R> 
Field< V> << R

结果是 R = V 每个推理规则在15.12.2.7中



现在我们用 R 替换子类型关系

 参数< V> < ;:参数< V> 
Field< V> <:V

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



V 不是 Object 在这个分析;该分析考虑了所有可能的值 V



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






这在Eclipse中似乎是一个重大错误。规格相当清楚地表明在这一步中类型变量没有被替代。 Eclipse显然首先进行类型变量替换,然后检查方法的特异性关系。



如果这种行为在一些例子中更明智,那么这不是其他例子。说,

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

直观地,正式地按照规格,m2比m1更具体,测试打印 2\" 。但是,如果替换 T = Integer 首先完成,这两种方法变得相同!






更新2

  m1:< R> void setValue(参数R>参数,R值)
m2:< V> void setValue(参数< V>参数,Field< V>值)

m3:< T> void setValue2(参数< T>参数,字段>值)
s4:setValue(参数,值)

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



每15.12.2.2,查看是否适用m1对于s4,首先进行类型推断,得出R = T的结论;那么我们检查 Ai:< Si ,这导致 Field< T>



与以前的分析一致 - 如果m1适用于s4,则处理任何调用m1(与s4基本相同)可以由m1处理,这意味着m2将比m1更具体,这是m1,这是假的。



参数化类型



考虑以下代码

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

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

void test()

PF< Object> pf2 = null;

参数< Object> p2 = getP2();
字段<对象> f2 = getF2();

pf2.setValue(p2,f2);

这个编译没有问题。根据4.5.2, PF< Object> 中的方法的类型是 PF 中的方法 T =对象。也就是说, pf2 的方法是

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

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

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


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:

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

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

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

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 specs are quite special, when generics are involved. But I find this rather confusing:

    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:

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

    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

解决方案

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

"The informal intuition is that one method is more specific than another if any invocation handled 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?

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:

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

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

Now we substitute R and check subtype relations

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.

V is not Object in this analysis; the analysis considers all possible values of 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, then check method specificity relation.

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=Integer is done first, the two methods become identical!


for Update 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.

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.

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.

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 pf2 are

    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.

这篇关于引用是泛型的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆