2016-12-20 1 views
2

J'ai du mal à sous-classer une classe très simple dont les méthodes retournent aussi la classe initiale.Utilisation de la méthode qui retourne l'objet MyClass dans MyClass lorsque MySubClass étend MyClass

public class MyClass { 

    public MyClass(){ 
    } 

    public MyClass filterOn(String something){ 
     MyClass result=new MyClass(); 
     result.doSomethingUsingThisInstance(this, something); 

     return result; 
    } 

} 


public class MySubClass extends MyClass{ 
    .... 
} 

Ok, maintenant si je veux appeler ceci:

MySubClass subClass=new MySubClass(); 
    MySubClass subClass2=(MySubClass)subClass.filterOn("Hello World"); 

Puis j'ai un java.lang.ClassCastException: ne peut pas lancer MyClass à MaSousClasse

Comment empêcher cela?

+0

La valeur de retour de 'filterOn' est' MyClass', pas 'MySubClass', donc l'exception est suffisamment valide. Changez le type de 'result' pour le réparer. – Henrik

+0

'MyClass result = new MyClass();' dans votre méthode de filtrage a un problème. vous créez super instance, et retourné à la fin, vous ne pouvez pas le transformer en sous-type. Je suppose que vous voudrez peut-être remplacer la méthode filterOn dans MySubClass. – Kent

+0

La solution la plus simple serait de ne pas lancer subClass2 car ce n'est pas une intuition MySubClass. Si vous avez besoin d'une instance de 'MySubClass', remplacez simplement' filteron' et renvoyez une instance 'MySubClass' à la place. – n247s

Répondre

2

Remplacer la méthode filterOn() pour créer l'instance que vous souhaitez dans MySubClass:

public class MySubClass extends MyClass{ 

    public MyClass filterOn(String something){ 
     MySubClass result = new MySubClass(); 
     result.doSomethingUsingThisInstance(this, something); 
     return result; 
    } 
    .... 
} 

Vous pouvez également éviter duplication dans filterOn() méthode en introduisant une méthode dans MyClass pour créer une instance de la classe en cours que nous remplaçons dans la sous-classe:

public class MyClass { 

    public MyClass(){ 
    } 

    public MyClass createSpecificInstance() { 
    return new MyClass(); 
    } 

    public MyClass filterOn(String something){ 
     MyClass result = createSpecificInstance(); 
     result.doSomethingUsingThisInstance(this, something); 

     return result; 
    } 

} 

Maintenant Sous classe ne remplacent createSpecificInstance():

public class MySubClass extends MyClass { 

    public MyClass createSpecificInstance() { 
    return new MySubClass(); 
    } 

} 
+0

Solution facile: D. Y aurait-il un moyen de déclarer mes méthodes en utilisant une sorte de «générique» (que je ne maîtrise pas du tout)? – Myoch

+0

En effet :) Oui, vous pourriez mais vous devriez utiliser la réflexion si vous voulez éviter la pseudo-méthode de méthode 'createSpecificInstance()', – davidxxx

+0

Pourriez-vous expliquer un peu plus, s'il vous plaît? vous dites que je peux utiliser des génériques (mais comment?) seulement si j'utilise la réflexion (comme utiliser la classe Méthode?), ai-je raison? – Myoch

0
(MySubClass)subClass.filterOn("Hello World"); 

et

public MyClass filterOn(String something) 

Vous ne pouvez pas lancer la classe de base à la classe dérivée. si vous le faites, vous aurez cette exception, ClassCastException

Lire ceci: java.lang.ClassCastException

0

Vous essayez de jeter votre valeur de retour à un type dérivé, qui ne fonctionne pas et les résultats à l'exception de fonte de classe.

Motif: La conversion vers le haut dans la hiérarchie de type fonctionne, puisque votre classe de base ne requiert que des attributs/méthodes que la classe dérivée possède (trivialement). L'inverse ne fonctionne pas car cela peut entraîner des problèmes. Considérez:

class Base { 
// some stuff here 
} 

