第十五章:泛型(下)

  • 我发现我记得太过详细了,接下来因为有其他书要看,我决定记录的稍微简单一点。
    ——————–分割线——————-

泛型

通配符

  • 关于通配符之前在类型信息那一块也提到过,下面就简单介绍一下。
//Type mismatch: cannot convert from ArrayList<String> to List<Object>
List<Object> list = new ArrayList<String>();
  • 我们以为ObjectString存在继承关系,故写出了上面的代码,殊不知其会导致编译错误,原因为何,看一下下面关于数组的例子。
Object[] objs = new String[10];
objs[0] = new Integer(0);
  • 这两句代码是能通过编译的,但是执行的时候会抛CCE异常,因为objs的本质是String数组而不是Object数组。类比于上一个例子。如果我们能将一个泛型指定为String的对象赋给一个泛型指定为Object的引用,那么是否意味着list可以往其插入Integer数据?这势必会引起错误。
  • 用白话描述一下,ArrayList<String>是一个将持有String类型(如果有子类当然也可以持有,不过它是final的)的集合,List<Object>是一个将持有Object或其子类的集合。虽然他们持有的类型具有继承关系或者说包含关系,但是他们本身没有这种继承关系,也就不能使用向上转型的这种写法。
  • 不过有一种类似的写法可以做到这种赋值,那就是使用通配符
List<?> list = new ArrayList<String>();
List<? extends Object> list2 = new ArrayList<String>();
  • 上面这两种写法在这里是一个效果,但是编译器对于他们的处理方式却稍有差异。在这里都是一个意思,即list持有的类型可以是Object其任何子类型。听起来好像没有区别,但最大的区别就是它的持有类型是未知的。我们也可以使用super关键字:
List<? super String> list2 = new ArrayList<String>();
  • 在这里通配符代表的就是String类型或其父类型
  • 接下里我们说一下这个未知是什么意思。
List<?> list = new ArrayList<String>();
list.add(new String());//error
list.add(new Object());//甚至连Object都无法加了
  • 当你使用通配符后,任何形参类型为泛型的方法都无法正确调用了。啥意思?其实很好理解。list在编译器的认知里是一个持有Object其子类的一种集合。那它到底是StringIntegerObject就无从得知。所以就不允许你直接认定泛型为String,因为这有可能导致错误。
  • 相比较于extendssuper的用法就稍微宽一点。
List<? super String> list = new ArrayList<String>();
list.add(new String());
  • 因为list的类型不管是String的任何父类型,它都是能够接收String的,所以这么写就没有毛病。
  • 既然使用通配符造成了这么大的限制,那是不是可以说它没什么实际意义呢?当然不是的。1、对于那些参数类型不包含泛型的方法,我们依然能够正确调用。2、有时能帮我们写出更通用的方法。
public class Solution {
    public static void main(String args[]) {
        Colle<String> str = new Colle<String>();
        str.set(new String("123"));
        Colle<Integer> i = new Colle<Integer>();
        i.set(new Integer(1));

        test(str);
        test(i);
    }
    public static void test(Colle<?> test) {
        System.out.println(test.get());
    }
}
class Colle <T> {
    T obj;
    public T get() {
        return obj;
    }
    public void set(T obj) {
        this.obj = obj;
    }
}
  • 通过通配符,我们就能提取一个公共方法test(),这样无论其持有什么类型,我们都能够正确的调用get方法(set的确是无法使用了)。
  • 其实关于不加泛型<?><? extends Object>还有一点点的区别,不过我觉得这种东西没必要那么过分深入研究(因为使用才是最好的锻炼),这里就省略了。也许我以后去读关于框架的书籍会重新研究一下这个。

自限定的泛型

  • 来看下面的代码:
public class Solution {
    public static void main(String ar[]) {
        Holder h1 = new Holder();
        Holder h2 = new Holder();
        h1.set(h2);
        Holder h3 = h1.get();
    }
}
class Holder extends Colle<Holder> {

}
class Colle <T> {
    T obj;
    public T get() {
        return obj;
    }
    public void set(T obj) {
        this.obj = obj;
    }
}
  • 这里不做解析了。

动态类型安全

  • 来看下面的代码:
public class Solution {
    public static void main(String ar[]) {
        List list1 = new ArrayList<String>();
        list1.add(new String("2"));
        list1.add(new Integer(1));
        List list2 = Collections.checkedList(new ArrayList<String>(), String.class);
        list2.add(new String("2"));
        //list2.add(new Integer(1));//error
    }
}
  • 由于类型擦除的原因,导致前三句执行时并不会报错。但是这并非我们所愿。因为list1本质是一个ArrayList<String>,我们自然是不希望它能够插入其他类型的。除了将List指定为List<String>之外,还有上面这样的另外一种做法。第六句会抛出一个CCE异常。除了checkedList之外,Collections还有很多类似的方法,这里就不介绍了。

小结

  • 其实关于泛型本章后面还有大量内容,由于时间原因,我只是粗读了一下,就没有再记下来了。有兴趣的可以去看原书。
  • 其实泛型带来的好处是使代码可读性更高,而安全不过是其额外的副作用而已。
  • 下面这个例子是我对泛型的一个理解总结。这是我在编写第十六章读后感时想到的。
public class People {
    public static void main(String args[]) {
        Holder<Integer> ho1 = new Holder<Integer>();
        Holder<String> ho2 = new Holder<String>();

        Holder ho11 = ho1;
        Holder ho22 = ho2;
        Holder<String> ho111 = ho11;
        Holder<Integer> ho222 = ho22;
        ho111.set(new String("1"));
        ho222.set(new Integer(1));

        System.out.println(ho111.get());
        System.out.println(ho222.get());
    }
}
class Holder <T> {
    T obj;
    T get() {
        return obj;
    }
    void set(T obj) {
        this.obj = obj;
    }
}
----------------运行结果
1
1
  • 你也许会以为new Holder<Integer>()new Holder<String>()是两种不同的类型。但是事实告诉我们,我们试图往前者插入String,不会导致任何错误。这是为什么呢?因为在Holder内部,它只认为泛型是它的擦除边界Object而已。而真正能够限制我们的编译期间ho111此刻认为的泛型。我们当然不会这么写代码,此处也是为了让我们深刻的理解泛型的作用。再次强调,在泛型类内部,所有的泛型类型是擦除边界!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值