Eclipse ECJ accepts this code, javac doesn't - who is right?

 ̄綄美尐妖づ 提交于 2019-12-23 10:09:36

问题


Consider the following returnsNull function and a call to it with a generic type:

public static <T> List<T> returnNull(Class<? extends T> clazz) {
    return null;
}

public static void main( String[] args )
{
    List<AtomicReference<?>> l = returnNull(AtomicReference.class);
}

The Eclipse compiler, when set to Java 8, accepts it, but javac in Java 8 rejects it with:

incompatible types: cannot infer type-variable(s) T
    (argument mismatch; java.lang.Class<java.util.concurrent.atomic.AtomicReference> cannot be converted to java.lang.Class<? extends java.util.concurrent.atomic.AtomicReference<?>>)

The underlying difference seems to be that given a two parameterized types P1<T> and P2<T>, Eclipse allows conversion from the outer type parameterized with the raw inner type: P1<P2> to the outer type parameterized with a lower bound of the of the inner-type with an unbounded wildcard like P1<? extends P2<?>>. javac doesn't.

This isn't just a theoretical musing: if this code was accepted it would solve my generics filtering problem.

Who is right?


回答1:


During applicability inference ECJ infers <T> to AtomicReference#RAW, which let's the signature of returnNull appear as

List<AtomicReference#RAW> returnNull(Class<? extends AtomicReference#RAW>)

The exact steps being:

  • initial constraint:
    • ⟨Class<AtomicReference#RAW> → Class<? extends T#0>⟩
  • reduced to:
    • ⟨Class<AtomicReference#RAW> <: Class<? extends T#0>⟩
    • ⟨AtomicReference#RAW <= ? extends T#0⟩
    • ⟨AtomicReference#RAW <: T#0⟩
    • AtomicReference#RAW <: T#0
  • resolved to:
    • T#0 = AtomicReference#RAW

Now, there's no problem passing a value of type Class<AtomicReference#RAW> into that method.

(the suffix #RAW is an implementation specific denotation for raw types, reproduced here for clarity).

EDIT: Things look different during invocation type inference: By adding the target type into the mix we end up (during incorporation) with the following constraint:

  • ⟨AtomicReference#RAW <: AtomicReference<?>⟩

This constraint should reduce to FALSE, but ecj reduces to TRUE. From this, rejecting the program seems to be the correct answer.

I filed bug 528970 for further investigation in ECJ.

There is some irony in this, because javac has a long standing bug whereby it wrongly assumes T#RAW <: T<X>. For compatibility reasons, ECJ explicitly copied this bug into many locations, but apparently in one particular code location it's the opposite: javac applies subtyping where ECJ checks for compatibility.

EDIT 2: One year later it seems that the bug in ECJ can only be fixed after JLS has been improved in and around JDK-8054721.



来源:https://stackoverflow.com/questions/47662493/eclipse-ecj-accepts-this-code-javac-doesnt-who-is-right

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!