在Java中在运行时选择要扩展的子类

6
我有两个类(称为B和C),它们都派生自一个类(称为A)。现在我需要编写一个类(称为D),它应该能够在运行时动态地从B或C派生。
B、C和A是通过库提供给我的类,而我唯一控制的类是D类。如何根据上述约束编写D类呢?显然,B和C来自不同的供应商,因此取决于其父类,需要重写不同的方法。
我不能编写不同版本的D类,以从B和C子类化,因为覆盖方法在不同的方法名称下具有相同的代码。

1
我不确定在Java中是否可能,除非你使用反射。你不能定义一个代理类D接口代替吗? - Bathsheba
你看过这个链接吗?https://dev59.com/-krSa4cB1Zd3GeqPZMVU - Gennadii Saprykin
2个回答

4
你应该定义一个接口 I,然后定义与 BC 相对应的具体实现。在运行时,您可以确定需要哪个 I 实现,可能使用工厂方法。然后,您的代码只需调用 I 的方法,而不是 BC 的方法。 编辑 似乎有些混淆了这是如何工作的。你想要的是让你的业务逻辑在属于你的一致、稳定的 API 上运行。为此,您创建一个我们称之为 I 的接口。现在,您需要针对需要适配的不同外部类(ABC)创建实现类。它可能看起来像这样:
public interface I {
    void doSomething();
}

public class IA implements I {
    private A a;

    public IA(A a) {
        this.a = a;
    }

    public void doSomething() {
        // specific to A
        a.doSomethingUnique();
    }
}

// similar implementation classes for B and C

现在,您需要在运行时获得一个特定于当前情况的I实例。任何能告诉您运行时使用哪个具体类的信息都可以用于此目的。最差的情况下,您可以这样做:

// in some util class
public static I getI(Object obj) {
    if (obj instanceof A) {
        return new IA((A) obj);
    } else if (obj instanceof B) {
        return new IB((B) obj);
    } else if (obj instanceof C) {
        return new IC((C) obj);
    }
    // maybe throw an exception? or return a mock I implementation?
}

现在,您所有的业务逻辑都只涉及到 I 的实例,并调用在接口中定义的方法,抽象掉您无法控制的不同具体类。

好的。换句话说,将BC都放入(各自的)包装对象中,这些对象的类都实现了I接口,然后使用这些包装类来代替直接访问BC - Florian Schaetz
我明确写道,A、B和C完全不受我的控制。我的类目前派生自A。现在我必须有选择地启用从B或C派生,并在不同的回调中执行相同的代码。 - Varun Bhatia
这个解决方案与A、B或C无关,完全不需要你控制它们。这种模式被广泛用于将现有类(或类)的接口适应到另一个类而不修改其源代码。它被称为适配器模式,请在此处阅读更多信息:https://en.wikipedia.org/wiki/Adapter_pattern - Karakuri
问题在于A、B和C类都不是接口。 - Varun Bhatia
A、B和C不一定要是接口。 - Karakuri

0

你可以使用私有继承来实现。

如果你有:

class A
{
  public void methodA() {...}
}
class B extends A
{
  public void methodB() {...}
}
class C extends A
{
  public void methodC() {...}
}

D将被实现为:

class D
{
  private B b;
  private C c;
  private D() {}
  public static D instantiateAsB()
  {
    D res = new D();
    res.b = new B();
  }
  public static D instantiateAsC()
  {
    D res = new D();
    res.c = new C();
  }
  public void methodA()
  {
    if ( b!=null )
      b.methodA();
    else
      c.methodA();
  }
  public foid methodB() 
  {
    if ( b==null )
      throw new MethodNotImplementedException();
    else
      b.methodB();
  }
  public foid methodC()
  {
    if ( c==null )
      throw new MethodNotImplementedException();
    else
      c.methodC();
  }
}

私有继承有一些缺点。其中之一是D不会成为A。因此,您无法将D对象作为参数传递给需要A的方法。以下代码将无法编译:

void method( A a ) {...}

D d = D.instantiateAsB();
method( d );

你可以使用D语言中的强制类型转换方法来解决这个问题:

// inside D class :
public A castAsA()
{
  if ( b!=null )
    return (A)b;
  else
    return (A)c;
}

public B castAsB()
{
  if ( b==null )
    throw new ClassCastException();
  else
    return b;
}

public C castAsC()
{
  if ( c==null )
    throw new ClassCastException();
  else
    return c;
}

而之前无法编译的代码将被重写为:

D d = D.instantiateAsB()
method( d.castAsA() );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接