【尚硅谷_java基础】十三番外:泛型补充

参考资料

  1. https://www.bilibili.com/video/BV1Kb411W75N?p=562

1.介绍

  • 不使用泛型下的用法:

    List myIntList = new ArrayList();// 1
    myIntList.add(new Integer(0));// 2
    Integer x = (Integer) myIntList.iterator().next();// 3
    

    通常情况下,程序员知道一个特定的 list里边放的是什么类型的数据。但是,这个类型转换是必须的(essential)。编译器只能保证 iterator返回的是 Object类型。为了保证对 Integer 类型变量赋值的类型安全,必须进行类型转换

  • 修改后:

    List<Integer> myIntList = new ArrayList<Integer>(); // 1
    myIntList.add(new Integer(0)); // 2
    Integer x = myIntList.iterator().next(); // 3
    

2. 泛型和子类继承

下面的代码片断合法么?

List<String> ls = new ArrayList<String>(); //1
List<Object> lo = ls; //2
lo.add(new Object()); // 3
String s = ls.get(0); // 4: 试图把 Object 赋值给 String

第 1 行当然合法,但是这个问题的狡猾之处在于第 2 行。
这产生一个问题:
一个 String 的 List 是一个 Object 的 List 么?大多数人的直觉是回答:“当然!”。

这里,我们使用lo 指向ls。我们通过lo 来访问ls,一个 String 的 list。我们可以插入任意对象进去。结果是ls 中保存的不再是 String。当我们试图从中取出元素的时候,会得到意外的结果。java 编译器当然会阻止这种情况的发生。第 2 行会导致一个编译错误。总之,如果 Foo 是 Bar 的一个子类型(子类或者子接口),而 G 是某种泛型声明,那么G<Foo>是 G<Bar>的子类型并不成立

3. 通配符(Wildcards)

  • 将任意元素加入到其中不是类型安全的:

    Collection<?> c = new ArrayList<String>();
    c.add(new Object()); // 编译时错误
    

    因为我们不知道 c 的元素类型,我们不能向其中添加对象。add 方法有类型参数 E 作为集合的元素类型。我们传给 add 的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。唯一的例外是 null,它是所有类型的成员。另一方面,我们可以调用 get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个 Object

  • List<? extends Shape>是有限制通配符的一个例子。这里代表一个未知的类型,就像我们前面看到的通配符一样。但是,在这里,我们知道这个未知的类型实际上是 Shape 的一个子类。我们说 Shape 是这个通配符的上限(upper bound)。像平常一样,要得到使用通配符的灵活性有些代价。这个代价是,现在向 shapes 中写入是非法的。比如下面的代码是不允许的:

    public void addRectangle(List<? extends Shape> shapes) {
    	shapes.add(0, new Rectangle()); // compile-time error!
    }
    

    因为 shapes.add 的第二个参数类型是 ? extends Shape ——一个 Shape 未知的子类。因此我们不知道这个类型是什么,我们不知道它是不是 Rectangle 的父类;它可能是也可能不是一个父类,所以这里传递一个 Rectangle 不安全。

4. 泛型方法

考虑写一个方法,它用一个 Object 的数组和一个 collection 作为参数,完成把数组中所有 object 放入 collection 中的功能。
下面是第一次尝试:

static void fromArrayToCollection(Object[] a, Collection<?> c) {
	for (Object o : a) {
	c.add(o); // 编译期错误
	}
}

现在,你应该能够学会避免初学者试图使用 Collection<Object>作为集合参数类型的错误了。或许你已经意识到使用Collection<?>也不能工作。回忆一下,你不能把对象放进一个未知类型的集合中去。解决这个问题的办法是使用 generic methods。 就像类型声明,方法的声明也可以被泛型化——就是说,带有一个或者多个类型参数。

static <T> void fromArrayToCollection(T[] a, Collection<T> c){
	for (T o : a) {
		c.add(o); // correct
	}
}

我们可以使用任意集合来调用这个方法,只要其元素的类型是数组的元素类型的父类。

Object[] oa = new Object[100];
Collection<Object> co = new ArrayList<Object>();
fromArrayToCollection(oa, co);// T 指 Object
String[] sa = new String[100];
Collection<String> cs = new ArrayList<String>();
fromArrayToCollection(sa, cs);// T inferred to be String
fromArrayToCollection(sa, co);// T inferred to be Object
Integer[] ia = new Integer[100];
Float[] fa = new Float[100];
Number[] na = new Number[100];
Collection<Number> cn = new ArrayList<Number>();
fromArrayToCollection(ia, cn);// T inferred to be Number
fromArrayToCollection(fa, cn);// T inferred to be NumberfromArrayToCollection(na, cn);// T inferred to be Number
fromArrayToCollection(na, co);// T inferred to be Object
fromArrayToCollection(na, cs);// compile-time error

注意,我们并没有传送真实类型参数(actual type argument)给一个泛型方法。编译器根据实参为我们推断类型参数的值。它通常推断出能使调用类型正确的最明确的类型参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CHH3213

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值