欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java泛型

程序员文章站 2022-06-15 14:39:08
...


1. 什么是泛型

在推出泛型之前,程序员通过构建元素类型为Object的集合,存储多个任意类型的数据对象;在使用该对象的过程中,程序员需要明确知道存储每个元素的数据类型(几乎不可能),否则会引发ClassCastException。

显然,类型的优点:
在不知道集合存储类型的前提下,泛型能够统一集合的元素类型——类型安全消除通过Object集合带来的强制类型转换

泛型的本质就是参数化类型,将所操作的数据类型指定为一个参数,实现动态更改。

2. 泛型类、接口

定义语法

class 类名<泛型标识,泛型标识,...>{
	private 泛型标识 变量名;
}

常用的泛型标识:

? 表示不确定的java类型
T (type) 表示具体的一个java类型
K V (key value) 分别代表java键值中的Key Value
E (element) 代表Element

如果泛型类在new的时候没有指定泛型类型,将默认使用Object来操作。

泛型类型不支持基本类型。

同一个泛型类,不同的泛型参数,本质上还是同一类型=>泛型类本身

// intGeneric是<Integer>,strGeneric是<String>
System.out.println(intGeneric.getClass()==strGenenric.getClass())  // true
// intGeneric.getClass()  ==>Generic

因此可知

  • 指定泛型类型,不支持多态
    public static test(Box<Number> box){}
    
    main(){
    	// 合法
    	Box<Number> box1;
    	// 尽管Integer instanceOf Number依旧不合法
    	// 因为本质上这都是指向同一个Box类
    	Box<Integer> box2;
    }
    
  • 指定泛型类型,不支持重载
    // 两个其中一个会报错
    // 因为他们本质上都是指向同一个Box类,因此不满足重载
    public static test(Box<Number> box){};
    public static test(Box<Integer> box){}
    

如果使用泛型通配符,支持重载和多态


从泛型类派生子类

  • 子类也是泛型类,则子类泛型标识至少要包含父类的泛型标识
    class child<T,E,K> extends father<T>{}
    
  • 子类不是泛型类,则父类的泛型类型需要显式声明
    class child extends father<String>{}
    
    此时子类使用父类的方法,方法的返回值都会变成明确的类型

接口

实现类的实现规则跟派生子类的规则一致。

3. 泛型方法

定义格式

修饰符 <T,E,...> 返回值类型 方法名(形参){}
  • 修饰符与返回值中间的<T>非常重要,可以理解为声明该方法为泛型方法
  • 只有声明了<T>的方法才是泛型方法,泛型类中使用了泛型的成员方法不是泛型方法
  • <T>相当于声明,该方法中要使用泛型T

实例:
传入一个T类型的集合,随机返回一个T

public <T> T test(List<T> list){
	return list.get(random.nextInt(list.size()));
}

泛型类中的泛型方法的泛型标识符优先级高于泛型类
如果泛型类的泛型标识为T,泛型类中的泛型方法的标识也为T,当我们new出泛型类,指定泛型为Integer之后,再调用泛型方法,并指定泛型为String,依旧是可以的。

泛型类中使用的泛型类的成员方法,不能作为静态方法,但是泛型方法可以。


可变参数

public <E> void print(E... es){
	for(E e:es){
		sout(e);
	}
}

4. 类型通配符

类型通配符一般是?,代替具体的类型实参==>类型通配符是实参,不是形参。

类型通配符可以实现重载和多态。

类型通配上限
指定通配上限为Number,此时通配时可以使用Number及其子类

public static void test(Box<? extends Number> boxs){
	Integer box = boxs.getFirst();
}

类型通配下限
指定通配必须是该类型及其父类。

public static void test(Box<? super Number> boxs){
  	//Integer box = boxs.getFirst();
  	Object  box = boxs.getFirst();
}

5. 泛型擦除

泛型只作用于编译阶段,编译后泛型会被擦除,因此泛型能够与jdk1.5之前的版本兼容。

无限制类型擦除
Java泛型

如果仅仅是<T>,则统统擦除为Object

有限制类型擦除
Java泛型

如果存在上限,则擦除为上限

桥接方法
Java泛型

父类会按无限制擦除规则擦除,子类会生成一个桥接方法。

6. 泛型和数组

  • 可以声明带泛型的数组引用,但不能直接创建带泛型的数组对象
    这样不被允许
    List<String>[] listArr = new ArrayList<>[5];
    等号左边的声明式合法的,后面的创建语句非法
    
  • 可以通过java.lang.reflect.Array的newInstance(Class<T>, int)创建T[]数组
相关标签: 知识积累