简体   繁体   English

Java泛型绑定中的原始类型

[英]Raw type in Java generics bound

Are raw types allowed in generics bounds? 泛型范围中是否允许使用原始类型? What is their exact meaning? 它们的确切含义是什么?

class Foo<T>{}
class Bar<T extends Foo>{}

instead of writing 而不是写作

class Bar<U, T extends Foo<U>> {}

the second version is less convenient at the use site; 第二个版本在使用现场不太方便; can be the first considered somehow equivalent, albeit different? 可以认为第一个被认为是等效的,尽管有所不同吗?

class Bar<T extends Foo<?>> {}

is this the type-safe version of the first case? 这是第一种情况的类型安全版本吗?

Using raw types instead of wildcarded types as type parameter bounds doesn't change the type arguments allowed for the class ( Bar<Foo> , Bar<Foo<?>> , Bar<Foo<String>> , etc.), but it does affect the use of the type parameter within the class. 使用原始类型而不是通配符类型作为类型参数范围不会更改该类允许的类型参数( Bar<Foo>Bar<Foo<?>>Bar<Foo<String>>等),但是确实会影响类中type参数的使用。 For example, using List as the bound: 例如,使用List作为边界:

class Bar1<T extends List<?>> {
    void m(T t) {
        t.add("abc"); // type error
        t.add(123); // type error
    }
}

class Bar2<T extends List> { // warning about raw List
    void m(T t) {
        t.add("abc"); // warning about raw List
        t.add(123); // warning about raw List
    }
}

Since the type argument E of List<E> is unknown in both cases, it isn't safe to add anything to the list, and the first class correctly raises compiler errors. 由于在两种情况下都不知道List<E>的类型参数E ,因此将任何内容添加到列表中都是不安全的,并且第一个类正确地引发了编译器错误。 However, using a raw type bound disables this type checking and treats the add(E) method as add(Object) , allowing anything. 但是,使用原始类型绑定将禁用此类型检查,并将add(E)方法视为add(Object) ,允许任何操作。 The problem becomes apparent when a List<Integer> is passed to the second class: List<Integer>传递给第二个类时,该问题变得很明显:

Bar2<List<Integer>> bar2 = new Bar2<>();
List<Integer> list = new ArrayList<>();
bar2.m(list);
System.out.println(list); // prints [abc, 123]
Integer i = list.get(0); // throws ClassCastException

The List<Integer> ends up with a String element, leading to a ClassCastException at runtime when it tries to set the String to an Integer . List<Integer>String元素结尾,当它尝试将String设置为Integer时,在运行时导致ClassCastException。 This is why raw types are dangerous. 这就是为什么原始类型很危险的原因。

Are raw types allowed in generics bounds? 泛型范围中是否允许使用原始类型? What is their exact meaning? 它们的确切含义是什么?

Yes, they are allowed in generics bounds. 是的,它们在通用范围内是允许的。 They mean exactly the same thing as raw types mean elsewhere. 它们的含义与原始类型在其他地方的含义完全相同。

To quote user John Feminella : 引用用户John Feminella


  • List : A list with no type parameter. List :没有类型参数的列表。 It is a list whose elements are of any type -- the elements may be of different types. 它是一个列表,其元素可以是任何类型-元素可以是不同类型。
  • List<?> : A list with an unbounded type parameter. List<?> :具有无限制类型参数的列表。 Its elements are of a specific, but unknown, type; 它的元素是特定但未知的类型。 the elements must all be the same type. 元素必须都是相同的类型。
  • List<T extends E> : A list with a type parameter called T. The supplied type for T must be of a type that extends E, or it is not a valid type for the parameter. List<T extends E> :带有类型参数T的列表。T的提供类型必须是扩展E的类型,否则不是该参数的有效类型。

The general purpose of generic types is to cause compile errors rather than ClassCastExceptions at runtime. 泛型类型的一般用途是在运行时导致编译错误,而不是ClassCastExceptions。

Here's a bunch of examples with comments. 这是一堆带有注释的示例。 Let me know if you have questions. 如果您有任何问题,请告诉我。

