不同类型的组合

Composition with different types

我有两个 Java classes B 和 C,它们都从 class A 延伸(我不拥有 class A)。

public class B extends A {…}
public class C extends A {…}

我需要一个“ListItem”类型的列表来保存 B 或 C 的实例,但由于(B 和 C)都已经被扩展,它们不能通过使用“ListItem”作为超级对象来进一步扩展class。

我认为唯一的出路是组合(“has-a”关系……)。

因为“ListItem”永远不应该有 B 和 C(但只有其中一个),我打算用 2 个构造函数创建 ListItem 并设置一个适当的 反映 B 或 C 是​​否被持有的内部类型。

有没有更好的方法来实现这个?请看下面的伪代码。

public class ListItem {

    private enum elemType {
        TYPE_B, TYPE_C 
    } 

    private final B mBItem;
    private final C mCItem;
    private final elemType mType;

    // used to hold instance of B
    public ListItem(B b) {
        this.mBItem = b;
        this.mType = TYPE_B;
    } 

    // used to hold instance of C
    public ListItem(C c) {
        this.mCItem = c;
        this.mType = TYPE_C;
    }

    // sample method to demonstrate delegation
    private int getAge() {
        int age;

        if (containsB()) {
            age = mBItem.getAge();
        }
        else if (containsC()) {
            age = mCItem.getAge();
        }
        else {…}
        return age;
    }

    private boolean containsB() {
        return (mType == TYPE_B);    
    }

    private boolean containsC() {
        return (mType == TYPE_C);
    }
}

我的意思是,你有:

public class B extends A {…}
public class C extends A {…}

所以你可以让你的物品保持 A:

public class ListItem {

    private final A item;

    public ListItem (A item) { 
        this.item = item; 
    }

}

而且你有很多关于如何从那里处理它的选项,例如简单地:

    public A getItem () {
        return item;
    }

并从调用者那里进行任何适当的转换或测试。这是最灵活的方式。或者我想是这样的:

    public B getB () {
        return item instanceof B ? (B)item : null;
    }

    public C getC () {
        return item instanceof C ? (C)item : null;
    }

但这开始变得草率了,并且会限制您在 ListItem.

中存储任何其他 A 派生的内容

您可能想在这里开始质疑您的设计。例如,如果您要向 A 添加一些功能,这些功能对于 BC 是常见的,并且是 ListItem[] 正常运行所必需的=63=],考虑从 A 派生一些 class 来添加共同的东西,从中派生 BC,然后让你的 ListItem 存储base(当然,如果你这样做,你就不能再存储 A):

public class AgedA extends A {
    ...
    public int getAge () { return ... }
}

// Then your B and C both extend AgedA, and your ListItem stores an
// AgedA and can use getAge().

您可以使 AgedA 是抽象的,getAge() 是虚拟的,如果这样更合适的话。或者您可以声明一个定义 getAge() 的接口并使用它。

另一种选择是您可以(正如评论中指出的那样)使 ListItem 成为一个接口,或者 ,然后让您的 BC实施或扩展它。

我想你也可以 for ListItem 例如<? extends A>,尽管这可能不合适,尤其是如果您的 ListItem 是混合的。使用泛型的主要好处是,例如getter 现在可以直接 return 子类型而无需强制转换,并且您可以在编译时将类型限制为函数参数和内容中的特定子类型,但除此之外它与仅存储 A 基本相同。

无论如何,除了将 A 存储在 ListItem 中并在更高级别处理不同类型的建议之外,我不能凭良心给您任何其他方法建议,因为对所有这些的需求对我来说似乎是有问题的。

根据您的描述,ListItem 似乎是 A 类(B 或 C,其他子 类 将来)项目的容器。我会推荐使用泛型,使用 <? extends A>,这样你就不需要一个以上的构造函数,并且 A 的所有方法都应该可用。

我认为一个干净的方法是让你的 ListItem 是从 A 延伸的抽象 class,然后有 BCListItem.

扩展

为什么要ListItem抽象?在 BC 之间共享的任何行为都可以在抽象 class 中实现,并且您还可以在其子 class 上附加接口要求。

public abstract class ListItem extends A {
    public int getAge () {
        // Both B and C share this implementation
    }

    // But B and C behave differently for this method
    public abstract void manipulate ();
}

public class B extends ListItem {
    public void manipulate () {
        // Something specific to B here.
    }
}

public class C extends ListItem {
    public void manipulate () {
        // Something specific to C here.
    }
}

然后你可以声明你的ListItem列表,根据需要插入BC的实例,并根据[中的具体方法或抽象接口操作它们。 =12=]:

ArrayList<ListItem> list = new ArrayList<>();

list.add(new B());
list.add(new C());

System.out.println(list.get(0).getAge());
list.get(1).manipulate();