Cast to concrete classe et méthode d'appel en Java


Disons que nous avons une classe de base appelée A et quelques sous-classes (B,C,D, etc.). La plupart des sous-classes ont la méthode do() mais pas la baseclass. La classe AA fournit une méthode appelée getObject(), qui créera un objet de type B, ou C ou D, etc., mais renvoie l'objet en tant que type A.

Comment convertir l'objet retourné au type concret et appeler sa méthode do(), si cette méthode est disponible?

MODIFIER: Je ne suis pas autorisé à modifier l'implémentation de la classe A, les sous-classes ou AA, depuis im utilisant une API source fermée.. Et oui, il a des problèmes de conception, comme vous pouvez le voir.

Author: lukuluku, 2012-10-01

8 answers

Je pense qu'une meilleure idée est de faire en sorte que la classe A définisse la méthode do() soit comme une méthode abstraite ou comme une méthode vide concrète. De cette façon, vous n'aurez pas à faire de casting.

Si vous n'êtes pas autorisé à modifier les classes que vous pourriez définir une classe MyA extends A qui définit les do() méthode et MyB, MyC,... et un {[6] } qui ferait essentiellement ce que AA fait, juste qu'il renvoie des objets de type MyB, MyC....

Si ce n'est pas ok alors je ne vois pas une autre façon de vérifier si l'objet renvoyé est de type B et faire un moulage de B et ainsi de suite.

 3
Author: Dan D., 2012-10-01 16:18:17

, Vous pouvez tester avec instanceof et appeler la do() méthodes:

A a = aa.getObject();
if (a instanceof B) {
   B b = (B) a;
   b.do();
}
// ...
 4
Author: Andreas Dolk, 2012-10-01 16:05:25

En supposant que A définit do, et qu'il n'est pas privé, vous pouvez simplement l'appeler sans lancer, quelle que soit la sous-classe que AA renvoie. C'est l'une des caractéristiques de polymorphisme. Lors de l'exécution, l'interpréteur utilisera la version correcte (c'est-à-dire l'implémentation de la classe réelle) de do.

 1
Author: hvgotcodes, 2012-10-01 16:04:28

Tout d'abord, ce serait une meilleure approche de faire Class A en tant que classe abstraite avec do() en tant que méthode abstraite......

De plus, si vous voulez toujours la façon dont vous voulez le faire..puis

Faites un casting explicite.

B b = (B) a; // a is a casted back to its concrete type.

De plus, vous devez garder à l'esprit ce comportement très important du compilateur.

The Object Reference Variable of Super Type must have the method to be called, whether the Sub Type Object has or not.

Par exemple:

A a = new B();

- Pour appeler une méthode, do() sur Object Reference Variable de Type A, classe A doit avoir la go() la méthode.

 0
Author: Kumar Vivek Mitra, 2012-10-01 16:10:14

Si vous n'êtes pas autorisé à modifier A mais que vous pouvez modifier les sous-classes, vous pouvez créer une interface avec la méthode do() et laisser toutes les sous-classes implémenter cette interface.

public interface Doer {
    public void do();
}

public class B extends A implements Doer {
    //implement do method
}

//.. same for other subclass

Alors vous n'avez pas besoin d'un casting. Sinon, vous aurez besoin de downcasts explicites.

 0
Author: Bhesh Gurung, 2012-10-01 16:13:27

Ce que vous décrivez me semble que vous souhaitez appeler Derived Class méthodes sur Base class référence..

, Mais pour cela, vous devez avoir vos méthodes dans votre base class aussi..
Donc, vous devez déclarer votre méthode do() dans votre classe de base A aussi.. Si vous ne voulez pas donner une implémentation, laissez-la être abstraite ou laissez-la être une méthode vide.. Il ne sera pas question..

Maintenant, si vous faites la même chose que vous expliquez.. Vous n'aurez pas besoin de faire un transtypage..

Parce que, approprié Derived Class méthode sera invoquée basé sur - which derived class object does your base class reference point to

public abstract class A {
   public abstract void do();
}

public class B extends A {
   public void do() {
       System.out.println("In B");
   }
}

public class Test {
    public static void main(String[] args) {
        A obj = returnA();

        obj.do();  // Will invoke class B's do() method
    }

    /** Method returning BaseClass A's reference pointing to subclass instance **/
    public static A returnA() {
         A obj = new B();
         return obj;
    }
}

