Java泛型中的原始类型绑定

时间:2018-03-30 11:42:29

标签: java generics

仿制药是否允许原始类型?它们的确切含义是什么?

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

而不是写

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

第二个版本在使用网站不太方便;可能是第一个被认为是等价的,虽然不同?

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

这是第一种情况的类型安全版本吗?

2 个答案:

答案 0 :(得分:0)

使用原始类型而不是通配类型作为类型参数边界不会更改类允许的类型参数(Bar<Foo>Bar<Foo<?>>Bar<Foo<String>>等),但它确实会影响类中类型参数的使用。例如,使用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
    }
}

由于E的类型参数List<E>在两种情况下都是未知的,因此向列表中添加任何内容都是不安全的,并且第一个类正确地引发了编译器错误。但是,使用原始类型绑定会禁用此类型检查并将add(E)方法视为add(Object),允许任何操作。当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

List<Integer>String元素结束,在尝试将String设置为Integer时,在运行时导致ClassCastException。这就是为什么原始类型是危险的。

答案 1 :(得分:0)

  

仿制药是否允许原始类型?它们的确切含义是什么?

是的,他们被允许进入泛型界限。它们的意思与原始类型在其他地方完全相同。

引用用户John Feminella

  • List:没有类型参数的列表。它是一个元素所在的列表 任何类型的元素 - 元素可能是不同类型的。
  • List<?>:具有无界类型参数的列表。它的元素是一个 具体但未知的类型;元素必须都是相同的类型。
  • List<T extends E>:一个名为T的类型参数的列表 T的提供类型必须是扩展E的类型,或者它不是参数的有效类型。

泛型类型的一般目的是在运行时导致编译错误而不是ClassCastExceptions。

以下是一些带注释的示例。如果您有疑问,请告诉我。

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