如何强制两个实例变量具有相同的泛型类型?

2022-09-01 20:43:50

假设我有一个类,其中包含两个泛型类型的实例变量,我如何强制这些类型相同?

我没有在其他地方找到任何类似的问题(更不用说答案了),这可能是因为我用错了术语。有这个问题,但我不确定如何将其应用于我的情况。

简而言之,我怎样才能让最后一行代码不编译?

class Foo<T> {
    private T value;
}

class Bar {
    private Foo var1;
    private Foo var2;

    public Bar(Foo var1, Foo var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public static void main(String[] args) {
        Foo<String> var1 = new Foo<>();
        Foo<Integer> var2 = new Foo<>();
        Bar b = new Bar(var1, var2); // this should not work
    }
}

答案 1

也使通用:Bar

class Bar<T> {
    private Foo<T> var1;
    private Foo<T> var2;

    public Bar(Foo<T> var1, Foo<T> var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public static void main(String[] args) {
        Foo<String> var1 = new Foo<>();
        Foo<Integer> var2 = new Foo<>();
        Bar<String> b = new Bar<>(var1, var2); // this does not work
    }
}

通过使用 的类级泛型参数,您可以为两个实例变量强制实施相同的类型。TBar

这也消除了使用原始类型(永远不应该使用)的警告,正如@daniu在注释中提到的。


现在,如果您碰巧不使用原始类型,但希望允许不同的类型,则可以使用通配符:

使用上限,它只允许类型化读取(并且将始终产生类型的实现):var1var2T

class Bar<T> {
    private Foo<? extends T> var1;
    private Foo<? extends T> var2;

    public Bar(Foo<? extends T> var1, Foo<? extends T> var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public static void main(String[] args) {
        Foo<String> var1 = new Foo<>();
        Foo<Integer> var2 = new Foo<>();
        Bar<Object> b = new Bar<>(var1, var2); // this does now work
    }
}

而使用下限,它只允许类型化写作(并且将始终使用类型的任何实现):var1var2T

class Bar<T> {
    private Foo<? super T> var1;
    private Foo<? super T> var2;

    public Bar(Foo<? super T> var1, Foo<? super T> var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public static void main(String[] args) {
        Foo<Integer> var1 = new Foo<>();
        Foo<Number> var2 = new Foo<>();
        Bar<Integer> b = new Bar<>(var1, var2); // this does now work
    }
}

有关该主题的更多信息,您可以阅读:什么是PECS(生产者扩展消费者超级)?


答案 2

利诺的答案很好。我想补充一个事实:

如果不必跟踪 s 的类型,而只需验证它们是否属于同一类型,则可以将 type 参数从类移动到其构造函数:varBar

class Bar {
    // These will always have the same type, but
    // that's not visible here
    private Foo<?> var1;
    private Foo<?> var2;

    // The parameter types of the construct ensures that
    // the vars are always of the same type
    public <T> Bar(Foo<T> var1, Foo<T> var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public static void main(String[] args) {
        Foo<String> var1 = new Foo<>();
        Foo<Integer> var2 = new Foo<>();

        Bar b1 = new Bar(var1, var1); // Works fine
        Bar b2 = new Bar(var1, var2); // Compilation error
    }
}

此技术适用于各种方法。


推荐