2016-08-10 2 views
14

Disons que nous avons quelques interfaces de test/classes comme ceci:Pourquoi une méthode générique d'une interface peut-elle être implémentée comme non générique en Java?

abstract class Plant { 
    public abstract String getName(); 
} 

interface Eatable { } 

class Apple extends Plant implements Eatable { 
    @Override 
    public String getName() { 
     return "Apple"; 
    } 
} 

class Rose extends Plant { 
    @Override 
    public String getName() { 
     return "Rose"; 
    } 
} 

interface Animal { 
    <T extends Plant & Eatable> void eat(T plant); 
} 

Vous pouvez voir Animal.eat est une méthode générique avec des contraintes. Maintenant, j'ai ma classe Human comme ceci:

class Human implements Animal { 
    @Override 
    public void eat(Plant plant) { 
    } 
} 

qui compile très bien. Vous pouvez voir Human.eat est moins contrainte que Animal.eat parce que l'interface Eatable est perdue.

Q1: Pourquoi le compilateur ne se plaint-il pas de cette incohérence?

Q2: Si Plant&Eatablerévisions à la baisse-Plant est acceptable pour le compilateur, pourquoi il se plaint sur eat(Object plant)?

+2

Quelle version de Java utilisez-vous? L'utilisation de Java 1.8 dans Eclipse provoque une erreur de compilation comme prévu. – Codebender

+0

@Codebender Java8 dans Intellij IDEA. –

+2

@Codebender Je n'ai aucun problème à compiler ceci. https://ideone.com/7xUcZn – flakes

Répondre

12

Lesson: Generics by Gilad Bracha selon lui

public static <T extends Object & Comparable<? super T>> T max(Collection<T> coll) 

Ceci est un exemple de donner des bornes multiples pour un paramètre de type, en utilisant la syntaxe & T1 T2 ... Tn &. Une variable de type avec plusieurs limites est connue pour être un sous-type de tous les types listés dans la limite . Lorsqu'un lien multiple est utilisé, le premier type mentionné dans la limite est utilisé comme l'effacement de la variable de type.

de sorte que votre exemple <T extends Plant & Eatable> void eat(T plant); seront effacées à void eat(Plant plant); alors quand vous le remplacez le compilateur ne se plaint pas

0

Ahmed réponse est juste, en passant, si vous voulez mettre la contrainte la mise en œuvre du interface d'animaux, vous devez le déclarer comme ceci:

interface Animal<T extends Plant & Eatable> { 
    void eat(T plant); 
} 

Ensuite, si vous implémente l'interface animal sans fournir les informations de type, le compilateur en utilisant le moins politique surprise déduire le T comme un type de plante. Mais si vous fournissez les informations de type nécessaires, le compilateur fonctionne correctement.

class Human implements Animal<Rose> // won't compile 
class Human implements Animal<Apple> // compile