具有多个边界的编译时类型参数

时间:2019-06-11 04:13:31

标签: java generics type-parameter type-bounds

我可以使用具有多个界限的类型参数来确保在编译时容器的内容符合某些特征吗?

这可能最好用代码表达:

public class TestCase {

    // our base type
    public static abstract class Animal { }

    // a container of animals, but we'd like to restrict to certain kinds
    public static class Zoo<T extends Animal> {
        private List<T> animals = new ArrayList<>();
        public void addAnimal(T animal) {
            this.animals.add(animal);
        }
        public List<T> getAnimals() {
            return Collections.unmodifiableList(animals);
        }
    }

    // Animal traits - we want to build a Zoo to only house animals with some traits
    public static interface Bird {}
    public static interface Mammal {}
    public static interface Large {}

    // An assortment of animals with different traits
    public static class Sparrow extends Animal implements Bird { }
    public static class Ostrich extends Animal implements Bird, Large { }
    public static class Meerkat extends Animal implements Mammal { }
    public static class Buffalo extends Animal implements Mammal, Large { }

    // some different types of zoos we could build
    public static class BirdZoo<T extends Animal & Bird> extends Zoo<T> {}
    public static class LargeAnimalZoo<T extends Animal & Large> extends Zoo<T> {}
    public static class LargeMammalZoo<T extends Animal & Large & Mammal> extends Zoo<T> {}

    // BirdZoo should accept Ostrich & Sparrow, not Meerkat or Buffalo
    public static void main(String[] args) {
        BirdZoo rawBirdZoo = new BirdZoo();
        rawBirdZoo.addAnimal(new Ostrich()); // warning - unchecked
        rawBirdZoo.addAnimal(new Sparrow()); // warning - unchecked
        rawBirdZoo.addAnimal(new Meerkat()); // warning - unchecked
        rawBirdZoo.addAnimal(new Buffalo()); // warning - unchecked

        BirdZoo<?> wildBirdZoo = new BirdZoo<>();
        wildBirdZoo.addAnimal(new Ostrich()); // error - incompatible types
        wildBirdZoo.addAnimal(new Sparrow()); // error - incompatible types
        wildBirdZoo.addAnimal(new Meerkat()); // error - incompatible types
        wildBirdZoo.addAnimal(new Buffalo()); // error - incompatible types

        BirdZoo<? extends Bird> boundedBirdZoo_B = new BirdZoo<>();
        boundedBirdZoo_B.addAnimal(new Ostrich()); // error - incompatible types
        boundedBirdZoo_B.addAnimal(new Sparrow()); // error - incompatible types
        boundedBirdZoo_B.addAnimal(new Meerkat()); // error - incompatible types
        boundedBirdZoo_B.addAnimal(new Buffalo()); // error - incompatible types

        BirdZoo<? extends Animal> boundedBirdZoo_A = new BirdZoo();
        boundedBirdZoo_A.addAnimal(new Ostrich()); // error - incompatible types
        boundedBirdZoo_A.addAnimal(new Sparrow()); // error - incompatible types
        boundedBirdZoo_A.addAnimal(new Meerkat()); // error - incompatible types
        boundedBirdZoo_A.addAnimal(new Buffalo()); // error - incompatible types

        BirdZoo<Ostrich> ostrichZoo = new BirdZoo<>();
        ostrichZoo.addAnimal(new Ostrich());
        ostrichZoo.addAnimal(new Sparrow()); // error - incompatible types
        ostrichZoo.addAnimal(new Meerkat()); // error - incompatible types
        ostrichZoo.addAnimal(new Buffalo()); // error - incompatible types
    }
}

我想要的是BirdZoo既接受鸵鸟和麻雀,又拒绝猫鼬和水牛城。还有其他方法可以构造这样的容器吗?

1 个答案:

答案 0 :(得分:1)

基本上,不,你不能。概念上的原因在于泛型类型如何绑定。当您创建类似Zoo<T extends Animal>的类时,T 并不是意思是“扩展了Animal的任何类型”,它的意思是“扩展了{ {1}},将在运行时提供”。通常,这可以让您执行所需的操作,但是您的案例似乎正在测试此系统的界限(ba-dum-tiss)。

我认为更复杂的答案必须放在通配符(Animal)绑定系统中-关于?的事情意味着它不能证明类型? extends A & B扩展了C的匹配项。

一个达到目标的(糟糕的)设计如下:

A & B & D

使用在方法签名中编码的类型参数,类型系统可以在每次方法调用时自由选择一个新的 public static abstract class Zoo{ private List<Animal> animals = new ArrayList<>(); protected void addAnimalHelper(Animal animal) { this.animals.add(animal); } } public static class BirdZoo extends Zoo { public <T extends Animal & Bird> void addAnimal(T animal) { addAnimalHelper(animal); } } BirdZoo birdZoo = new BirdZoo(); birdZoo.addAnimal(new Ostrich()); // ok birdZoo.addAnimal(new Sparrow()); // ok birdZoo.addAnimal(new Meerkat()); // Meekrat doesn't conform to Bird birdZoo.addAnimal(new Buffalo()); // Buffalo doesn't conform to Bird ,这使得它可以在一次调用时绑定到Ostrich,而在下一次调用时绑定到Sparrow。

显然,与您想要的设计相比,它有一些缺点:

  1. 底层存储是原始的(在这种情况下仅为T),取决于类型化的子类来强制元素的类型
  2. 同样,将元素移出存储区并保持已知类型非常困难/混乱。
  3. 每个子类中都需要样板方法+可以访问帮助程序以对方法中的类型进行编码。

另一个只能部分起作用的选项:

Animal

它抱怨将 // Note - inheritance isn't used here since it breaks due to method overriding issues. public static class BirdZoo<T extends Animal & Bird> { private List<T> animals = new ArrayList<>(); public <X extends Animal & Bird> void addAnimal(X animal) { this.animals.add((T)animal); // Unchecked cast warning } public List<T> getAnimals() { return Collections.unmodifiableList(animals); } } BirdZoo<?> wildBirdZoo = new BirdZoo<>(); wildBirdZoo.addAnimal(new Ostrich()); // ok wildBirdZoo.addAnimal(new Sparrow()); // ok wildBirdZoo.addAnimal(new Meerkat()); // error - incompatible types wildBirdZoo.addAnimal(new Buffalo()); // error - incompatible types 强制转换为X的事实表明了我们正在讨论的问题。例如,当实例使用T构造时,我们就可以了。另一方面,假设动物园是使用BirdZoo<?>构建的,并且调用了BirdZoo<Ostrich>。然后我们有ostrichBirdZoo.addAnimal(new Sparrow());T=OstrichX=SparrowT都扩展了XAnimal,但是Bird静态或类型检查器都不聪明足以说明一切!

T != X

似乎完全破坏了类型系统。


长话短说...这可能无法按您想要的方式工作。