一.什么是泛型?
泛型是JDK5中引入的特性,它提供了编译时类型安全检测机制
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
错误的例子:
List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);
for(int i = 0; i< arrayList.size();i++){
String item = (String)arrayList.get(i);
Log.d("泛型测试","item = " + item);
}
List<String> arrayList = new ArrayList<String>();
...
//arrayList.add(100); 在编译阶段,编译器就会报错
二.泛型的特性
泛型只在编译阶段有效。
泛型信息不会进入到运行时阶段。
public class Demo1 {
public static void main(String[] args) {
List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();
//getClass()返回的是对象运行时的类型
Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();
if(classStringArrayList.equals(classIntegerArrayList)){
System.out.print("泛型测试类型相同");
}
}
}
三.常用类型
- E - Element (在集合中使用,因为集合中存放的是元素)
- T - Type(表示Java 类,包括基本的类和我们自定义的类)
- K - Key(表示键,比如Map中的key)
- V - Value(表示值)
- N - Number(表示数值类型)
- ? – (表示不确定的java类型)
- S、U、V - 2nd、3rd、4th types
四.泛型的使用
1.泛型类
泛型类:是在实例化类的时候指明泛型的具体类型
(1)使用语法
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
(2)Java1.7以后,后面的<>中的具体的数据类型可以省略不写
类名<具体的数据类型> 对象名 = new 类名<>(); 菱形语法
(3)注意事项
泛型类,如果没有指定具体的数据类型,此时,操作类型是Object
泛型的类型参数只能是类类型,不能是基本数据类型
泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
public class GenericDemo {
public static void main(String[] args) {
//泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
//传入的实参类型需与泛型的类型参数类型相同,即为Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);
//传入的实参类型需与泛型的类型参数类型相同,即为String.
Generic<String> genericString = new Generic<String>("key_vlaue");
System.out.println("泛型测试key is " + genericInteger.getKey());
System.out.println("泛型测试key is " + genericString.getKey());
/*泛型测试key is 123456
泛型测试key is key_vlaue*/
}
}
泛型类的子类
- 子类也是泛型类,子类和父类的泛型类型要一致
- 子类不是泛型类,父类要明确泛型的数据类型
2.泛型接口
定义一个泛型接口
//定义一个泛型接口
public interface Generator<T> {
public T next();
}
实现类不是泛型类,接口要明确数据类型
/**
* 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
* 即:class FruitGenerator<T> implements Generator<T>{
* 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
*/
class FruitGenerator<T> implements Generator<T>{
@Override
public T next() {
return null;
}
}
实现类也是泛型类,实现类和接口的泛型类型要一致
/**
* 传入泛型实参时:
* 定义一个生产器实现这个接口,虽然只创建了一个泛型接口Generator<T>
* 但是可以为T传入无数个实参,形成无数种类型的Generator接口。
* 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
* 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。
*/
public class FruitGenerator implements Generator<String> {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
@Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
3.泛型方法
泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型
泛型方法的基本介绍
- @param tClass 传入的泛型实参
- @return T 返回值为T类型
- 说明:
1)public 与 返回值中间非常重要,可以理解为声明此方法为泛型方法。
2)只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法
3)表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
IllegalAccessException{
T instance = tClass.newInstance();
return instance;
}
静态方法与泛型
静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。
//静态方法方法,采用多个泛型类型
public static <T, E, K> void printType(T t, E e, K k) {
System.out.println(t + "\t" + t.getClass().getSimpleName());
System.out.println(e + "\t" + e.getClass().getSimpleName());
System.out.println(k + "\t" + k.getClass().getSimpleName());
}
五.类型通配符
1、什么是类型通配符?
类型通配符一般是使用"?"代替具体的类型实参。所以,类型通配符是类型实参,而不是类型形参。
2、通配符上限
类/接口<? extends 实参类型> 要求该泛型的类型,只能是实参类型,或实参类型的子类类型
3、通配符下限
类/接口<? super 实参类型> 要求该泛型的类型,只能是实参类型,或实参类型的父类类型。
六.泛型与数组
可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
import java.util.*;
public class Demo3 {
public static void main(String[] args) {
//不能直接创建带泛型的数组对象
//ArrayList<String>[] listArr = new ArrayList<>();
ArrayList<String>[] listArr = new ArrayList[5];
ArrayList<Integer> intList = new ArrayList<>();
intList.add(100);
ArrayList<String> strList = new ArrayList<>();
strList.add("jason");
listArr[0] = strList;
String s = listArr[0].get(0);
System.out.println(s);
}
}
import java.util.*;
import java.lang.reflect.Array;
public class Fruit<T> {
private T[] array;
public Fruit(Class<T> clz, int length) {
array = (T[])Array.newInstance(clz, length);
}
public void put(int index, T item) {
array[index]=item;
}
public T get(int index) {
return array[index];
}
public T[] getArray(){
return array;
}
}
public static void main(String[] args) {
Fruit<String> fruit = new Fruit<>(String.class, 3);
fruit.put(0, "桃子");
fruit.put(1, "栗子");
fruit.put(2, "苹果");
System.out.println(Arrays.toString(fruit.getArray()));
String s1 = fruit.get(2);
System.out.println(s1);
}
/*[桃子, 栗子, 苹果]
苹果*/