目录
介绍
JAVA推出泛型以前,程序员可以构建一个元素类型为Object的集合,该集合能够存储任意的数据类型对象,而在使用该集合的过程中,需要程序员明确知道存储每个元素的数据类型,否则很容易引发ClassCastException异常。
Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构。
泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。
优点
类型安全
消除了强制类型的转换
泛型类
语法定义
代码示例
// <T> 泛型标识--类型形参 T创建对象的时候指定具体的数据类型
public class Generic<T> {
//T 外部创建使用类的时候来指定 可以理解为 谁用谁定义
private T key;
public Generic() {}
public Generic(T key) {
this.key = key;
}
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
@Override
public String toString() {
return "Generic{" +
"key=" + key +
'}';
}
}
测试:
注意泛型类不支持基本数据类型 这点在编译时就会有报警提示
泛型类注意事项
抽奖示例
public class ProdocGetter<T> {
Random random = new Random();
//奖品
private T product;
//奖品池
List<T> list = new ArrayList<>();
//添加奖品
public void addPro(T t){
list.add(t);
}
//随机抽奖
public T getProduct(){
return list.get(random.nextInt(list.size()));
}
public static void main(String[] args) {
//创建抽奖器对象 指定奖品类型(数据类型)
ProdocGetter<String> stringProdocGetter = new ProdocGetter<>();
String[] strings = {"苹果电脑","小米手机","奔驰汽车","迪迦奥特曼玩具"};
//奖品池塞入奖品
for (String pro : strings) {
stringProdocGetter.addPro(pro);
}
//抽奖
System.out.println("恭喜您,抽中了:"+stringProdocGetter.getProduct());
System.out.println("分割线====================================");
ProdocGetter<Integer> intProdocGetter = new ProdocGetter<>();
Integer[] integers = {100,500,9000,16645,30458};
//奖品池塞入奖品
for (Integer pro : integers) {
intProdocGetter.addPro(pro);
}
//抽奖
System.out.println("恭喜您,抽中了:"+intProdocGetter.getProduct()+"元");
}
}
测试:
泛型类派生子类
定义
代码示例
子类是泛型
父类泛型
public class Parent<E> {
private E value;
public E getValue() {
return value;
}
public void setValue(E value) {
this.value = value;
}
}
子类泛型
//泛型类派生子类,子类也是泛型类,那么子类的泛型标识要和父类一致。
public class Child<T> extends Parent<T>{
@Override
public T getValue() {
return super.getValue();
}
public static void main(String[] args) {
Child<String> child = new Child<>();
child.setValue("我是一个字符串");
System.out.println(child.getValue());
}
}
测试:
注意此时的子类泛型是T,继承的父类也是T,而最原先定义的父类泛型是E
子类如果是泛型,那么必须指定父类泛型和子类是一致的
如果不一致:
子类不是泛型
//泛型类派生子类 如果子类不是泛型,那么父类需要明确指定数据类型 如果不指定那么数据类型则为Object
public class ChildSecond extends Parent<Integer>{
@Override
public Integer getValue() {
return super.getValue();
}
@Override
public void setValue(Integer value) {
super.setValue(value);
}
public static void main(String[] args) {
ChildSecond childSecond = new ChildSecond();
childSecond.setValue(1000);
System.out.println(childSecond.getValue());
}
}
测试:
如果不明确指定父类的数据类型
泛型接口
定义
泛型接口的代码示例和泛型类派生子类如出一辙,不再代码示例
泛型方法
定义
代码示例
还是以之前的例子,但是这里需要多定义一个泛型方法
public class ProdocGetter<T> {
Random random = new Random();
//奖品
private T product;
//奖品池
List<T> list = new ArrayList<>();
//添加奖品
public void addPro(T t){
list.add(t);
}
//随机抽奖
public T getProduct(){
return list.get(random.nextInt(list.size()));
}
//定义泛型方法
public <E> E getProduct(List<E> list){
return list.get(random.nextInt(list.size()));
}
public static void main(String[] args) {
ProdocGetter<Integer> prodocGetter = new ProdocGetter<>();
List<String> strList = new ArrayList<>();
strList.add("华为手机");
strList.add("苹果手表");
strList.add("空调");
//泛型方法的调用 类型是通过调用方法的时候来指定
String procut = prodocGetter.getProduct(strList);
System.out.println(procut+"\t"+procut.getClass().getSimpleName());
System.out.println("分割线=============================================>");
ArrayList<Integer> integers = new ArrayList<>();
integers.add(100);
integers.add(30004);
integers.add(6471);
Integer integer = prodocGetter.getProduct(integers);
System.out.println(integer+"\t"+integer.getClass().getSimpleName());
}
}
测试:
定义多个泛型类型的静态泛型方法
泛型方法与可变参数
泛型方法总结
类型通配符
定义
代码示例
我们先看下数据类型Integer和Number的关系
点进Integer可以看到Integer是继承了Number
根据原先多态的想法,我们可以这样定义:
但是这里却报错了
也就是说泛型这里不可以使用多态来进一步转换类型数据,这个时候类型通配符的作用就显现出来了
通配符的上限
定义
代码示例
书写三个继承关系类,Animal->BIgDog->Dog
Animal:
public class Animal {
}
BIgDog:
public class BIgDog extends Animal{
}
Dog:
public class Dog extends BIgDog{
}
测试:
本次没有测试演示,可以看到此时定义的通配符上限类为BigDog,所以showAnimal中传递的集合数据类型只能由BigDog或者BigDog的子类,BigDog的父类Animal已经报警不可用
需要注意的是通配符上限的集合是不允许填充实例数据的,比如:
因为此时定义了通配符的上限类,而集合根本不确定集合中存的数据是什么数据类型
通配符的下限
定义
代码示例
可以看到此时定义的通配符下限类为BigDog,所以showAnimals中传递的集合数据类型只能由BigDog或者BigDog的父类,BigDog的子类Dog已经报警不可用
通配符下限这里集合是可以直接添加实例的
jdk中下限通配符的使用
改造三个类
Animal
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
'}';
}
}
BigDog
public class BIgDog extends Animal{
public Integer age;
public BIgDog(String name, Integer age) {
super(name);
this.age = age;
}
@Override
public String toString() {
return "BIgDog{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
Dog
public class Dog extends BIgDog{
public Integer level;
public Dog(String name, Integer age, Integer level) {
super(name, age);
this.level = level;
}
@Override
public String toString() {
return "Dog{" +
"level=" + level +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
TreeSet中的有一个构造函数就是典型的通配符下限使用
测试:
类型擦除