class Derived1 extends Base { 
    private int foo; 
} 

class Derived2 extends Base { 
    private String bar; 
} 

Base myObject = new Derived1(); 
// this works, Derived1 has everything Base requires 
// myObject.foo still exists, but can not be trivially accessed. 

Derived2 myOtherObject = (Derived2)myObject; 
// Now what? 
// What happens to myObject.foo? 
// Where does myOtherObject.bar come from? 

Que pouvez-vous faire dans votre situation:

  • Si les implémentations de filterOn() sont très différentes en fonction de la mise en œuvre concrète de la classe dérivée, faire abstraite dans la classe de base et ré-implémenter dans la classe dérivée.
  • Vérifiez si vous avez un problème de conception. Avez-vous vraiment besoin de convertir le résultat de filterOn() en classe dérivée?
  • Utilisez Generics. N'oubliez pas que vous pouvez utiliser une classe dérivée comme générique dans une classe de base. Cela ne fonctionne que si l'implémentation de filterOn() est exactement la même pour chaque sous-classe (sauf les types de cours).
  • Fournit un constructeur permettant de créer une instance de classe dérivée à partir de la classe de base. Utilisez-le à la place de la distribution.Quelque chose dans la ligne de
Derived1(Base baseObject){ 
    // copy all the stuff from the base object 
    this.foo = 0; // initialize the rest 
} 
  • Peut-être que l'héritage n'est pas ce dont vous avez besoin. La composition a tendance à battre beaucoup l'héritage (Explained nicely here). vous pouvez essayer que:
class BetterThenDerived { 
    private Base myBaseObject; 
    private int foo; 
} 
0

C'est un côté du problème de la covariance. Voir par exemple Covariance and contravariance. Et Demonstrate covariance and contravariance in Java?. Ce n'est pas vraiment là où Java excelle, mais il y a quelques options. Tout d'abord, lors de la substitution d'une méthode, vous êtes autorisé à déclarer un type de retour plus spécifique. Par exemple, en MySubClass vous pouvez déclarer:

@Override 
public MySubClass filterOn(String something) { 
    // ... 
} 

Maintenant, cela ne résout pas votre problème. Vous avez toujours besoin d'un moyen pour que cette méthode crée et fasse quelque chose à un objet MySubClass. Ce qu'il fait, il libère le code client d'avoir besoin d'un casting du tout. Vous pouvez prendre la mise en œuvre de la méthode sous forme de réponse de davidxxx (à condition que result.doSomethingUsingThisInstance() est protected ou public):

@Override 
public MySubClass filterOn(String something) { 
    MySubClass result = new MySubClass(); 
    result.doSomethingUsingThisInstance(this, something); 
    return result; 
} 

Vous pouvez être ennuyé d'avoir à dupliquer cette méthode dans tout ce que vous sous-classes. Si le vrai travail reste dans result.doSomethingUsingThisInstance() je devrais penser que vous pourriez être en mesure de vivre avec.

L'autre idée est de clone() pour produire un objet du type d'exécution correcte:

public class MyClass implements Cloneable { 

    public MyClass filterOn(String something) { 
     try { 
      MyClass result = (MyClass) this.clone(); 
      result.doSomethingUsingThisInstance(this, something); 

      return result; 
     } catch (CloneNotSupportedException cnse) { 
      throw new AssertionError(cnse); 
     } 
    } 

} 

je penserais deux fois avant d'utiliser cette idée, cependant. L'une des raisons pour lesquelles votre clone contient des données que vous ne souhaitez pas utiliser, vous devrez peut-être vous assurer qu'il est effacé avant de travailler sur le clone. Vous pouvez toujours le combiner avec le retour du sous-type spécifique dans la sous-classe, maintenant il faut juste un casting (encore dans la mise en œuvre, rien que le client a besoin de se soucier):

public class MySubClass extends MyClass { 

    @Override 
    public MySubClass filterOn(String something) { 
     return (MySubClass) super.filterOn(something); 
    } 

}