【问题标题】:How to create a collection/list of instances of many classes which implement two common interfaces (without modifying those classes)?如何创建实现两个通用接口(不修改这些类)的许多类的实例的集合/列表?
【发布时间】:2021-01-22 23:59:55
【问题描述】:

我有两个接口(@98​​7654321@、IfaceB)和两个实现这些接口的类(C 类、D 类):

interface IfaceA {
    void doA();
}

interface IFaceB {
    void doB();
}

class C implements IfaceA, IFaceB {
    public void doA() {}
    public void doB() {}
}

class D implements IfaceA, IFaceB {
    public void doA() {}
    public void doB() {}
}

我无法更改这些类的签名。

如何创建实现这两个接口的类实例的列表或集合?

我尝试了什么:

public static void main(String[] args) {

    List<? extends IfaceA & IFaceB> test_1;
    List<? extends IfaceA, IFaceB> test_2;

    Class<? extends IfaceA, IFaceB>[] test_3;
}

都是错误的(一个通配符只能有一个边界,而我不确定它是否可以与类型绑定)。

我知道这个可能有用:

Object[] objects = new Object[] {
        new C(), new D()
};

for (Object o: objects) {
    IfaceA a = (IfaceA) o;
    IfaceB b = (IfaceB) o;

    a.doA();
    b.doB();
}

但这看起来不太对。

【问题讨论】:

    标签: java inheritance


    【解决方案1】:

    可能的方法是创建包装器类型

    • 只能包装implements IfaceA, IFaceB的类的实例
    • 允许从包装实例上的两个接口调用所有方法。

    它可能看起来像:

    class Wrapper<T extends IfaceA & IFaceB> implements IfaceA, IFaceB {
    
        private final T element;
    
        public Wrapper(T element) {
            this.element = element;
        }
    
        @Override
        public void doA() {
            element.doA();
        }
    
        @Override
        public void doB() {
            element.doB();
        }
    
    }
    

    这将让我们使用 Wrapper 作为 List 中的元素类型:

    class Demo {
    
        public static void main(String[] args) {
    
            //Wrapper<?> can represent both Wrapper<C> and Wrapper<D>
            List<Wrapper<?>> list = new ArrayList<>();
            list.add(new Wrapper<>(new C())); 
            list.add(new Wrapper<>(new D()));
    
            for (Wrapper<?> wrapper : list){
                wrapper.doA(); //both calls compile fine
                wrapper.doB(); //both calls compile fine
            }
    
        }
    
    }
    

    替代版本。

    我们可以通过 getter 访问该元素并直接在其上调用来自 IfaceA 和 IFaceB 接口的所有方法,而不是将方法调用委托给包装的元素。

    class Wrapper<T extends IfaceA & IFaceB> {
        private final T element;
    
        public Wrapper(T element) {
            this.element = element;
        }
    
        public T getElement() {
            return element;
        }
    }
    
    public class Demo {
        public static void main(String[] args) {
            List<Wrapper<?>> list = new ArrayList<>();
            list.add(new Wrapper<>(new C()));
            list.add(new Wrapper<>(new D()));
    
            for (Wrapper<?> wrapper : list){
                //here `var` represents "some" subtype of both IfaceA & IFaceB 
                var element = wrapper.getElement();             
    
                // so following is legal
                element.doA(); 
                element.doB(); 
            }
        }
    }
    

    或者如果有人更喜欢 Java 8 风格,我们可以像这样重写上面的循环

    list.stream()
        .map(Wrapper::getElement)
        .forEach(element -> {
            element.doA();
            element.doB();
        });
    

    【讨论】:

    • 替代解决方案令人印象深刻。尽管如此,我认为通配符绑定的一个很大限制是它不能扩展两个接口。谢谢你,好建议!
    • @Benjamin 欢迎您。也同意,不知道为什么我们不能有&lt;? extends Type1 &amp; Type2&gt;。根据Why can't you have multiple interfaces in a bounded wildcard generic? 中接受的答案,似乎有一些准备工作可以允许它。我的猜测是,由于这不是很常见的用例/需求(可以通过创建另一个接口来解决,该接口将扩展 IfaceA 和 IFaceB,这将允许我们拥有List&lt;? extends mixOfAB&gt;)Java 开发人员可能专注于其他事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多