Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
class 类名称 <泛型标识,泛型标识,…> {
private 泛型标识 变量名;
.....
}
// Java1.7以后,后面的<>中的具体的数据类型可以省略不写,如:类名<具体的数据类型> 对象名 = new 类名<>();
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
泛型类,如果没有指定具体的数据类型,此时,操作类型是Object
泛型的类型参数只能是类类型,不能是基本数据类型
泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型
子类是泛型类,子类和父类的泛型类型要一致
class ChildGeneric<T> extends Generic<T>
子类不是泛型类,父类要明确泛型的数据类型
class ChildGeneric extends Generic<String>
/**
* 此处的T可以随便写为任意标识,常见的有T、E等形式的参数表示泛型,泛型在定义的时候不具体,使用的时候才变得具体。在使用的时候确定泛型的具体数据类型。即在创建对象的时候确定泛型。
*/
public class GenericsClassDemo<T> {
//t这个成员变量的类型为T,T的类型由外部指定
private T t;
//泛型构造方法形参t的类型也为T,T的类型由外部指定
public GenericsClassDemo(T t) {
this.t = t;
}
//泛型方法getT的返回值类型为T,T的类型由外部指定
public T getT() {
return t;
}
}
//
// 例如:
当你的泛型类型想变为Integer类型时,也是很方便的。直接在创建时,T写为Integer类型即可:
Generic<Integer> genericInteger = new Generic<Integer>(666);
泛型在定义的时候不具体,使用的时候才变得具体。
// 泛型在定义的时候不具体,使用的时候才变得具体,在使用的时候确定泛型的具体数据类型。即:在创建对象的时候确定泛型。
Generic<String> genericString = new Generic<String>("helloGenerics");
// 此时,泛型标识T的类型就是String类型,那我们之前写的类就可以这么认为:
public class GenericsClassDemo<String> {
private String t;
public GenericsClassDemo(String t) {
this.t = t;
}
public String getT() {
return t;
}
}
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中。
interface 接口名称 <泛型标识,泛型标识,…> {
泛型标识 方法名();
.....
}
定义类时确定泛型的类型
public class GenericsImp implements GenericsInteface<String> {
@Override
public void add(String s) {
System.out.println("设置了泛型为String类型");
}
}
始终不确定泛型的类型,直到创建对象时,确定泛型的类型
public class GenericsImp<T> implements GenericsInteface<T> {
@Override
public void add(T t) {
System.out.println("没有设置类型");
}
}
确定泛型
public class GenericsTest {
public static void main(String[] args) {
GenericsImp<Integer> gi = new GenericsImp<>();
gi.add(66);
}
}
泛型方法,是在调用方法的时候指明泛型的具体类型 。
修饰符 <T,E, ...> 返回值类型 方法名(形参列表) {
方法体...
}
泛型方法能使方法独立于类而产生变化,
Ps:
非常重要,可以理解为声明此方法为泛型方法。
的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。/**
*
* @param t 传入泛型的参数
* @param 泛型的类型
* @return T 返回值为T类型
*/
public <T> T genercMethod(T t){
System.out.println(t.getClass());
System.out.println(t);
return t;
}
示例
public <E> void print(E... e){
for (E e1 : e) {
System.out.println(e);
}
}
类型通配符一般是使用"?"代替具体的类型实参,当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。此时只能接受数据,不能往该集合中存储数据。
//泛型通配符?:左边写> 右边的泛型可以是任意类型
ArrayList<?> list1 = new ArrayList<Object>();
ArrayList<?> list2 = new ArrayList<String>();
ArrayList<?> list3 = new ArrayList<Integer>();
一般的,泛型通配符?主要应用在参数传递方面,如下:
public static void test(ArrayList<?> coll){
}
public static void main(String[] args) {
// 此时,我们可以传递不同类似进去方法中去。
ArrayList<Integer> list1 = new ArrayList<Integer>();
test(list1);
ArrayList<String> list2 = new ArrayList<String>();
test(list2);
}
语法
// 要求该泛型的类型,只能是实参类型,或实参类型的子类类型。
类/接口<? extends 实参类型>
// 要求该泛型的类型,只能是实参类型,或实参类型的父类类型。
类/接口<? super 实参类型>
现有:Animal Dog Cat类
class Animal{}//父类
class Dog extends Animal{}//子类
class Cat extends Animal{}//子类
泛型的上限示例
// 编译不通过
// ArrayList extends Animal> list = new ArrayList
ArrayList<? extends Animal> list2 = new ArrayList<Animal>();
ArrayList<? extends Animal> list3 = new ArrayList<Dog>();
ArrayList<? extends Animal> list4 = new ArrayList<Cat>();
泛型的下限示例
ArrayList<? super Animal> list5 = new ArrayList<Object>();
ArrayList<? super Animal> list6 = new ArrayList<Animal>();
// 编译不通过
// ArrayList super Animal> list7 = new ArrayList();
// 编译不通过
// ArrayList super Animal> list8 = new ArrayList();
泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是,泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为–类型擦除。
**泛型数组:**可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象,不过我们可以通过java.lang.reflect.Array的newInstance(Class,int)创建T[]数组。
array = (T[])Array.newInstance(clz, length);
Class<T>
Constructor<T>
示例
public static void main(String[] args) throws Exception {
Class<Person> personClass = Person.class;
Constructor<Person> constructor = personClass.getConstructor();
Person person = constructor.newInstance();
}
https://www.bilibili.com/video/BV1xJ411n77R