Java 14:泛型

在泛型类出现之前,主要通过继承来实现功能,比如ArrayList类只维护一个Object引用的数组,这样的问题在于:

1、从数组中获取一个值时必须进行强制类型转换,2、可以向数组中添加任意类的对象(影响程序的可读性和安全性)

泛型提供了更好的解决方案:类型参数,ArrayList有一个类型参数来指定元素的类型:ArrayList<String> files=new ArrayList<String>();

Java 7以后可以更进一步简化:ArrayList<String> files = new ArrayList<>();


一个泛型类就是具有一个或多个类型变量的类,

public class Pair<T> {
	private T first;
	private T second;
	
	public Pair(){}
	public Pair(T a,T b){
		this.first=a;
		this.second=b;
	}
	public T getFirst() {
		return first;
	}
	public void setFirst(T first) {
		this.first = first;
	}
	public T getSecond() {
		return second;
	}
	public void setSecond(T second) {
		this.second = second;
	}
}
Pair pair=new Pair();如果这样没有显式的声明类型,Pair默认为Object,可以往里面添任意的东西
public static <T> T func(T[] a){
	return a[0];
}//普通函数也可以使用泛型
在调用时,可以这样

Base.<Integer>func(a);
Base.func(a);


有时需要对类型变量加以约束,否则无法采用Comparable

	public static <T extends Comparable> T min(T[] a){
		if(a.length==0||a==null){
			return null;
		}else{
			T min=a[0];
			for(int i=1;i<a.length;i++){
				if(min.compareTo(a[i])>0){
					min=a[i];
				}
			}
			
			return min;
		}
	}
Comparable接口本身也是一个泛型类型,但注意这里用的是extends而不是implements,在这个地方,不论是子类或是接口,都用extends

public static <T extends Comparable> T min(T[] a){
	if(a.length==0||a==null){
		return null;
	}else{
		T min=a[0];
		for(int i=1;i<a.length;i++){
			if(min.compareTo(a[i])>0){
				min=a[i];
			}
		}

		return min;
	}
}

T extends A &b 进行多个限定,至多只有一个类,且必须放在第一个位置

在Java虚拟机中没有泛型类型,所有的对象都属于普通类,定义泛型类型时会自动提供一个原始类型,原始类型就是擦除类型变量并替换为限定类型(没有限定类型则替换为Object),成为一个普通的类,Pair<T>的原始类型就是将所有的T变成Object,

原始类型用第一个限定的类型变量来替换(没有则Object),当泛型声明为具体类型进行操作时:

Pair<Employee> buddies=....
Employee buddy=buddies.getFirst();
编译器1、调用原始的方法 2、对原始类型Object进行强制类型转换

泛型方法中也进行类型擦除:

public static <T extends Comparable> T min( T[] a)    -------->   public static Comparable min (Comparable[] a) 


class DateInterval extends Pair <Date>并重写了关于Date的set函数,问题是在方法擦除以后,从Pair继承来的是Object的方法,擦除变量和多态产生冲突

Pair擦除以后的方法:

public void setSecond(Object second) {
	this.second = second;
}
DateInterval擦除以后的方法:

public void setSecond(Date second) {
	。。。具体里面什么书上没说
}
编译器在DateInterval类中产生桥方法:

public void setSecond(Object second){
	setSecond((Date)second);
}
如果也重写getSecond方法:

public Date getSecond(){
	return (Date)super.getSecond().clone();
}
 就会出现:两个方法,从签名上来看,只有返回值不同(一个是DateInterval的Date,一个是Pair的Object),这在Java里是无法区分的,但是在虚拟机里确实可以区分的。


总之:

1、Java中没有泛型,只有普通方法和类

2、所有的类型参数都用他们的限定类型来替换

3、桥方法被合成,来保证多态


泛型的限制:
1、类型参数只能是普通类,不能是8种基本类型


2、对于类型的判断,虚拟机对象总有一个非泛型类型,所有的查询只产生原始类型

Pair<Date> d=new DateInterval(2, new Date(), new Date());
if(d instanceof Pair){
	System.out.println(1111);//ok
}else{
	System.out.println(2222);
}
Pair<Date> d=new DateInterval(2, new Date(), new Date());
if(d instanceof Pair<?>){
	System.out.println(1111);//ok
}else{
	System.out.println(2222);
}
if(d instanceof Pair<Date>)//编译不通过


3、不能实例化参数类型数组
Pair<String>[] table=new Pair<String>[10]; //ERROR

只有一种安全而高效的方法:使用ArrayList:  ArrayList<Pair<String>> a=new ArrayList();


4、不能实例化类型变量

也就是不能在类中进行 new T();

5、不能在静态方法和静态域中使用类型变量T


6、无论S与T是什么关系,Pair<S>和Pair<T>一般没有什么联系

也就是无法将Pair<Manager>赋给 Pair<Employee>

public static void print(Pair<Employee> p){
	Employee e1=p.getFirst();
	Employee e2=p.getSecond();
	e1.print();
}
解决方法是:使用通配符类型
public static void print(Pair<? extends Employee> p){
	Employee e1=p.getFirst();
	Employee e2=p.getSecond();
	e1.print();
}
这里,Pair<Manager>是Pair <?extends Employee>的子类型

通配符不仅可以限定从谁继承,还可以指定超类: ? super Manager

直观地讲:带有超类限定的通配符可以向泛型对象写入,带子类限定的通配符可以从泛型对象读取



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值