动态参考投射取决于实际对象类型

时间:2018-10-23 13:32:51

标签: java dynamic reflection

我有类层次结构,例如

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提供的。

4 个答案:

答案 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
    } 
}