泛型
简介
java中的可变类型都是使用通配符(通常为大写字母)代替,例如public interface test<T>
,其中的T就是通配符,只起到占位符的作用,可以是任意的大写字母。然而通配符又可分为上界通配符和下界通配符,具体如下:
-
上界:<? extends T>
-
下界:<? super T>
现在有动物Animal类、Dog类、Husky类 ,下面以这三个类为例进行叙述,三个类的详细信息如下:
// 动物类 public class Animal { private String type; public Animal(String type) { this.type = type; } public String getType() { return type; } public void setType(String type) { this.type = type; } @Override public String toString() { return "Animal{" + "type='" + type + '\'' + '}'; } } // 狗类 public class Dog extends Animal{ private String name; public Dog(String type, String name) { super(type); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + '}'; } } // 哈士奇类 public class Husky extends Dog{ private Integer age; public Husky(String type, String name, Integer age) { super(type, name); this.age = age; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Husky{" + "age=" + age + '}'; } }
上界通配符
注:以下所说的“当前类”皆指List中通配符的实例,即List<? extends T>
中的T
特性
- 上界通配符实例化的类必须是当前类,或是当前类的子类
- 接收集合中的数据的数据类型只能是当前类,或是当前类的超类
- 存数据时只能存入null
代码演示:
// 1.extends实例化的只能是当前类,或者是当前类的子类
List<? extends Dog> dogList = new ArrayList<Dog>(){{
add(new Dog("哺乳动物","大黄"));
}};
List<? extends Dog> d = new ArrayList<Husky>();
// List<? extends Dog> d2 = new ArrayList<Animal>();
// 2.extends取数据时,接收数据的对象只能是当前类或当前类的超类
Dog dog = dogList.get(0);
Animal dog2 = dogList.get(0);
//Husky husky = dogList.get(0);// 只能用泛型类或是泛型的超类接收数据,这也符合多态的语法规范
// 3.存数据是只能存入null
//dogList.add(new Dog("哺乳动物","哈士奇"));// 存数据失败
dogList.add(null);
分析
所谓”上界通配符“就指当前类为集合中存储的数据类型的上限,根据java多态的性质可知,存储数据时只能存储当前类或者是当前类的子类。然而取数据的时候,并不确定存入的是当前类还是子类,所以只能用当前类或者当前类的超类(父类)接收获取的数据。在存入数据时由于不确定实例化的类是当前类还是当前类的子类,所以无法存入,从而只能存入null。
下界通配符
特性
- 下界通配符实例化的类必须是当前类,或是当前类的超类
- 存储数据时数据类型只能是当前类,或是当前类的超类
- 取数据时只能使用Object接收数据
代码演示:
// 1.super下实例化的只能是当前类或者是当前类的超类
List<? super Dog> dogList1 = new ArrayList<Animal>();
//List<Dog> a = new ArrayList<>(Animal);// 正常写法时验证是错误的,符合语法规定
List<? super Dog> dogList2 = new ArrayList<Dog>();
// 2.存数据时可以存当前类,或者是当前类的子类
dogList1.add(new Dog("哺乳动物","憨豆"));
dogList1.add(new Husky("哺乳动物","地主",2));
//dogList1.add(new Animal("哺乳动物"));// 编译失败
//Dog d1 = dogList1.get(0);
// 3.取数据时只能使用当前类
//Dog dog1 = dogList1.get(0);// 编译失败
// Animal animal = dogList1.get(0);// 编译失败
Object obj1 = dogList1.get(0);
Object obj2 = dogList1.get(1);
Dog a = (Dog) obj1;
Husky b = (Husky) obj2;
System.out.println(a);
System.out.println(b);
分析
所谓“下界通配符”就指当前类为集合中存储的数据类型的下限,既然是下限则实例化时传入的对象只能是当前类或是当前类的超类。由于不知道实例化的是当前类还是当前类的超类,所以存数据时只能存入当前类或是当前类的子类。同理取数据时同样由于无法确定实例化时的数据类型,所以只能用Object类型接收数据。
总结(个人见解)
上界通配符和下界通配符都可以狭隘的理解为实例化集合时逻辑限定范围,弄明白这点,取数据和存数据自然的很轻易的就能理解了,从而也可以自行推导出来(纯属个人见解,如有说的不对的地方望海涵,指正)。