gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 10:02:51 25 4
gpt4 key购买 nike

我能否使用具有多个边界的类型参数来保证在编译时容器的内容符合某些特征?

这可能最好用代码表达:

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 接受 Ostrich 和 Sparrow,拒绝 Meerkat 和 Buffalo。有没有其他方法可以构建这样的容器?

最佳答案

基本上,不,你不能。概念上的原因在于泛型类型是如何绑定(bind)的。当您创建类似 Zoo<T extends Animal> 的类时, T 表示“任何扩展 Animal 的类型”,它表示“将在运行时提供的扩展 Animal 的特定类型”。通常这可以让你做你想做的事,但你的案例似乎是在测试这个系统的界限 (ba-dum-tiss)。

我认为更具体的答案必须进入通配符 ( ? ) 绑定(bind)系统 - 关于 ? 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

通过方法签名中编码的类型参数,类型系统可以自由选择新的 T对于每个方法调用,这允许它在一次调用时绑定(bind)到 Ostrich,在下一次调用时绑定(bind)到 Sparrow。

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

  1. 底层存储是“原始”的(在本例中只是 Animal ),这取决于类型子类来强制元素的类型
  2. 同样,从存储中取出元素并保持已知类型是困难/困惑的。
  3. 需要每个子类中的样板方法 + 访问帮助程序以在方法中编码类型。

另一种方法只能部分起作用:

  // 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

它提示类型转换的事实 XT有点表明我们正在讨论的问题。例如,当用 BirdZoo<?> 构造实例时,我们基本上没问题。另一方面,假设动物园是使用 BirdZoo<Ostrich> 构建的和 ostrichBirdZoo.addAnimal(new Sparrow());叫做。然后我们有 T=OstrichX=Sparrow .两者 TX扩展 AnimalBird ,但是T != X但是静态或类型检查器不够智能,无法判断!

BirdZoo<?> wildBirdZoo = new BirdZoo<>();
wildBirdZoo.addAnimal(new Ostrich()); // ok
wildBirdZoo.addAnimal(new Sparrow()); // ok

BirdZoo<Ostrich> ostrichBirdZoo = new BirdZoo<>();
ostrichBirdZoo.addAnimal(new Ostrich()); // ok
ostrichBirdZoo.addAnimal(new Sparrow()); // ok and doesn't throw at runtime --> Sparrow to Ostrich cast succeeds.

System.out.println(wildBirdZoo.getAnimals());
System.out.println(ostrichBirdZoo.getAnimals()); // Contains a sparrow...?

这似乎完全打破了类型系统。


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

关于java - 具有多个边界的编译时类型参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56536374/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com