Java 根据实际对象类型动态参照转换

Java 根据实际对象类型动态参照转换,java,dynamic,reflection,Java,Dynamic,Reflection,我有一些类,比如 ChildA extends Parent ChildB extends Parent ChildC extends Parent 然后在我的应用程序中,我通过父引用在一个方法中获取这个子对象的任何一个 if (parent instanceof ChildA) { ChildA childA = (ChildA) parent; } else if (parent instanceof ChildB) { ChildB chi

我有一些类,比如

ChildA extends Parent
ChildB extends Parent
ChildC extends Parent
然后在我的应用程序中,我通过父引用在一个方法中获取这个子对象的任何一个

    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;
    }
问题是所有这些孩子都有相同的方法,但他们的父母没有

因此,
ChildA、ChildB和ChildC
都有
getSomeValue()
getter,但
Parent
没有

现在,我需要解析来自任何子级的值,但是父级引用没有为我提供API,所以我需要将父级强制转换为特定的子级类型

下面是我试图做的事情的片段:

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个引用,然后检查它们以转换为特定类型,然后检查实际创建的类型以调用该方法

问题是如何在运行时正确地将引用转换为特定的子引用? 我想使用反射来写作是可能的,尽管我甚至不能用反射来解决它

还有,即使有可能,这样做可以吗

仅供参考:我正在处理一个遗留应用程序,因此无法更改以前编写的代码,因此无法在父类中添加此API。此外,这些类是从外部jar提供的

问题是如何正确地投下绝对正确的参考 这种类型中的哪一种是特定的参考

使用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;
    }

如果您确定所有的子对象都有getSomeValue方法,那么您可以将它们称为

parent.getClass().getMethod("getSomeValue").invoke(parent);

然而,这不是一个好的做法。类设计违反了Liskov的替换原则。

我的方法是通过缺少的接口扩展父类。为此,我定义了一个接口来声明缺少的方法:

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

作为一种可能的解决方案,您可以创建一个由特定子类参数化的映射作为键,并将供应商作为值。每个供应商负责铸造和处理特定方法

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

map能否将父类的引用更改为子类从中扩展的其他子类?类似于ChildA-->SubParent-->Parent您可以使用instanceof运算符来检查确切的实例类型您可以添加一个代码示例,说明如何通过父引用在该子对象上的任何方法中获得任何值吗?
getSomeValue()
的返回类型是什么?这有关系吗?StringHi,谢谢,但这个问题包含“正确”这个词,所以若我有一百个孩子,那个么用你们的解决方案,我会有一个很长的方法。我正在寻找一个好的解决方案,你能像我在前面的评论中所问的那样对父类进行子类化吗?ChildA-->SubParent-->Parent,然后在应用程序中使用SubParent引用而不是父引用。对不起,这些类是从外部jar提供的,因此我没有访问它们的权限。您的意思是所有类,即所有子类和父类都来自外部jar吗?是的。我可以用任何方式使用它们,但不能改变哇!伟大的解决方案!:)尽管您必须在映射中为每个子类创建条目。