我可以使用具有多个界限的类型参数来确保在编译时容器的内容符合某些特征吗?
这可能最好用代码表达:
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既接受鸵鸟和麻雀,又拒绝猫鼬和水牛城。还有其他方法可以构造这样的容器吗?
答案 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。
显然,与您想要的设计相比,它有一些缺点:
T
),取决于类型化的子类来强制元素的类型另一个只能部分起作用的选项:
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=Ostrich
。 X=Sparrow
和T
都扩展了X
和Animal
,但是Bird
,但静态或类型检查器都不聪明足以说明一切!
T != X
似乎完全破坏了类型系统。
长话短说...这可能无法按您想要的方式工作。