【问题标题】:Cast to concrete class and call method in Java在Java中转换为具体类和调用方法
【发布时间】:2012-10-01 16:02:28
【问题描述】:

假设我们有一个名为A 的基类和一些子类(BCD 等)。大多数子类都有do() 方法,但基类没有。 AA 类提供了一个名为getObject() 的方法,该方法将创建BCD 等类型的对象,但将对象返回为A 类型。

如果此方法可用,我如何将返回的对象转换为具体类型并调用其do() 方法?

编辑: 我不允许更改 Class A、子类或 AA 的实现,因为我使用的是封闭的 Source API。是的,它确实存在一些设计问题,就像你可以看到。

【问题讨论】:

  • 将 do() 设为抽象方法或将 A 设为接口而非类。或者一个抽象类;这样每个类都会调用它自己的 do() 而它们将从 A 继承默认值。
  • 可以换B、C、D、AA吗?
  • 如果您知道对象的类别,只需将其转换为 (B) 或其他。如果您不知道对象的类,请使用instanceofgetClass().getName() 找出它是什么类。
  • @Dan nope,这就是我首先发布这个问题的原因:)

标签: java inheritance reflection types casting


【解决方案1】:

您可以使用instanceof 进行测试并调用do() 方法:

A a = aa.getObject();
if (a instanceof B) {
   B b = (B) a;
   b.do();
}
// ...

【讨论】:

  • 我认为这个解决方案违反了开放封闭原则。
【解决方案2】:

我认为更好的办法是让A 类将do() 方法定义为抽象方法或具体的空方法。这样你就不必做任何演员表了。

如果不允许更改任何类,则可以定义一个类MyA extends A,它定义了do() 方法和MyBMyC、...和一个MyAA,基本上做AA 所做的事情,只是它返回MyBMyC....类型的对象。

如果这不行,那么除了检查返回的对象是否为 B 类型并强制转换为 B 等等之外,我没有看到其他方法。

【讨论】:

  • 知道了,请看第二段和第三段。
  • 它有可能以某种方式将对象转换为其具体类并检查 do() 方法是否可用并使用反射调用它?
  • 好吧,如果你转换它,那么你就不必检查方法是否存在。如果它不存在,它将在编译时失败。但是,您可以使用反射而不进行强制转换。
  • 看我的例子,它有编译时间和运行时安全,没有警告,可以完美解决你的问题。
【解决方案3】:

假设A 定义了do,并且它不是私有的,那么无论AA 返回的子类如何,您都可以直接调用它而无需强制转换。这是多态性的特征之一。在运行时,解释器将使用正确的(即实际类的实现)版本的do

【讨论】:

  • 如果 getObject() 返回 A(超类实例),你将如何调用子类中的 do()?我不明白你的答案..你能更具体一点吗..
  • 不能。我说将do 放在基类中,否则您必须遵循@Andreas_D 解释的内容。
【解决方案4】:

首先,将Class A 作为一个抽象类,并将do() 作为一个抽象方法,这将是一种更好的方法......

此外,如果你仍然想要你想要的方式..那么

进行显式转换。

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

此外,您应该牢记编译器的这一非常重要的行为

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

例如:

A a = new B();

-要调用方法,do() on Object Reference Variable 类型 A,类 A必须有 go() 方法。

【讨论】:

    【解决方案5】:

    如果不允许更改A,但可以更改子类,则可以使用do() 方法创建接口,并让所有子类实现该接口。

    public interface Doer {
        public void do();
    }
    
    public class B extends A implements Doer {
        //implement do method
    }
    
    //.. same for other subclass
    

    那么你就不需要演员表了。否则,您将需要一些明确的向下转型。

    【讨论】:

      【解决方案6】:

      在我看来,您所描述的内容就像您想在 Base class 参考上调用 Derived Class 方法..

      但是为此,您还需要在 base class 中包含您的方法..
      所以,你还需要在你的基类A 中声明你的方法do()。如果你不想给出一个实现,让它是抽象的,或者让它是一个空方法。没关系..

      现在,如果你做同样的事情你正在解释..你不需要做类型转换..

      因为,适当的Derived Class 方法将基于 - 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;
          }
      }
      

      好的,刚刚看到你的编辑,你不能改变你的类..

      在这种情况下,您实际上需要根据返回的引用实例执行typecast..

      所以,在上面的main 方法中,在A obj = returnA(); 这一行之后添加以下行:-

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

      但是,在这种情况下,您需要在每个子类上检查 instanceof。这可能是一个主要问题..

      【讨论】:

      • 正确!该 api 有很多从类 A 派生的类,将来可能会有更多类从类 A 派生,这将迫使我每次都更改代码。解决方案应该独立于新的子类
      • @lukuluku。但是怎么会有人给你这样的设计呢??
      【解决方案7】:

      最好的方法是使用 A 类该方法。但是由于您不允许更改任何课程。我建议您使用反射围绕所有类创建一个包装器实例。

      下面的类中的静态方法只是用来展示如何做到这一点。您可以有单独的实例变量,可以将 A 包裹在 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();
          }
      
      }
      }
      

      第二个选项是instance of,您必须检查其类型,然后进行强制转换。

      【讨论】:

        【解决方案8】:

        如果方法调用返回相关类的实例,您可以做一些工作,这是您的具体问题(上文)。

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

        测试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() + "]";
           }
        }
        

        测试类 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();
          }
        }
        

        测试类 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();
           }
        }
        

        链类

        /**
         * 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 的辅助类。

        /** 
         * 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);
           }
        }
        

        输出:

            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]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-12-25
          • 1970-01-01
          • 2016-01-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多