泛型,即“参数化类型”。
Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。
泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法
泛型的类型参数只能是类类型,不能是简单类型。Generic< int >是错误的
不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。
if(a instanceof Generic){ } 是错误的
if(a instanceof Generic){ } 是ok的
类型通配符一般是使用?代替具体的类型实参,可以把?看成所有类型的父类。
public class GenericTest {
//这个类是个泛型类
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
//这不是一个泛型方法
//只是泛型类中的一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
public T getKey(){
return key;
}
/**
* 这个方法显然是有问题的,在编译器会给我们提示这样的错误信息"cannot reslove symbol E"
* 因为在类的声明中并未声明泛型E,所以在使用E做形参和返回值类型时,编译器会无法识别。
public E setKey(E key){
this.key = keu
}
*/
//在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
//由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
public <E> void show_3(E t){
System.out.println(t.toString());
}
//在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
public <T> void show_2(T t){
System.out.println(t.toString());
}
/**
* 静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
* 如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。
* 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
* 即使静态方法要使用泛型类中已经声明过的泛型也不可以。
* 如:public static void show(T t){..},此时编译器会提示错误信息:
"StaticGenerator cannot be refrenced from static context"
*/
public static <T> void show(T t){
}
}
/**
* 这才是一个真正的泛型方法。
* 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
* 这个T可以出现在这个泛型方法的任意位置.
* 泛型的数量也可以为任意多个
* 如:public <T,K> K showKeyName(Generic<T> container){
* ...
* }
*/
public <T> T showKeyName(Generic<T> container){
T test = container.getKey();
return test;
}
//泛型方法和可变参数
public <T> void printMsg( T... args){
for(T t : args){
Log.d("泛型测试","t is " + t);
}
}
//这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
public void showKeyValue1(Generic<Number> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
//这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
public void showKeyValue2(Generic<?> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
/**
* 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "
* 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
* 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。
public <T> T showKeyName(Generic<E> container){
...
}
*/
/**
* 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
* 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
* 所以这也不是一个正确的泛型方法声明。
public void showkey(T genericObj){
}
*/
public static void main(String[] args) {
}
}
1、Java泛型
Java的泛型是JDK1.5时引入的。Java的泛型是伪泛型。
- 1.泛型类
在创建实例时,需要为其类型变量赋值
class A<T>{
}
- 2.泛型方法
class A<T>{
public T fun1(){}
public void fun2(T t){} //参数
//以上两个都不是泛型方法,他们是泛型类里面的一个方法
//发现方法要求需要在方法上有泛型的定义
public <T> T fun3(){}//此为泛型方法
}
class B{
public <T> fun1(){}//也为泛型方法,泛型方法不一定要在泛型类中
}
-
泛型方法和泛型类并没有直接的关系
-
3.泛型类的使用
*泛型类中定义的泛型
>可以在方法的返回值中使用
>可以在方法的参数中使用
>可以在局部变量是使用
class C<T>{
public T fun1(){ //返回值
T t = ...//可以的
new T()//不可以的,会报错
}
public void fun2(T t){} //方法的参数
}
- 4.泛型的继承和实现
*子类不是泛型类:需要给父类传递一个具体的类型常量
>此时父类中所有的泛型参数都会被此类型常量给替换掉
*子类是泛型类:可以给父类传递一个具体的类型参数,也传递一个泛型参数
class AA1 extends A<String>{}
class AA2<E> extends A<E>{}
2、Java泛型通配符?
通配符分类
具体的可以看 java 通配符解惑
? :无界通配
<? extends T>:是指 “上界通配符(Upper Bounds Wildcards)”
<? super T>:是指 “下界通配符(Lower Bounds Wildcards)”
3、java泛型 类型擦除
Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类型在编译后都会被清除掉。
List<String>、List<T> 擦除后的类型为 List。
List<String>[]、List<T>[] 擦除后的类型为 List[]。
List<? extends E>、List<? super E> 擦除后的类型为 List<E>。
List<T extends Serialzable & Cloneable> 擦除后类型为 List<Serializable>。
4、pecs
Java 泛型中的PECS原则
生产者(Producer)使用extends,消费者(Consumer)使用super。
上界通配符 Plate<? extends Fruit> 覆盖下图中蓝色的区域。
上界<? extends T>不能往里存,只能往外取
下界Plate<? super Fruit>覆盖下图中红色的区域。
下界<? super T>不影响往里存,但往外取只能放在Object对象里
参考