继承和转换:这是一个很好的java吗?

时间:2012-02-03 16:51:45

标签: java oop inheritance

查看此代码(来自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();

是?太棒了。我想我会用它!

4 个答案:

答案 0 :(得分:8)

我认为使用泛型来说这很简洁......

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的评论......

//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();

答案 1 :(得分:3)

对我来说很可疑。投射没有什么可怕的,但在这种情况下,您可以通过将salute方法引入AssocA来解决问题。 AssocA的子类可以提供它们的实现;这是继承利益的一部分。

您现在正在做的是说所有EntityA个实例都有AssocA个实例,但是在您的meet方法中,您基本上强制AssocA实例为{ {1}}实例。这是可疑的部分;如果您确实需要AssocAConcrete,那么AssocA为什么会存在。

另一个选项(根据您的评论)是在AssocAConcrete方法中调用salute。这样,特定的子类就指定了在超类中定义的行为greet,并按照自己的意愿行事。在这种情况下,greet可能会变为私有或受到保护。另一种实现可以轻松地执行不同的操作,例如salute

答案 2 :(得分:0)

并行类层次结构的问题是非常真实的,真的很糟糕。 AssocAConcrete始终与EntityAConcrete一起使用的逻辑耦合无法用类型系统表示。

您无法将myAEntityAConcrete的类型专门化为AssocAConcrete,而不会将其隐藏在超类中。我认为解决这个问题的最接近的工作是“Family polymorphism”,但这不是主流。

答案 3 :(得分:0)

如果你有大部分代码使用引用“myA”,你可以声明另一个引用:

public AssocAConcrete myAConcrete = (AssocAConcrete)myA;

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

如果你需要像hvgotcodes那样做很多,你应该考虑将方法移到AssocA类。