使用“超级”关键字限定泛型

2022-08-31 14:46:54

为什么我只能与通配符一起使用,而不能与类型参数一起使用?super

例如,在接口中,为什么方法不是这样编写的CollectiontoArray

interface Collection<T>{
    <S super T> S[] toArray(S[] a);
}

答案 1

super绑定命名类型参数(例如 )而不是通配符(例如)是非法的,因为即使它被允许,它也不会做你希望它能做的事,因为因为是所有引用类型的终极,而且一切都是一个,实际上没有界限<S super T><? super T>ObjectsuperObject

在您的特定示例中,由于任何引用类型的数组都是(通过 Java 数组协方差),因此它可以在编译时用作(如果此类绑定是合法的)的参数,并且在运行时不会阻止。Object[]<S super T> S[] toArray(S[] a)ArrayStoreException

你试图提出的是:给定:

List<Integer> integerList;

并给出这个假设的界限:supertoArray

<S super T> S[] toArray(S[] a) // hypothetical! currently illegal in Java

编译器应仅允许编译以下内容:

integerList.toArray(new Integer[0]) // works fine!
integerList.toArray(new Number[0])  // works fine!
integerList.toArray(new Object[0])  // works fine!

并且没有其他数组类型参数(因为只有这 3 种类型作为 )。也就是说,您正在尝试阻止它编译:Integersuper

integerList.toArray(new String[0])  // trying to prevent this from compiling

因为,根据你的论点,不是 .但是,是 a 的 ,而 a 是一个 ,所以编译器仍然会让上面的编译,即使假设你可以做到!StringsuperIntegerObjectsuperIntegerString[]Object[]<S super T>

因此,以下内容仍将编译(就像它们现在的方式一样),并且在运行时无法通过使用泛型类型边界进行任何编译时检查来阻止:ArrayStoreException

integerList.toArray(new String[0])  // compiles fine!
// throws ArrayStoreException at run-time

泛型和数组不会混合,这是它显示的众多地方之一。


非数组示例

同样,假设您有这个泛型方法声明:

<T super Integer> void add(T number) // hypothetical! currently illegal in Java

你有这些变量声明:

Integer anInteger
Number aNumber
Object anObject
String aString

你的意图(如果它是合法的)是它应该允许,和,当然,但不是。好吧,是一个,所以无论如何都会编译。<T super Integer>add(anInteger)add(aNumber)add(anObject)add(aString)StringObjectadd(aString)


另请参见

相关问题

在泛型类型规则上:

关于使用和:superextends


答案 2

由于没有人提供令人满意的答案,正确的答案似乎是“无缘无故”。

polygenelubricants很好地概述了java数组协方差发生的坏事,这本身就是一个可怕的特征。请考虑以下代码片段:

String[] strings = new String[1];
Object[] objects = strings;
objects[0] = 0;

这种明显错误的代码在没有诉诸任何“超级”构造的情况下进行编译,因此数组协方差不应用作参数。

现在,在这里,我有一个完全有效的代码示例,需要在命名的type参数中:super

class Nullable<A> {
    private A value;
    // Does not compile!!
    public <B super A> B withDefault(B defaultValue) {
        return value == null ? defaultValue : value;
    }
}

可能支持一些不错的用法:

Nullable<Integer> intOrNull = ...;
Integer i = intOrNull.withDefault(8);
Number n = intOrNull.withDefault(3.5);
Object o = intOrNull.withDefault("What's so bad about a String here?");

如果我完全删除,后一个代码片段不会编译,因此确实需要。BB

请注意,如果我反转类型参数声明的顺序,则我尝试实现的功能很容易获得,从而将约束更改为 。但是,只有当我将方法重写为静态方法时,这才有可能:superextends

// This one actually works and I use it.
public static <B, A extends B> B withDefault(Nullable<A> nullable, B defaultValue) { ... }

关键是,这种Java语言限制确实限制了一些其他可能的有用功能,并且可能需要丑陋的解决方法。我想知道,如果我们需要虚拟化,会发生什么。withDefault

现在,为了与多基因elubricants所说的内容相关联,我们在这里使用不是为了限制作为传递的对象的类型(参见示例中使用的字符串),而是限制调用方对我们返回的对象的期望。作为一个简单的规则,您可以与所需的类型和您提供的类型一起使用。BdefaultValueextendssuper


推荐