泛型

泛型

泛型的作用

将类型参数化,使得这个类型可以是不确定的某一种类型,表明可接受的类型。

泛型的具体形式:

1.泛型类
定义形式:
class Test{
private T t;
Test(T t){
this.t = t;
}
public T getT(){
return t;
}

}
2.泛型方法
声明方式:
public void show(){

}
泛型参数类型声明需要在返回类型之前。
3、泛型类型参数命名约定
E - Element,主要由Java Collections框架使用。
K - Key,主要用来表示地图的参数类型。
V - 值,主要用于表示地图的参数类型值。
N - 数字,主要用于表示数字。
T - Type,主要用于表示第一个泛型类型参数。
S - Type,主要用于表示第二个泛型类型参数。
U - Type,主要用来表示第三个泛型类型参数。
V - Type,主要用于表示第四个泛型类型参数。

类型擦除和原始类型

JAVA的虚拟机中并不存在泛型,泛型只是为了编程效率和安全性而创建的一种机制,在代码编译后,java虚拟机会把这些泛型参数类型都擦除,用相应的确定类型来代替,即类型擦除,而用于替代的类型称为原始类型,在类型擦除过程中,一般使用第一个限定的类型来替换,若无限定则使用Object.
泛型擦除例子:
1、泛型类(不带泛型限定)代码:
class Test{
private T t;
public void test(T t){
}
}
虚拟机进行擦除后的代码:
class Test{
private Object t;
public void test(Object t){
}
}
2、泛型类(带泛型限定)擦除例子: 
class Test{
private T t;
public void test(T t){
}
}
虚拟机进行擦除后的原始类型:
class Test{
private Comparable t;
public void test(Comparable t){
}
}

泛型较之于使用Object的优势

由于泛型可以接受多个参数,而Object经过强制类型转换也可以转换为任何类型,但是泛型可以把使用Object的错误提前到编译后,而不是运行后,提升安全性。以结合泛型与集合框架举例说明:
代码1:
ArrayList list = new ArrayList();
al.add(“test”);
al.add(1);
String s1 = (String)list.get(0);
String s2 = (String)list.get(1);
以上代码在编译时没问题,但在运行时出错。
原因:首先声明无泛型的ArrayList时,其默认的原始类型是Object数组,既然为Object类型,就可以接受任意数据的赋值,因此编译时没有问题,但是在运行时,Integer强转成String,肯定会出现ClassCastException.因此泛型的引入增强了安全性,把类转换异常提前到了编译时期。
ArrayList list = new ArrayList();
al.add(“test”);
al.add(4);
String s1 = (String)list.get(0);
String s2 = (String)list.get(1);
以上代码在编译期就已出错。
由此也可以看出泛型可以把运行期的错误提前到编译器来,也就提高了编程效率和安全性。

泛型的限定

泛型限定是通过‘?’(称为通配符)来实现的,表示可以接受任意类型。
1、这里顺便说明一下‘?’与‘T’的区别:仅仅在对参数类型的使用上。
‘?’:
public void print(ArrayList<?> al){
Iterator<?> it = al.iterator();
while(it.hasNext()){
System.out.println(in.next());
}
}
‘T’:
public void print(ArrayList al){
Iterator it = al.iterator();
while(it.hasNext()){
T t = it.next();
System.out.println(t);
}
}
区别就在:‘T’可以作为类型来使用而‘?’仅能作为接收任意类型
2、泛型限定的几种方式:
A、 ? extends A 这种方式说明只能接收 A及其子类类型,所谓的“上限”
B、 ? super A 这种方式说明只能接收A及其父类类型,所谓的“下限”
下面代码展示类型限定的应用方式:
由于泛型参数类型可以表示任意类的类型,若T要引用compareTo方法,可用以下代码实现:
public shwo(T a, T b){
int num = a.compareTo(b);
}
此处用于限定T类型继承自Comparable,故T类型就可以调用compareTo方法。
也可以有多个类型限定,例如:
<T extends Comparable & Serializable>
这种书写方式把comparable写在前边,由于类型擦除的问题,原始类型是由Comparable替换的,因此写在前边的是类中存在此类型的泛型方法放在前边,避免调用方法时类型的强制转换,提高效率。
class Test<T extends Comparable & Serializable>{
private T first;
private T second;
public Test(T first, T second) {
int a = first.compareTo(second);

}
}
此处是利用了CompareTo方法,因此把Comparable写在前边,类型擦除后为Comparable,若为Serializable,还得用强制类型转换,否则不能使用compareTo方法

关于泛型类型限定的“继承”误区

类型的限定虽然使用关键字extends,但是并不是继承,比如:
即与之间并没有任何关系,唯一的关系就是他们都是Object的子类。
所以这种写法是错误的:ArrayList al = new ArrayList();
此处的, 作为泛型的意思是ArrayList的接收类型。

泛型的一些基本规则约束

1、泛型的类型参数必须为类的引用,不能用基本类型(int, short, long, byte, float, double, char, boolean)
2、泛型的类型参数可以有多个,代码举例如下:
public <T, E> void test(){
}
3、泛型可以使用extends, super, ?(通配符)来对类型参数进行限定
4、由于类型擦除,运行时类型查询只适用于原始类型,比如instanceof、getClass()、强制类型转换,a instanceof (A),在类型擦除后便是 a instanceof A,因此以上运行的一些操作在虚拟机中操作都是对原始类型进行操作,因为在虚拟机种并不存在泛型。
5、不能实例化类型变量,即不能出现以下的类似代码
T t = new T();
T.Class
原因:类型擦除后,就是Object t = new Object(),这不符合编程人员的想法。
6、不能在静态方法中出现参数类型,例如:
private static T example; //error
public static void showExample() {//error
T…
}
public static T showExample() {//error
T…
}
首先方法是一个返回类型为T的普通方法,而非泛型方法,这和在静态方法中使用非静态参数是一样的,静态方法是先于对象而存在内存中的,因此在编译的时候,T的类型无法确定。
但是这样的代码就是正确的:
class Test{
public static T show(){

}
}
因为这个静态方法是泛型方法。
7、如果有两个接口是同一接口的不同参数化,那么一个类不能同时成为这两个接口类型的子类,例如:
class testA implements Comparable{}
class testB extends Calendar implements Comparable{} //error
当类型擦除后,testA实现的是Comparable,而testB继承了testA,不能再次又去实现Comparable,会报这样的错误:The interface Comparable cannot be implemented more than once with different arguments: Comparable<Main.Test.testA> and Comparable<Main.Test.testB>。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值