2009-04-22 7 views
2

Disons que j'ai cette classe de base:Pourquoi les classes dérivées n'ont-elles parfois que des types spécialisés dans les méthodes surchargées?

abstract public class Base { 
    abstract public Map save(); 
    abstract public void load(Map data); 
} 

à ma grande surprise que je pouvais faire cela dans une classe dérivée:

public class Derived extends Base { 
    @Override 
    public Map<String, String> save() { //Works 
     ... 
    } 
    ... 
}  

mais je ne pouvais pas le faire:

public class Derived extends Base { 
    @Override 
    public void load(Map<String, String> data) { // Fails 
     ... 
    } 
    ... 
}  

Que se passe-t-il ici? Pourquoi puis-je utiliser un type de retour spécialisé, mais pas un type de paramètre spécialisé?

Ce qui est encore plus déroutant est que si je continue la déclaration initiale de load, je peux attribuer au type plus spécial:

public class Derived extends Base { 
    @Override 
    public void load(Map data) { 
     Map<String, String> myData = data; // Works without further casting 
     ... 
    } 
    ... 
}  

Répondre

11

Il y a une conversion implicite du type spécialisé au type brut - c'est toujours "sûr" car quelqu'un utilisant le type brut ne peut faire aucune supposition. Donc, quelqu'un qui s'attend à obtenir un Map retour d'une méthode ne dérange pas si elles obtiennent un Map<String, String>.

Il n'est pas une conversion implicite de type brut au type spécialisé - si quelqu'un passe une Map première en load il peut avoir des clés non-chaîne et les valeurs. C'est tout à fait légal par la déclaration de type de base de load.

génériques côté Laissant, vos méthodes sont un peu comme ceci:

public abstract class Base 
{ 
    public abstract Object save(); 
    public abstract void load(Object x); 
} 

public class Derived extends Base 
{ 
    @Override 
    public String save() { ... } // Valid 

    @Override 
    public void load(String x) // Not valid 
} 

Avec les génériques enlevés, est clairement pourquoi l'appel save est correct, mais l'appel load est pas? Considérez ceci:

Base b = new Derived(); 
Object x = b.save(); // Fine - it might return a string 
b.load (new Integer(0)); // Has to compile - but the override wouldn't work! 
+1

Downvoters ... écrire des commentaires –

2

Vous ne pouvez pas modifier les paramètres à load(Map<String, String> data) car vous pourriez facilement violer si vous utilisez la classe de base au lieu de la classe spécialisée comme ceci:

Base base = new Derived() 
base.load(new HashMap<Integer, Integer>()); 

cette appellerait la charge méthode de dérivée, mais en violation de la déclaration. Parce que les génériques ne sont vérifiés qu'à la compilation, il ne serait pas possible de détecter cette erreur.

Les valeurs de retour ne posent aucun problème tant qu'elles sont plus spécialisées dans les sous-classes que dans les superclasses.

0

Je crois que le problème est qu'en général il est possible d'avoir deux méthodes discriminées par les types d'arguments. Précisément, il faut être sélectionné. Cela nécessite de regarder les types non effacés. Cela signifie que les deux méthodes load peuvent être distinguées. Nous avons donc des méthodes distinctes, mais sous une forme effacée, il n'existe qu'une seule méthode.

Ce n'est pas un problème avec les types de retour car (en Java) vous pouvez avoir des types de retour covariants mais pas de surcharge par type de retour.

+0

Le problème que j'ai donné dans ma réponse ne serait pas non plus un problème? –

+0

Ce n'est pas pertinent. –

Questions connexes