Ok, tout à l'heure vu votre edit, que vous n'êtes pas autorisé à changer vos classes..

Dans ce cas, vous devrez réellement faire un typecast basé sur l'instance de référence renvoyée..

Donc, main méthode ci-dessus, après A obj = returnA(); cette ligne, ajoutez la ligne suivante: -

if (obj instanceof B) {
    B obj1 = (B) obj;

}

Mais, dans ce cas, vous devrez vérifier instanceof sur chacune de vos sous-classes.. Cela peut être un majeur problème..

 0
Author: Rohit Jain, 2012-10-01 16:14:04

La meilleure façon de le faire a A classe cette méthode. Mais puisque vous n'êtes pas autorisé à changer de classe. Je vous conseille de créer une instance de wrapper autour de toutes les classes en utilisant des réflexions.

La méthode statique dans la classe ci-dessous est utilisée juste pour montrer comment le faire. Vous pouvez avoir une variable d'instance distincte qui peut envelopper A dans E.

public class E {

public static void doMethod(A a) {
    Class<?> class1 = a.getClass();
    Method method;
    try {
        method = class1.getDeclaredMethod("doMethod", null);// B, C, D has doMethod
        method.invoke(a, null);
        // I know to many exceptions
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }

}
}

La deuxième option est instance of pour laquelle vous devrez vérifier le type, puis le lancer.

 0
Author: Amit Deshpande, 2012-10-01 17:03:35

Vous pouvez le faire avec un peu de travail si les invocations de méthode renvoient des instances de la classe en question, qui est votre question spécifique (ci-dessus).

import static java.lang.System.out;

public class AATester {
   public static void main(String[] args){
      for(int x: new int[]{ 0, 1, 2 } ){
         A w = getA(x);
         Chain.a(w.setA("a")).a(
            (w instanceof C ? ((C) w).setC("c") : null );
         out.println(w);
      }
   }

   public static getA(int a){//This is whatever AA does.
      A retval;//I don't like multiple returns.
      switch(a){
         case 0:  retval = new A(); break;
         case 1:  retval = new B(); break;
         default: retval = new C(); break;
      }
      return retval;
   }
}

Classe d'essai A

public class A {
   private String a;
   protected String getA() { return a; }
   protected A setA(String a) { this.a=a; return this; }//Fluent method
   @Override
   public String toString() {
      return "A[getA()=" + getA() + "]";
   }
}

Classe d'essai B

public class B {
   private String b;
   protected String getB() { return b; }
   protected B setB(String b) { this.b=b; return this; }//Fluent method
   @Override
   public String toString() {
      return "B[getA()=" + getA() + ", getB()=" + getB() + "]\n  " 
      + super.toString();
  }
}

Classe d'essai C

public class C {
   private String c;
   protected String getC() { return c; }
   protected C setC(String c) { this.c=c; return this; }//Fluent method
   @Override
   public String toString() {
      return "C [getA()=" + getA() + ", getB()=" + getB() + ", getC()=" 
             + getC() + "]\n  " + super.toString();
   }
}

La classe de chaîne

/**
 * Allows chaining with any class, even one you didn't write and don't have 
 * access to the source code for, so long as that class is fluent.
 * @author Gregory G. Bishop [email protected] (C) 11/5/2013 all rights reserved. 
 */
public final class Chain {
   public static <K> _<K> a(K value) {//Note that this is static
      return new _<K>(value);//So the IDE names aren't nasty
   }
}

Classe d'aide de la chaîne.

/** 
 * An instance method cannot override the static method from Chain, 
 * which is why this class exists (i.e. to suppress IDE warnings, 
 * and provide fluent usage). 
 *
 * @author Gregory G. Bishop [email protected] (C) 11/5/2013 all rights reserved.
 */
final class _<T> {
   public T a;//So we can reference the last value if desired.
   protected _(T t) { this.a = T; }//Required by Chain above
   public <K> _<K> a(K value) {
      return new _<K>(value);
   }
}

Sortie:

    A [get(A)=a]
    B [get(A)=a, getB()=null]
      A [getA()=a]
    C [getA()=a, getB()=null, getC()=c)]
      B [get(A)=a, getB()=null]
      A [get(A)=a]
 0
Author: ggb667, 2013-11-26 03:47:15