给定两个java类A和B,其中A通常通过B实例化,例如:
A myA = B.createA();
我可以创建A的子类(让我们称之为SubA)并以某种方式让它由B.createA()方法实例化吗?
(请注意,我无法修改A和B ....)
我知道并非A的所有实例都是SubA的实例,因此我不能这样做:
SubA mySubA = B.createA();
同样,我也不能这样投:
SubA mySubA = (SubA) (B.createA());
出于同样的原因 - 它将获得ClassCastException。
我是否密集并忘记了一些基本的东西,或者没有办法做到这一点?
(后期补充:我很抱歉,我应该提到A和B各有大约50种方法,我想要做的就是给SubA添加一个属性,以及一个getter和一个setter。我我真的不想实现所有50个A的方法来调用超类对象中的相应方法。)
答案 0 :(得分:3)
这通常通过代理完成:
class SubA extends A {
private A proxiedClass;
public SubA(A a) {
proxiedClass = a;
}
public int anyMethodInA() {
return proxiedClass.anyMethodInA();
}
}
...
SubA mySubA = new SubA(B.createA());
手动执行此操作相当冗长,因此大多数人使用某种AOP库(如AspectJ)来拦截他们感兴趣的方法调用。
答案 1 :(得分:3)
听起来您真正喜欢的是修改原始A
和B
的行为。在这种情况下,您可以尝试扩展这两个类(其中B
的扩展名纯粹是为了指定一个稍微不同的工厂方法来创建SubA
)。
class SubA extends A {
/** This is the one special aspect of SubA justifying a sub-class.
Using double purely as an example. */
private double specialProperty;
public double getSpecialProperty() { return specialProperty; }
public void setSpecialProperty(double newSP) { specialProperty = newSP; }
public SubA() {
super();
// Important differences between SubAs and As go here....
// If there aren't any others, you don't need this constructor.
}
// NOTE: you don't have to do anything else with the other methods of
// A. You just inherit those.
}
class SubB extends B {
// Purely for the purposes of a slightly different factory method
public A createA() {
return new SubA();
}
// Or if you need a static method
// (this is usually instead of the first choice)
public static A createA() {
return new SubA();
}
}
请注意,此时,您可以创建一个SubB
工厂对象,并使其看起来像原始B
,如下所示:
B myNewB = new SubB();
A myA = myNewB.createA();
或者,如果您使用的是静态工厂,那么它就不是那么接近匹配(但它很接近)。
A myA = SubB.createA();
现在,如果你真的需要对子属性做一些事情,你可以通过子界面访问它。即,如果你像这样创建对象:
SubA mySubA = SubB.createA();
mySubA.setSpecialProperty(3.14);
double special = mySubA.getSpecialProperty();
编辑以讨论“延迟添加”:
此时,您的SubA对象应该正是您想要的。它将从父级(A)继承50个方法,您可以将其他属性添加到子级,以及getter和setter。我改变了上面的代码来说明我的意思。
答案 2 :(得分:1)
你可以在它周围创建一个包装器,SubA
有一个以A
为参数的构造函数。
像这样:
SubA mySubA = new SubA(B.createA());
由于SubA
的所有实例都是A
的实例,您可以将其分配给现有的A
变量并覆盖任何必要的方法。
A myA = new SubA(B.createA());
我想不出任何其他干净的方式。
答案 3 :(得分:1)
如果您只是想在没有面向对象的情况下向A
添加字段(例如更改行为),则可以将其添加为“外部字段”。使用WeakHashMap
从A
的实例映射到字段值(只要字段值不直接或间接引用A
,或者您将拥有对象生命周期争用问题):
private static final Map<A,FieldType> map =
new java.util.WeakHashMap<A,FieldType>(); // thread-safe from 1.6, IIRC
public static FieldType getField(A a) {
return map.get(a);
}
public static void setField(A a, FieldType value) {
map.set(a, value);
}
我们真的应该使用WeakIdentityHashMap
,但它在Java库中不存在!