Java在1.5之后加入了泛型的概念。泛型,即“参数化类型”。泛型的本质是为了参数化类型(将类型参数化传递)(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
Java泛型有三种使用方式:泛型类、泛型方法、泛型接口。
泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为“泛型擦除”。
泛型类的语法如下:
修饰符 class 类名称 <泛型类型标识>{
/*(成员变量类型)*/
private 泛型标识 var;
}
备注:常用的泛型标识:T、E、K、V
我们首先定义一个简单的类:
/**
* @describtion 泛型类定义
* 此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
* 在实例化泛型类时,必须指定T的具体类型
* @param <T> 泛型标识-类型参数
*/
public class Generic<T> {
/**
* T 由外部使用类的时候来指定
*/
private T t;
public Generic(T key) {
this.t = t;
}
public T getT() {
return t;
}
public void setT(T t){
this.t = t;
}
}
//传入的实参类型需与泛型的类型参数类型相同,即为Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);
//传入的实参类型需与泛型的类型参数类型相同,即为String.
Generic<String> genericString = new Generic<String>("key_vlaue");
备注:
子类也是泛型类,子类与父类的泛型类型要一致
class ChildGeneric<T> extends Generic<T>
class ChildGeneric<T, K, V> extends Generic<T>
子类不是泛型类,父类要明确泛型的数据类型
class ChildGeneric extends Generic<String>
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子:
// 定义一个泛型接口
public interface Generator<T> {
T getKey();
}
当实现泛型接口的类,未传入泛型实参时:
/**
* 泛型接口的实现类是一个泛型类,那么要保证实现接口的泛型类的泛型标识包含接口的泛型标识
*/
public class Pair<T, E> implements Generator<T>{
private T key;
private E value;
public Pair(T key, E value) {
this.key = key;
this.value = value;
}
@Override
public T getKey() {
return key;
}
public E getValue() {
return value;
}
}
当实现泛型接口的类,传入泛型实参时:
/**
* 实现泛型接口的类不是泛型类,需要明确实现泛型接口的数据类型
*/
public class Apple implements Generator<String> {
@Override
public String getKey() {
return "hello Generator";
}
}
泛型方法的语法如下: 其中<泛型类型标识>必须存在。
[作用域修饰符] <泛型类型标识> [返回类型] 方法名称(参数列表){}
* 泛型方法能使方法独立于类而产生变化
* 如果static方法要使用泛型能力,就必须使其成为泛型方法
/**
* 泛型方法的基本介绍
* @param tClass 传入的泛型实参
* @return T 返回值为T类型
* 说明:
* 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
* 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
* 3)<T>表明该方法将使用泛型类型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 class GenericMethod {
// 这个类是个泛型类
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
// 虽然在方法中使用了泛型,但是这并不是一个泛型方法。这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
// 所以在这个方法中才可以继续使用 T 这个泛型。
public T getKey(){
return key;
}
// 这个方法显然是有问题的,在编译器会给我们提示这样的错误信息"cannot reslove symbol E"
// 因为在类的声明中并未声明泛型E,所以在使用E做形参和返回值类型时,编译器会无法识别。
// public E setKey(E key){this.key = keu}
}
/**
* 泛型方法。
* 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
* 这个T可以出现在这个泛型方法的任意位置.
* 泛型的数量也可以为任意多个
* 如:public <T,K> K showKeyName(Generic<T> container){}
*/
public <T> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
//当然这个例子举的不太合适,只是为了说明泛型方法的特性。
T test = container.getKey();
return test;
}
// 这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
public void showKeyValue1(Generic<Number> obj){
System.out.println("泛型测试: key value is " + obj.getKey());
}
// 这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
// 同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
public void showKeyValue2(Generic<?> obj){
System.out.println("泛型测试: 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 class GenericFruit {
class Fruit{
@Override
public String toString() {
return "fruit";
}
}
class Apple extends Fruit{
@Override
public String toString() {
return "apple";
}
}
class Person{
@Override
public String toString() {
return "Person";
}
}
static class GenerateTest<T>{
public void show_1(T t){
System.out.println(t.toString());
}
// 在泛型类中声明了一个泛型方法,使用泛型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 main(String[] args) {
Apple apple = new GenericFruit().new Apple();
Person person = new GenericFruit().new Person();
GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();
// apple是Fruit的子类,所以这里可以
generateTest.show_1(apple);
// 编译器会报错,因为泛型类型实参指定的是Fruit,而传入的实参类是Person
// generateTest.show_1(person);
// 使用这两个方法都可以成功
generateTest.show_2(apple);
generateTest.show_2(person);
// 使用这两个方法也都可以成功
generateTest.show_3(apple);
generateTest.show_3(person);
}
}
public <T> void printMsg( T... args){
for(T t : args){
System.out.println("泛型测试: t is " + t);
}
}
printMsg("111", 222, "aaaa", "2323.4", 55.55);
public class StaticGenerator<T> {
/**
* 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
* 即使静态方法要使用泛型类中已经声明过的泛型也不可以。
* 如:public static void show(T t){..},此时编译器会提示错误信息:
* "StaticGenerator cannot be refrenced from static context"
*/
public static <T> void show(T t){}
}
如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法。
类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参。
public class Animal {}
public class Cat extends Animal{}
public class MiniCat extends Cat {}
要求该泛型的类型,只能是实参类型,或实参类型的子类类型。
语法:类/接口 <? extends 实参类型>
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<>();
ArrayList<Cat> cats = new ArrayList<>();
ArrayList<MiniCat> miniCats = new ArrayList<>();
// showAnimal_up(animals); // 不符合泛型上限通配符要求,只能是实参类型,或实参类型的子类类型
showAnimal_up(cats);
showAnimal_up(miniCats);
}
/**
* 类型通配符上限:要求该泛型的类型,只能是实参类型,或实参类型的子类类型
* @param list
*/
public static void showAnimal_up(ArrayList<? extends Animal> list) {
// list.add(new Cat()); // 不允许添加
list.forEach(v -> {
System.out.println(v);
});
}
要求该泛型的类型,只能是实参类型,或实参类型的父类。
语法:类/接口 <? super 实参类型>
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<>();
ArrayList<Cat> cats = new ArrayList<>();
ArrayList<MiniCat> miniCats = new ArrayList<>();
showAnimal_down(animals);
// showAnimal_down(cats);
// showAnimal_down(miniCats);
}
/**
* 类型通配符下限:要求该泛型的类型,只能是实参类型,或实参类型的父类
* @param list
*/
public static void showAnimal_down(ArrayList<? super Animal> list) {
list.add(new Cat()); // 允许添加
list.forEach(v -> {
System.out.println(v);
});
}
public class Fruit<T> {
private T[] array;
// 构造函数
public Fruit(Class<T> clz, int length) {
// 通过Array.newInstance创建泛型数组
array = (T[])Array.newInstance(clz, length);
}
/**
* 填充数组
*/
public void putArray(int index, T item) {
array[index] = item;
}
/**
* 获取数组
*/
public T getArray(int index) {
return array[index];
}
}
Fruit<String> fruit = new Fruit<>(String.class, 3);
fruit.putArray(0, "橘子");
fruit.putArray(1, "香蕉");
fruit.putArray(2, "苹果");
System.out.println(fruit.getArray(0));
不能创建一个确切的泛型类型的数组,例如:
List<String>[] ls = new ArrayList<String>[10];
使用通配符创建泛型数组,例如:
List<?>[] ls = new ArrayList<?>[10];
List<String>[] ls = new ArrayList[10];
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。