【问题标题】:Inheritance and casting: is this good java?继承和强制转换:这是好的 java 吗?
【发布时间】:2012-02-03 16:51:44
【问题描述】:

看看这段代码(来自here

abstract class EntityA {
    AssocA myA;
    abstract void meet();
}

abstract class AssocA {
    int something;
    abstract void greet();
}

class AssocAConcrete extends AssocA {
    void greet() {
        System.out.println("hello");
    }
    void salute() {
        System.out.println("I am saluting.")
    }
}

class EntityAConcrete extends EntityA {
    void meet() {
        System.out.println("I am about to meet someone");
        ((AssocAConcrete)myA).salute();
    }
}

有两个并行的继承树,一个父类和一个关联类。问题出在第 23 行:

((AssocAConcrete)myA).salute();

这很痛苦,我的代码中到处都是这种东西。尽管那行是Entity具体实现的一部分,但我需要提醒它,我要使用AssocA的具体实现,AssocAConcrete。

是否有某种注释来声明这种关系?还是有更好、更通俗的 Java 方式来表达这种设计?谢谢!


这是对@Dave的回应,因为我想在里面放一些代码……

有趣!所以调用看起来像这样:

AssocAConcrete myAssoc = new Assoca();
EnitityA<T extends AssocA> myEntity = new EntityA<AssocAConcrete>();
myEntity.setAssoc(myAssoc);
myAssoc.salute();

是吗?这太酷了。我想我会用它!

【问题讨论】:

  • 如果你写AssocAConcrete myA;而不是AssocA myA;(第2行),它会解决它。但这会破坏其余的代码吗?
  • 是的,整个想法是 AssocAConcrete 可能有不同的变体,EntityA 的客户不希望依赖它。请注意,仅使用 AssocA 和 EntityA 的代码永远不会知道或提及其中一个或另一个 Concrete 类。而且只有两个具体的“A”类知道实现细节。

标签: java oop inheritance


【解决方案1】:

我认为使用泛型会更整洁...

abstract class EntityA<T extends AssocA> {

    // Basically, this means myA is at least an AssocA but possibly more...
    T myA;
    abstract void meet();
}

abstract class AssocA {
    int something;
    abstract void greet();
}

class AssocAConcrete extends AssocA {
    void greet() {
        System.out.println("hello");
    }
    void salute() {
        System.out.println("I am saluting.");
    }
}

class EntityAConcrete extends EntityA<AssocAConcrete> {
    void meet() {
        System.out.println("I am about to meet someone");
        myA.salute();
    }
}

除了避免强制转换之外,这还使得在 AssocA 实现中添加不同的功能变得更加容易。应该总是有一种方法可以在不使用虚拟实现(即只抛出“NotImplementedException”的方法)或强制转换的情况下做事。即使这样做并不总是容易或值得重构时间。换句话说,没有人会因为选角而责怪你(嗯……也许有些人会,但你不能取悦所有人)。

编辑(实例化注释):

来自下面@pitosalas 的 cmets...

//Won't work...can't call 'new' on abstract class AssocA
AssocAConcrete myAssoc = new Assoca();

//Instead, do this...
AssocAConcrete myAssoc = new AssocAConcrete();

然后……

// Again, won't work.  T is only declaring the type inside your class/method.
// When using it to declare a variable, you have to say EXACTLY what you're making,
// or at least something as exact as the methods you're trying to invoke
EnitityA<T extends AssocA> myEntity = new EntityA<AssocAConcrete>();

//Instead do this...
EnitityA<AssocAConcrete> myEntity = new EntityAConcrete();

// Or this...
EntityAConcrete myEntity = new EntityAConcrete();

然后这应该很好......

// Assuming this is defined as `public void setAssoc(T newAssoc) {this.myA = newAssoc;}`
myEntity.setAssoc(myAssoc);
myAssoc.salute();

【讨论】:

  • 我希望我能给你+2。这正是泛型的用途;我不明白为什么有些人会回避他们。哦,我是会责备你使用强制转换来解决一个用泛型更好地解决的问题的人之一。
  • 啊有趣。我会像这样实例化它们吗?
  • 实例化的方式各不相同。您当前用于实例化的代码在哪里?您能否更新您的问题以提供示例?
  • 呸。我想向您展示代码,但我无法自我回答,也无法将代码放在评论中...这是一个尝试...也许我会更新问题...有趣!所以调用看起来像这样: AssocAConcrete myAssoc = new Assoca(); EnitityA myEntity = new EntityA(); myEntity.setAssoc(myAssoc); myAssoc.salute();是的?这太酷了。我想我会用它!
  • 我喜欢这个答案,但问题是他可能有另一个子类,但在基类上没有指定另一种方法......那怎么办?关键是无论您是否使用泛型,都需要遵守合同......
【解决方案2】:

我觉得很可疑。强制转换没有什么可怕的,但在这种情况下,您可以通过将salute 方法引入AssocA 来解决问题。 AssocA 的子类可以提供它们的实现;这是继承的好处的一部分。

您现在所做的是说所有EntityA 实例都有一个AssocA 实例,但是在您的meet 方法中,您基本上强制AssocA 实例成为AssocAConcrete 实例。那是可疑的部分。如果你真的需要AssocAConcrete,为什么还要存在AssocA

另一个选项(基于您的 cmets)是在 greet 方法中调用 salute。这样,特定的子类具有指定的行为greet,在超类中定义,并做它想做的事。在这种情况下,salute 可能会变为私有或受保护。另一个实现可以很容易地做一些不同的事情,比如runLikeHell

【讨论】:

  • 类族之间的这种逻辑耦合非常普遍。唉,类型系统不支持表达。
  • @nwgotcodes - 不是一回事。我想隐藏两个具体“A”类的实现知识,所以它们彼此都知道,但只有 AssocAConcrete 知道如何打招呼。 AssocXConcrete 可能不知道该怎么做。
  • 然后在你的 greet 方法中调用 salute
【解决方案3】:

并行类层次结构的问题非常真实,非常糟糕。 AssocAConcrete 始终与EntityAConcrete 的逻辑耦合不能用类型系统表示。

您不能将EntityAConcrete 中的myA 类型特化为AssocAConcrete,而不将其隐藏在超类中。我认为解决这个问题的最接近的工作是“Family polymorphism”,但这不是主流。

【讨论】:

    【解决方案4】:

    如果您有很大一部分代码使用引用“myA”,您可以声明另一个类似的引用:

    public AssocAConcrete myAConcrete = (AssocAConcrete)myA;
    

    现在您可以使用新的引用myAConcrete 并访问AssocAConcrete 类的功能。

    如果您需要像提到的 hvgotcodes 那样执行此操作,您可能应该考虑将方法移至 AssocA 类。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-16
      • 1970-01-01
      • 2012-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-05
      • 2010-11-30
      相关资源
      最近更新 更多