Cast to concrete class and call method in Java

2019-04-28 20:48发布

Lets say we have a baseclass called A and some subclasses (B,C,D, etc.). Most subclasses have the method do() but the baseclass does not. Class AA provides a method called getObject(), which will create an object of type B, or C or D, etc., but returns the object as type A.

How do I cast the returned object to the concrete type and call its do() method, if this method is available?

EDIT: I'm not allowed to change the implementation of Class A, the subclasses or AA, since im using a closed Source API.. And yeah, it does have some design issues, as you can see.

8条回答
The star\"
2楼-- · 2019-04-28 21:31

You can test with instanceof and call the do() methods:

A a = aa.getObject();
if (a instanceof B) {
   B b = (B) a;
   b.do();
}
// ...
查看更多
萌系小妹纸
3楼-- · 2019-04-28 21:31

First of all it would be a better approach to make Class A as an abstract Class with do() as an Abstract method in it......

Moreover if you still want the way you want to do it..then

Do an explicit cast.

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

Moreover you should keep in mind this very important behaviour of the Compiler.

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

Eg:

A a = new B();

- To call a method, do() on Object Reference Variable of Type A, class A must have the go() method.

查看更多
狗以群分
4楼-- · 2019-04-28 21:35

Assuming A defines do, and it is not private, you can just call it without a cast, no matter the subclass that AA returns. That's one of the features of polymorphism. At runtime, the interpreter will use the correct (i.e. the implementation of the actual class) version of do.

查看更多
在下西门庆
5楼-- · 2019-04-28 21:41

What you are describing seems to me like you want to invoke Derived Class methods on Base class reference..

But for that, you need to have your methods in your base class also..
So, you need to declare your method do() in your base class A also.. If you don't want to give an implementation, let it be abstract, or let it be an empty method.. It will not matter..

Now, if you do the same thing you're explaining.. You won't need to do a typecast..

Because, appropriate Derived Class method will be invoked based upon - 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, just now saw your edit, that you are not allowed to change your classes..

In that case, you will actually need to do a typecast based on the instance of returned reference..

So, in main method above, after A obj = returnA(); this line add the following line: -

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

}

But, in this case, you would need to check instanceof on each of your subclasses.. That can be a major problem..

查看更多
可以哭但决不认输i
6楼-- · 2019-04-28 21:45

You can do this with a little work if the method invocations return instances of the class in question, which is your specific question (above).

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;
   }
}

Test class 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() + "]";
   }
}

Test class 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();
  }
}

Test Class 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();
   }
}

The Chain class

/**
 * 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 ggb667@gmail.com (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
   }
}

Chain's helper class.

/** 
 * 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 ggb667@gmail.com (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);
   }
}

Output:

    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]
查看更多
戒情不戒烟
7楼-- · 2019-04-28 21:46

I think a better idea is to actually have class A define the do() method either as an abstract method or as a concrete empty method. This way you won't have to do any cast.

If you are not allowed to change any of the classes than you could define a class MyA extends A which defines the do() method and MyB, MyC,... and a MyAA that would basically do what AA does, just that it returns objects of type MyB, MyC....

If this is not ok then I don't see another way than checking if the returned object is of type B and do a cast to B and so on.

查看更多
登录 后发表回答