2012-11-03 3 views
5

Désolé pour le titre vague. J'ai ce morceau de code qui compile sur Eclipse Juno (4.2), mais pas javac (1.7.0_09):Java CRTP et Wildcards: le code compile dans Eclipse mais pas `javac`

package test; 

public final class Test { 
    public static class N<T extends N<T>> {} 

    public static class R<T extends N<T>> { 
     public T o; 
    } 

    public <T extends N<T>> void p(final T n) {} 

    public void v(final R<?> r) { 
     p(r.o);  // <-- javac fails on this line 
    } 
} 

L'erreur est:

 
Test.java:13: error: method p in class Test cannot be applied to given types; 
     p(r.o); 
     ^
    required: T 
    found: N<CAP#1> 
    reason: inferred type does not conform to declared bound(s) 
    inferred: N<CAP#1> 
    bound(s): N<N<CAP#1>> 
    where T is a type-variable: 
    T extends N<T> declared in method <T>p(T) 
    where CAP#1 is a fresh type-variable: 
    CAP#1 extends N<CAP#1> from capture of ? 
1 error 

Ainsi, les questions sont les suivantes:

  1. Est-ce un bug javac ou un bug Eclipse?

  2. Y a-t-il un moyen de faire cette compilation sur javac, sans changer la signature de la méthode v (à savoir garder le caractère générique)?

    Je sais que le changer en <T extends N<T>> void v(final R<T> r) le fait compiler, mais j'aimerais savoir s'il y a moyen d'éviter cela en premier. En outre, la méthode p ne peut pas être modifiée en <T extends N<?>> void p(final T n) car le contenu a des types qui nécessitent la contrainte exacte T extends N<T>.

+0

@Nambari: Il est également 1,7. De plus, Eclipse n'utilise pas javac pour compiler le code, mais son propre compilateur. – kennytm

+0

Il compile dans mon éclipse avec java 6 quelle version java utilisez-vous? –

+0

@AmitD: Oui cela fonctionne sur Eclipse à la fois sur 1.6 et 1.7, mais il ne compilera pas avec javac de OpenJDK 6 et 7, ni avec [JDK 6 de Sun sur ideone] (http://ideone.com/Z03W7V). – kennytm

Répondre

6

jokers sont limités en ce qu'ils rompent les expressions récursives comme T extends X<T> que les paramètres de type permettent. Nous savons ce que vous essayez de faire est en sécurité basé sur les points suivants:

  1. r.o est de type T (déclarée par R), qui est ou s'étend N<T>.
  2. La méthode p prend un argument de type T (déclaré par p), qui est également ou s'étend N<T>.
  3. Donc même si r est tapé R<?>, un appel p(r.o) devrait théoriquement être légal.

C'est peut-être le raisonnement du compilateur Eclipse (connu pour faire des allocations correctes pour certaines nuances de génériques où javac doesn't).

En supposant que vous voulez compiler avec javac et ne peut pas changer la signature de v comme vous l'avez mentionné, le mieux que vous pouvez faire est de recourir à l'utilisation d'un type brut, qui « se retire » de contrôle de type générique:

public void v(final R<?> r) { 
    //necessary to placate javac - this is okay because [insert above reasoning] 
    @SuppressWarnings("rawtypes") 
    N nRaw = r.o; 
    p(nRaw); 
} 
Questions connexes