Java 泛型

首先,肘子假设各位读者朋友已经了解了泛型的基本概念了。


同样以Head First Java的习题开头(P576练习)
下面哪些语句可以通过编译么?

//Dog是Animal的子类
//[1]
ArrayList<Dog> dogs1 = new ArrayList<Animal>();
//[2]
ArrayList<Animal> animals1 = new ArrayList<Dog>();
//[3]
List<Animal> list = new ArrayList<Animal>();
//[4]
ArrayList<Dog> dogs = new ArrayList<Dog>();
//[5]
ArrayList<Dog> dogs = new ArrayList<Dog>();
ArrayList<Animal> animals = dogs;
//[6]
ArrayList<Dog> dogs = new ArrayList<Dog>();
List<Dog> dogList = dogs;
//[7]
ArrayList<Object> objects = new ArrayList<Object>();
List<Object> objList = objects;
//[8]
ArrayList<Object> objs = new ArrayList<Dog>();

想要解决问题需要理解好一下几个概念:

泛型,继承与子类型

如果class Dog extends class Animal,即类Dog继承类Animal,那么Dog对象可以赋值给Animal对象。比如String是Object的子类,Integer,Float是Number的子类,所以以下的赋值是合法的:

String str = "1234";
Object obj = str;

public void foo(Number n) {}
foo(new Integer(6));
foo(new Float(10.001));

且来看看ArrayList的源码:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, ...
        {
        ...
        }
//所以ArrayList<E>是AbstractList<E>,List<E>的子类
//ArrayList<Dog>的父类是AbstractList<Dog>和List<Dog>,而ArrayList<Dog>与ArrayList<Cat>都不是ArrayList<Animal>的子类,ArrayList<Dog>与ArrayList<Cat>的父类是Object

那么现在看回文章开头的习题:
所以能够通过编译的是[3][4][6][7][8]。

泛型命名规范

为了与java关键字区别开来,java泛型参数只是使用一个大写字母来定义。各种常用泛型参数的意义如下:
- E — Element,常用在java Collection里,如:List,Iterator,Set
- K,V — Key,Value,代表Map的键值对
- N — Number,数字
- T — Type,类型,如String,Integer等等
- S,U,V etc. - 2nd, 3rd, 4th 类型,和T的用法一样

参考:java泛型编程

泛型不协变(covariant)

说白了,就是数组与ArrayList的对比。

Java 语言中的数组是协变的(covariant),比如, Integer extends Number,那么不仅 Integer是 Number,而且 Integer[]也是 Number[],在要求 Number[]的地方完全可以传递或者赋予 Integer[]。(更正式地说,如果 Number是 Integer的超类型,那么 Number[]也是 Integer[]的超类型)。您也许认为这一原理同样适用于泛型类型 ——
List< Number>是 List< Integer>的超类型,那么可以在需要 List< Number>的地方传递 List< Integer>。不幸的是,情况并非如此。
不允许这样做有一个很充分的理由:这样做将破坏要提供的类型安全泛型。如果能够将 List< Integer>赋给 List< Number>。那么下面的代码就允许将非 Integer的内容放入 List< Integer>:

 List<Integer> li = new ArrayList<Integer>(); 
 List<Number> ln = li; // illegal 
 ln.add(new Float(3.1415));
 //ln是 List< Number>,所以向其添加 Float似乎是完全合法的。但是如果 ln是 li的别名,那么这就破坏了蕴含在 li定义中的类型安全承诺 —— 它是一个整数列表,这就是泛型类型不能协变的原因。

这也就很好解释下面的代码为什么编译不过了:

class Test {
    public void takeAnimals(Animal[] animals) {}
    public void takeAnimals(ArrayList<Animal> animals){}
    public void go() {
        Dog[] dogs = {new Dog(), new Dog(), new Dog()};
        Animal[] animals = {new Animal(), new Dog(), new Cat()};
        takeAnimals(dogs);
        takeAnimals(animals);

        ArrayList<Animal> animals2 = new ArrayList<>();
        animals.add(new Animal());
        animals.add(new Dog());
        animals.add(new Cat());
        takeAnimals(animals2);

        ArrayList<Dog> dogs2 = new ArrayList<>();
        dogs2.add(new Dog());
        dogs2.add(new Dog());
        takeAnimals(dogs2); //编译不过哦o(0-0)o
    }

}

引用:Java 理论和实践: 了解泛型

如何写泛型

网络上有很详细的讲解了,肘子觉得写得比较清楚的是
Java泛型:泛型类,泛型接口,泛型方法

但是肘子觉得比较实用的是在输出一些信息会用到泛型方法:

public class Test {
    public static <T> void print(T t) {
        System.out.println(t);
    }

    public static <T> void print(T... t) {
        for(T it: t)
            System.out.println(it);
    }
    public static void main(String[] args){
        print("hello world");
        print(123);
        print(12.09);
        print(false);

        print(123456, "hahhahah", true, 145.098);
    }
}

这个在写小程序的时候,要输出一些primitive数据类型的变量时是不是有点方便呢?

以上。
争取这周把书啃完。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值