class Foo<U> extends ArrayList<U> {}

class BarRaw<T extends Foo> {
    void doSomething(T t) {
        // all you know is that `t` extends ArrayList holding any kinds of Objects

        // raw types are dangerous because..
        // the types are only checked at runtime, rather than compile time.

        // warnings about raw types, but no compile error
        t.set(0, Integer.valueOf(1));
        t.set(1, "someString");

        Object obj0 = t.get(0); // obj0 is truly an `Integer`
        Object obj1 = t.get(1); // obj1 is truly a `String`

        // valid, casting a true `Integer` to a `Integer`
        Integer int0 = (Integer) t.get(0);

        // valid, but ClassCastException at runtime! Casting a true `String` to a `Integer`
        Integer int1 = (Integer) t.get(1);
    }
}

class BarParam1<U, T extends Foo<U>> {
    void doSomething(T t, U u) {
        // `t` extends ... `ArrayList<U>`, can only add elements of type `U`
        // and the elements you get are guaranteed to be of type `U`

        t.set(0, u); // valid
        t.set(1, new Object()); // compile err, can only set elements of type `U`
        t.set(1, (U) new Object()); // valid, but ClassCastException at runtime
        t.set(2, Integer.valueOf(0)); // compile err, can only set elements of type `U`

        U u0 = t.get(0); // valid
        Object obj0 = t.get(0); // valid, any Object can be an Object..

        Integer int0 = t.get(0); // compile err, can't convert from `U` to `Integer`
        Integer int1 = (Integer) t.get(0); // valid but DANGER

        if (obj0 instanceof Integer) {
            Integer int2 = (Integer) obj0; // valid and safe since you checked
        }
    }
}

class BarParam2<U extends Number, T extends Foo<U>> {
    void doSomething(T t, U u) {
        // `T` extends ... `ArrayList<U extends Number>`
        // can only add elements of type `U extends Number`
        // and the elements you get are guaranteed to be of type `U extends Number`

        t.set(0, u); // valid
        t.set(1, new Object()); // compile err, can only set elements of type `U` exactly
        t.set(1, (U) new Object()); // valid, but ClassCastException at runtime
        t.set(2, Integer.valueOf(0)); // compile err, can only set elements of type `U` exactly

        U u0 = t.get(0); // valid
        Object obj0 = t.get(0); // valid, any Object can be an Object..

        Integer int0 = t.get(0); // compile err, can't convert from `U` to `Integer`
        Number num0 = t.get(0); // valid, `U` is guaranteed to extend `Number`
        Integer int1 = (Integer) t.get(0); // valid but DANGER

        if (obj0 instanceof Integer) {
            Integer int2 = (Integer) obj0; // valid and safe since you checked
        }
    }
}

class BarWild1<U, T extends Foo<?>> {
    void doSomething(T t, Number u) {
        // all compile err, no idea what `?` is
        t.set(0, u);
        t.set(1, new Object());
        t.set(1, (String) new Object());

        String u0 = t.get(0); // compile err, no idea what `?` is other than some Object
        Object obj0 = t.get(0); // valid, `?` extends `Object` since all objects do.
    }
}

class BarWild2<T extends Foo<? extends Number>> {
    void doSomething(T t, Number u) {
        // `t` extends ... `ArrayList<? extends Number>`
        // can only add elements of type `? extends Number`
        // and the elements you get are guaranteed to be of type `? extends Number`

        // all compile err, no idea what exact type `?` is
        t.set(0, u);
        t.set(1, new Object());
        t.set(1, (Number) new Object());
        t.set(2, Integer.valueOf(0));

        Number num0 = t.get(0); // valid, we know that the elements extend `Number`
        Object obj0 = t.get(0); // valid, any Object can be an Object..

        Integer int0 = t.get(0); // compile err, all we know is that the elements extend `Number`
        Integer int1 = (Integer) t.get(0); // valid but DANGER

        if (obj0 instanceof Integer) {
            Integer int2 = (Integer) obj0; // valid and safe since you checked
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM