【JavaSE】Java泛型

一.概述

1.定义

泛型本质是参数化类型,即把一个所操作数据类型声明为一个参数。类似于形参,该形参代表的是一种数据类型,在调用时指定实参该数据类型才能确定。

2.特点
  • 泛型只在编译时检测提示,在编译后泛型会被擦除
  • 泛型可以操作不同的类型数据,提高了重用性,一次定义,多种使用
  • 泛型传递实参时必须是引用类型
3.好处(相对于Object)
  • 提高可读性,清晰知道操作的具体类型
  • 提高安全性,在编译期间对数据类型进行检查
  • 在获取数据时不需要强制转换类型

二.应用

1.泛型类
  • 定义
//泛型类:在类上定义泛型
//<T>定义了一个泛型T,泛型名称就像变量名,根据自己爱好改
//可以一次定义多个泛型,如<A,B,C,D,E>
class GenericClass<T>{
	//类的泛型可以在类中非静态成员中使用
	private T t;	
	public GenericClass(T t){
		this.t=t;
	}
	public T get(){
		return this.t;
	}	
	
	//类的泛型不能用于静态成员,因为在创建类对象时才能确定泛型的具体类型
	//private static T t2;    		//编译错误
	//public static T get2(){}    	//编译错误
}
  • 访问
		//泛型类声明和创建对象泛型要一致(左右一致)
		//泛型必须是引用类型,这里int自动装箱成Integer
		GenericClass<Integer> g=new GenericClass<Integer>(12345);
		
		//在jdk1.7增加了菱形泛型,只需声明时指定实际泛型,创建时可以省略
		//GenericClass<Integer> g=new GenericClass<>(12345);
		
		System.out.println(g.get());
  • 注意
  • 类的泛型不能用于静态成员,因为在创建类对象时才能确定泛型的具体类型
  • 泛型必须是引用类型
2.泛型接口
  • 定义
//泛型接口跟泛型类的定义一样
interface GenericInterface<T>{
	//接口中属性都是静态,所以无法使用接口懂的泛型
	//T t;   //编译错误
	
	//接口中的方法可以使用接口的泛型
	T get();
	void set(T t);
}
  • 实现泛型接口
//方式1:使用泛型类实现泛型接口
class GenericClass<T> implements GenericInterface<T>{
	@Override
	public T get() {return null;}

	@Override
	public void set(T t) {}
}

//方式2:使用普通类实现泛型接口,接口中的泛型需要给出实际的引用类型
class CommonClass implements GenericInterface<String>{
	
	//因为泛型接口中的泛型已确定为String,所以重写的方法的T都变成了String
	@Override
	public String get() {return null;}

	@Override
	public void set(String t) {}	
}
3.泛型方法
  • 定义
//泛型方法可以定义在普通类或泛型类中
class CommonClass{
	//泛型方法:在方法返回值前使用<>定义泛型
	//泛型方法中的泛型可以在[方法返回值]丶[方法参数]丶[方法体内]使用
	public <M>  M  genericMethod(List<M> list){
		M m=list.get(0);
		return m;
	}
	
	//静态泛型方法
	public static <M>  M  staticGenericMethod(List<M> list){
		M m=list.get(1);
		return m;
	}
}
  • 访问
		List<Integer> list=new ArrayList<Integer>();
		list.add(1);
		list.add(2);
		CommonClass cc=new CommonClass();
		
		//调用泛型方法时不用显式指明实际泛型,系统会自动根据方法参数进行判断
		Integer i1= cc.genericMethod(list);		
		Integer i2=CommonClass.staticGenericMethod(list);
		
		System.out.println(i1);
		System.out.println(i2);

三.泛型通配符与边界

1.通配符

泛型通配符?表示不确定的泛型实参,可代表多种类型,常搭配泛型类为方法参数。

public static void main(String[] args) {
	//传递各种泛型
	printList(new ArrayList<Integer>());		
	printList(new ArrayList<Number>());
	printList(new ArrayList<String>());
}

//使用?代替具体的泛型,该方法参数可接收任意泛型的List
//这里不可以用List<Object>代替,虽然Object是所有类的父类,但Object泛型类不是所有泛型类的父类
public static void printList(List<?> list){}
  • <T>?区别
  • 都表示多种不确定的类型
  • <T> 主要用于定义各种泛型类丶泛型方法等,相当于形参
  • ?主要用于泛型类的实参
2.下边界<? super 底层类>

下边界限制了泛型只能是本类或其祖先类

  • 特点
  • 可改,泛型类存放的数据都是底层类或底层类的父类,所以修改时传递数据限定为底层类或其底层类子类,就会形成多态的父类引用指向子类对象
  • 可读,因为所有类的父类是Object,所以读取时用Object接收就行
class A{}
class B extends A{}
class C extends B{}
class D extends C{}
//下边界为B
List<? super B> list=new ArrayList<>();
//List<? super B> list=new ArrayList<A>();

//编译错误,修改时不能传递B的父类
//list.add(new A());

//正常,修改时只能传递B类和B的子类
list.add(new B());
list.add(new C());
list.add(new D());

//编译错误,因为泛型类没有限制上边界,所以默认上边界只能时Object
//B b= list.get(0);
Object o=list.get(0);
3.上边界<? extends 顶层类>

上边界限制了泛型只能是本类或其子孙类

  • 特点
    • 不可改(null除外),因为泛型没有下边界,所以传递进来的泛型数据不确定,当修改时可能会出现类型转换异常
    • 可读,因为上边界已经限制了顶层类,所以读取时用顶层类接收就行(多态的父类引用指向子类地址)
//上边界为B
List<? extends B> list=new ArrayList<>();
//添加ABCD均编译错误,因为没有设定下边界
//list.add(new A());
//list.add(new B());
//list.add(new C());
//list.add(new D());

//正常,因为上边界为B,而A和Object是B的父类
B b= list.get(0);
A a=list.get(0);
Object o=list.get(0);

四.泛型可变参数

	public static void main(String[] args) {
		//泛型可变参数可放任意类型,但会被解析成Object数组
		Object[] o= genericMethod(1,1.2,"abc");
				
		//放同种类型就会被解析成该类型的数组
		Integer[] i=genericMethod(1,2,3);
	}
		
	//泛型可变参数
	public static <T> T[] genericMethod(T... args){		
		return args;
	}

五、获取子类上继承类的泛型实参

1.概念
  • List<E> E表示泛型参数形参
  • ArrayList<Integer> Integer表示泛型参数实参
  • ArrayList<Integer> ArrayList<Integer>整个表示参数化类型
2.步骤
  • 获取子类对象的Class
  • 获取带泛型的参数化类型的父类
  • 将带有泛型的父类的类型转成具体参数化的类型ParameterizedType
  • 通过ParameterizedType的方法获取泛型实参
public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao<T>{
	private Class clazz;
	
	public BaseDaoImpl() {
		//举例:class UserDaoImpl extends BaseDaoImpl<User>
		
		//对子类进行反射,this在子类实例化是指向子类对象(UserDaoImpl)
		Class clazz = this.getClass();
		
		//获取父类的参数化类型,即子类extends后面那一坨(BaseDaoImpl<User>,都带包名的,此处省略)
		Type type = clazz.getGenericSuperclass();
		
		//强转成ParameterizedType
		ParameterizedType pType = (ParameterizedType) type;
		
		//通过参数化类型获得实际类型参数(User)
		Type[] types = pType.getActualTypeArguments();
		//因为BaseDaoImpl<User>只有一个实际类型参数,获取第一个即可
		this.clazz = (Class) types[0];
	}
}	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值