2017-05-24 2 views
2

Vu le codewildcards Java par défaut à java.lang.Object au lieu de la limite supérieure

abstract class Base<Thistype extends Base> 
{ 
    public void addOnSomethingListener(Consumer<? super Thistype> action) 
    {} 

    public void foo() 
    { 
     System.out.println("foo"); 
    } 
} 

class Simple<Thistype extends Simple> extends Base<Thistype> 
{ 

} 

class Test1 extends Simple<Test1> 
{ 

} 

class Test2 extends Simple 
{ 

} 

class Test 
{ 
    public static void main(String[] args) 
    { 
     Test1 test1 = new Test1(); 
     Test2 test2 = new Test2(); 

     test1.addOnSomethingListener(test -> 
     { 
      test.foo(); // VALID as "test" is of type "Thistype" which is "Test1". 
     }); 

     test2.addOnSomethingListener(test -> 
     { 
      test.foo(); // INVALID as "test" is of type "Thistype" which is "java.lang.Object" instead of "Base" which is the upper bound. 
     }); 
    } 
} 

Pourquoi le type générique de la classe Test2 pas par défaut à la classe Base mais plutôt par défaut à la classe java.lang.Object?

J'ai fourni la limite supérieure, mais il semble que ce soit sans importance si les caractères génériques sont utilisés ou quand aucun générique n'est donné du tout.

Le code dans la fonction principale devrait pouvoir être compilé si vous me le demandez.

Répondre

4

En déclarant Test2 classe sans specyfing son argument de type:

class Test2 extends Simple {} 

vous créez un raw type. Par conséquent le compilateur le traite comme une instance de Object.

+0

Ok, mais existe-t-il un moyen de le faire par défaut à la limite supérieure ... comme si je ne me trompe pas, il ne peut jamais être autre chose.Cela résoudrait mon problème aussi – Wietlol

+0

Vous pouvez simplement ajouter une autre classe: 'class UpperBounded étend Simple {} 'et déclare' Test2' en l'utilisant comme argument de type: 'class Test2 extends Simple {}'. – syntagma

+0

Oui, mais cela n'a aucune différence par rapport à la classe Test1. – Wietlol

0

Il ne "par défaut pas Object". Ce qui s'est réellement passé est qu'en utilisant un type brut (Test2 étend le type brut Simple), il "désactive" tous les génériques pour ce type. Ainsi, la méthode addOnSomethingListener de Test2 (qui est héritée du type brut Simple) a effectivement la signature void addOnSomethingListener(Consumer action) (c'est-à-dire que le type du paramètre est effacé au type brut Consumer).

Il se trouve que la méthode de accept de type Consumer brut a la signature void accept(Object) (parce qu'il est normalement void accept(T), mais le T dans Consumer est sans bornes, de sorte que lors de l'utilisation du type brut Consumer, il est effacé à void accept(Object).