【问题标题】:Need a design advice to skip object type casting需要设计建议以跳过对象类型转换
【发布时间】:2013-09-11 07:03:50
【问题描述】:

我有一个域类,它有很多子类,如下所示(B 是接口类型,B1 和 B2)实现 B 接口。

正如您在我的主课中看到的,它需要强制转换 (B1 b1 = (B1) a.getB();) 并且我们知道它可以是 B1 或 B2,所以据我所知应该没问题。

我的问题是类型可以增长很多,使用A的人需要设置更多条件来检查B的实例并访问B1或B2的具体值...... 有什么办法可以跳过这个强制转换,这样可以减少未来的很多条件

public class TestMain
{
  public static void main(String[] args)
  {
    A a = new A();
    //this can be B1 or B2 
    B1 b1 = (B1) a.getB();
  }
}

public class A
{
  private B b;

  protected B getB()
  {
    return b;
  }

  protected void setB(B b)
  {
    this.b = b;
  }
}

public interface B
{
  void test();
}

public class B1 implements B
{
  @Override
  public void test()
  {
    // TODO Auto-generated method stub
  }
}
public class B2 implements B
{
  @Override
  public void test()
  {
    // TODO Auto-generated method stub
  }
}

【问题讨论】:

  • 您在寻找instance of 关键字吗?
  • 为什么需要将 B 实例转换为特定的子类型(B1、B2 等...)?拥有一个通用接口的想法是只使用该接口的方法。如果在某些情况下您需要使用某个实现类独有的方法,则必须对其进行强制转换(在使用 instanceof 检查其类型之后)。

标签: java oop casting polymorphism


【解决方案1】:

假设您确实需要此功能(上面 Eran 的评论提出了一个很好的观点;多态性可能满足您的大部分或全部需求),答案是泛型。它们是一个稍微高级的主题,但它们是 Java 的核心(JDK 中的所有集合类都使用它们),因此学习它们非常重要。

泛型是一种表达“某种类型”的方式,也可选择“某种类型,它是某种其他类型的子类型”。后者是你想要的。

public class A<T extends B>
{
  private T b;

  protected T getB()
  {
    return b;
  }

  protected void setB(T b)
  {
    this.b = b;
  }
}

&lt;T extends B&gt; 部分声明了一个泛型参数T,并说它必须是B 的子类。从那时起,您可以使用T,就好像它是A 类中的一个类型一样。对于此类的用户,他们必须指定 A 的类型,编译器会自动为您进行向下转换:

A<B1> a = new A<B1>();
a.set(new B1());
B1 b = a.getB();

请注意,在第二行中,如果你已经完成了a.set(new B2()),这将是一个编译时错误:编译器会抱怨a 被参数化为B1,因此你不能传递@ 987654334@给它。在非常高的层次上,您可以想象编译器正在执行搜索和替换,将原始类中的T 替换为B1。如果你声明:

A<B2> a = new A<B2>();

然后搜索和替换同样会将T 变成B2

实际上,由于一种叫做擦除的东西,它并不是那么简单。您可以在Java trail on generics 上阅读更多信息,并且还有许多其他可用资源。 This PDF 是一个很好的中级描述。

【讨论】:

    【解决方案2】:
    public class Test {
        public static void main(String[] args) {
            B b = new B1();
            if(B1.class.getName().equals(b.getClass().getName())){
                // implement logic for B1 class
            }else if(B1.class.getName().equals(b.getClass().getName())){
                // implement logic for B2 class         
            }
        }
    }
    
    interface B{    
    }
    
    class B1 implements B{  
    }
    
    class B2 implements B{  
    }
    

    进行方法覆盖,您可以为不同的类实现不同的逻辑,您可以使用接口引用调用方法而无需任何强制转换

    【讨论】:

    • 这个 if-else 链由于各种原因并不是很好,但是如果您确实想要这样做,为什么要比较类名呢?为什么不只是if(B1.class.equals(b.getClass())...
    【解决方案3】:

    您需要在执行向下转换时显式添加转换。向上转换是隐含的,并且总是安全的。另外,为什么需要将其显式转换为 B1 或 B2。相反,您可以使用它的基本接口类型作为对对象的引用(即 B),这不需要您添加演员表。

    此外,基引用类型对您来说也是安全的,因为您不必担心实际的对象类型,因此 在运行时防止 ClassCastException。

    使用基类型作为对象的引用有很多优点:

    • 在运行时避免 ClassCastException。
    • 您不必担心添加演员表
    • 此外,如果您将更多类添加到基类层次结构中,您仍然可以使用基引用类型访问这些类。这使您的设计更加灵活。

    【讨论】:

    • 在我的情况下你是完全正确的,用户需要访问 B1 或 B2 特定值,所以在这种情况下他们需要转换它
    • 如果答案正确,请继续并将其标记为已接受。这是您可以参考的链接以获取详细信息。 meta.stackexchange.com/questions/5234/…
    【解决方案4】:

    您也可以在不强制转换的情况下执行以下操作。

    B b1 = a.getB();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-28
      • 2011-11-13
      • 1970-01-01
      • 1970-01-01
      • 2013-11-26
      • 1970-01-01
      相关资源
      最近更新 更多