查看此代码(来自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();
是?太棒了。我想我会用它!
答案 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
一起使用的逻辑耦合无法用类型系统表示。
您无法将myA
中EntityAConcrete
的类型专门化为AssocAConcrete
,而不会将其隐藏在超类中。我认为解决这个问题的最接近的工作是“Family polymorphism”,但这不是主流。
答案 3 :(得分:0)
如果你有大部分代码使用引用“myA”,你可以声明另一个引用:
public AssocAConcrete myAConcrete = (AssocAConcrete)myA;
现在您可以使用新的引用myAConcrete
并访问AssocAConcrete
类的功能。
如果你需要像hvgotcodes那样做很多,你应该考虑将方法移到AssocA
类。