有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

不同类型的java组合

我有两个Java类B和C,它们都是从类A扩展而来的(我不拥有类A)

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

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

我认为唯一的方法是组合(“has-a”关系…)

由于“ListItem”不应该有B和C(但只能有一个),我计划用两个构造函数创建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);
    }
}

共 (3) 个答案

  1. # 1 楼答案

    我认为一个干净的方法是让ListItem成为从A扩展而来的抽象类,然后让BCListItem扩展而来

    为什么要把ListItem抽象化?跨BC共享的任何行为都可以在抽象类中实现,您还可以对其子类施加接口要求

    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的实例,并根据ListItem中的具体方法或抽象接口操作它们:

    ArrayList<ListItem> list = new ArrayList<>();
    
    list.add(new B());
    list.add(new C());
    
    System.out.println(list.get(0).getAge());
    list.get(1).manipulate();
    
  2. # 2 楼答案

    从您的描述来看,ListItem似乎是a型项目(B或C,未来的其他子类)的容器。我建议使用泛型,使用<? extends A>,这样就不需要多个构造函数,A的所有方法都应该可用

  3. # 3 楼答案

    我是说,你有:

    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派生的内容

    你可能想在这里开始质疑你的设计。例如,如果将^ {CD1}}添加到^ {CD5> }和^ {CD6> } EM的某些功能,并且对于{{CD3}} EEM的正确操作是必要的,考虑从^ {< CD1> }导出某个类,以添加普通的东西,从中导出^ {CD5}}和^ {CD6}},然后让你的ListItem存储这个基础(当然,如果你这样做,你就不能再存储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成为一个接口,或者a class that extends ^{},然后让BC实现或扩展它

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

    在任何情况下,除了建议将A存储在ListItem中并在更高级别上处理不同的类型之外,我不能凭良心向您提供任何其他方法建议,因为对所有这些的需求在我看来都是可疑的