我有类层次结构,例如
ChildA extends Parent
ChildB extends Parent
ChildC extends Parent
然后在我的应用程序中,我通过Parent引用进入了这个孩子的任何方法。
问题是所有这些孩子都有相同的方法,但他们的父母却没有
因此,ChildA, ChildB, and ChildC
都有getSomeValue()
的吸气剂,而Parent
没有。
现在,我需要解析该子对象中任何一个的值,但是Parent引用没有为我提供API,因此我需要将Parent转换为特定的子类型。
下面是代表我要执行的操作的代码段:
private void processChildren(Parent parent) {
ChildA childA = null;
ChildB childB = null;
ChildC childC = null;
if (parent instanceof ChildA) {
childA = parent;
}
if (parent instanceof ChildB) {
childB = parent;
}
if (parent instanceof ChildC) {
childC = parent;
}
String someValue;
if (Objects.nonNull(childA)) {
someValue = childA.getSomeValue();
} // and the same checks and extracts for each of childs and for many methods
}
如您所见,为了只提取一个值,我需要创建3个引用,然后检查它们以将其转换为特定类型,然后检查实际创建的类型以调用该方法。
问题是如何在运行时将引用正确转换为特定的子引用? 我想可以用反射来写,尽管即使使用反射也无法解决。
即使有可能,也可以吗?
仅供参考:我正在使用旧版应用程序,因此无法更改以前编写的代码,因此无法在Parent类中添加此API。另外,这些类是从外部jar提供的。
答案 0 :(得分:3)
作为一种可能的解决方案,您可以创建一个由特定子类作为参数,供方为值的参数化映射。每个供应商都有责任铸造和处理特定的方法。
Map<Class<? extends Parent>, Supplier> getValueMap = new HashMap<>();
getValueMap.put(ChildA.class, () -> { return ((ChildA) parent).getValue(); });
getValueMap.put(ChildB.class, () -> { return ((ChildB) parent).getValue(); });
getValueMap.put(ChildC.class, () -> { return ((ChildC) parent).getValue(); });
getValueMap.get(parent.getClass()).get();
答案 1 :(得分:0)
问题是如何正确转换绝对是 这种类型的一种具体参考?
使用instanceof运算符检查父级引用的确切实例类型。
if (parent instanceof ChildA) {
ChildA childA = (ChildA) parent;
} else if (parent instanceof ChildB) {
ChildB childB = (ChildB) parent;
} else if (parent instanceof ChildC) {
ChildC childAC = (ChildC) parent;
}
答案 2 :(得分:0)
如果您确定所有子级都具有getSomeValue方法,则可以将其称为
parent.getClass().getMethod("getSomeValue").invoke(parent);
但是,这不是一个好习惯。该类设计违反了Liskov的替换原则here。
答案 3 :(得分:0)
我的方法是通过缺少的接口扩展Parent
。为此,我正在定义一个接口,该接口声明缺少的方法:
public interface HasSomeValue {
String getSomeValue();
}
由于不可能让Parent
本身实现此接口,因此我为Parent
定义了一个包装器:
private static class ParentWithSomeValue implements HasSomeValue {
private Parent parent;
public ParentWithSomeValue(Parent parent) {
this.parent = parent;
}
@Override
public String getSomeValue() {
try {
Method method = parent.getClass().getMethod("getSomeValue");
return (String) method.invoke(parent);
} catch (Exception e) {
return null;
}
}
}
此外,ParentWithSomeValue
委托了Parent
已经拥有的其他方法。然后ParentWithSomeValue
提供了Parent
的完整接口,并且可以替换它。
getSomeValue
是使用反射实现的。如果parent
具有被调用的方法,则使用反射将调用委派给parent
。否则,将返回默认值。在我的示例null
中。另一个选择是如果parent
必须具有被调用的方法,则会引发异常。
使用这种方法,您还可以扩展已实现的接口或添加更多接口,并在每个方法的基础上实现委托。
将此应用于您的代码:
private void processChildren(Parent parent) {
if (Objects.nonNull(parent)) {
ParentWithSomeValue parentWithSomeValue = new ParentWithSomeValue(parent);
String someValue = parentWithSomeValue.getSomeValue();
// go on
}
}