泛型
什么是泛型
1.背景:
JAVA推出泛型以前,程序员可以构建一个元素类型为Object的集合,该集合能够存储任意的数据类型对象,而在使用该集合的过程中,需要程序员明确知道存储每个元素的数据类型,否则很容易引发ClassCastException异常。
2.概念:
Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构。
泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。
3.好处:
-
类型安全
-
消除了强制类型的转换
4.类型:
- E - Element (在集合中使用,因为集合中存放的是元素)
- T - Type(表示Java 类,包括基本的类和我们自定义的类)
- K - Key(表示键,比如Map中的key)
- V - Value(表示值)
- N - Number(表示数值类型)
- ? - (表示不确定的java类型)
- S、U、V - 2nd、3rd、4th types
泛型类、接口
1.泛型类
-
泛型类的定义语法
class 类名称<泛型标识,泛型标识,...> { private 泛型标识 变量名; ...... }
-
常用的泛型标识:T、E、K、V
-
使用语法
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
-
Java1.7以后,后面的<>中的具体的数据类型可以省略不写
类名<具体的数据类型> 对象名 = new 类名<>(); //菱形语法
1.1 泛型类注意事项
- 泛型类,如果没有指定具体的数据类型,此时,操作类型是Object
- 泛型的类型参数只能是类类型,不能是基本数据类型
- 泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型
1.2 泛型类测试
泛型类的定义
/**
* 泛型类的定义
* @param <T> 泛型标识--类型形参
* T 创建对象的时候来指定具体的数据类型
*/
public class Generic<T> {
// T 到底是什么? 是由外部使用类的时候类指定的
private T key;
public Generic(T key) {
this.key = key;
}
public Generic() {
}
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
@Override
public String toString() {
return "Generic{" +
"key=" + key +
'}';
}
}
使用:
public class MainClass {
public static void main(String[] args) {
// 泛型类在创建的时候,来指定操作的具体数据类型
Generic<String> stringGeneric = new Generic<String>("张三");
String stringGenericKey = stringGeneric.getKey();
System.out.println(stringGenericKey);
System.out.println("-------------------------------------");
Generic<Integer> integerGeneric = new Generic<>();
integerGeneric.setKey(200);
Integer integerGenericKey = integerGeneric.getKey();
System.out.println(integerGenericKey);
System.out.println("-------------------------------------");
// 泛型类在创建对象的时候,没有指定类型,将按照 Object 类型类操作
Generic generic1 = new Generic("李四");
Object key1 = generic1.getKey();
Generic generic2 = new Generic(300);
Object key2 = generic2.getKey();
// 泛型类不支持基本数据类型。(底层T转为Object,再转成指定类型,基本数据类型没有继承自Object)
// Generic<int> intGeneric = new Generic<int>(100); // 编译检测不通过
System.out.println("-------------------------------------");
// 同一泛型类,根据不同的数据类型创建的对象,本质上是同一类型
// 可以结合后面的泛型擦除理解,泛型擦除之后 Generic < String > 和Generic< Integer >都是Generic类。
System.out.println(stringGeneric.getClass()); // class com.yzp.demo2.Generic
System.out.println(integerGeneric.getClass()); // class com.yzp.demo2.Generic
System.out.println(stringGeneric.getClass() == integerGeneric.getClass()); // true
}
}
1.3 泛型类案例
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* 抽奖器(奖品)
*/
public class ProductGetter<T> {
// 随机数
Random random = new Random();
// 奖品
private T product;
// 奖品池
List<T> list =new ArrayList<>();
// 添加奖品
public void addProduct(T t) {
list.add(t);
}
// 抽奖
public T getProduct() {
product = list.get(random.nextInt(list.size()));
return product;
}
}
public class MainClass {
public static void main(String[] args) {
// 创建抽奖器对象,指定数据类型
ProductGetter<String> stringProductGetter = new ProductGetter<>();
String[] strProducts = {"苹果手机", "华为手机", "扫地机器人", "咖啡机"};
// 给抽奖器中填充奖品
for (String strProduct : strProducts) {
stringProductGetter.addProduct(strProduct);
}
// 抽奖
String product = stringProductGetter.getProduct();
System.out.println("恭喜您抽中了:" + product);
System.out.println("-------------------------------");
ProductGetter<Integer> integerProductGetter = new ProductGetter<>();
int[] intProducts = {500, 3000, 5000, 10000, 300000};
for (int i = 0; i < intProducts.length; i++) {
integerProductGetter.addProduct(intProducts[i]);
}
Integer product1 = integerProductGetter.getProduct();
System.out.println("恭喜您,抽中了:" + product1 + "元");
}
}
2.从泛型类派生子类
-
子类也是泛型类,子类和父类的泛型类型要一致
class ChildGeneric<T> extends Generic<T>
-
子类不是泛型类,父类要明确泛型的数据类型
class ChildGeneric extends Generic<String>
父类
/**
* 父类
*/
public class Parent<E> {
private E value;
public E getValue() {
return value;
}
public void setValue(E value) {
this.value = value;
}
}
泛型子类
/**
* 泛型类派生子类,子类也是泛型类,那么子类的泛型标识要和父类的一致。
* 子类可以进行泛型扩展,比如 ChildFirst<T,E,K>
* @param <T>
*/
public class ChildFirst<T> extends Parent<T> {
@Override
public T getValue() {
return super.getValue();
}
}
非泛型子类
/**
* 泛型类派生子类,如果子类不是泛型类,那么父类要明确数据类型
*/
public class ChildSecond extends Parent<Integer> {
@Override
public Integer getValue() {
return super.getValue();
}
}
测试
public class Test04 {
public static void main(String[] args) {
ChildFirst<String> stringChildFirst = new ChildFirst<>();
stringChildFirst.setValue("张三");
String value = stringChildFirst.getValue();
System.out.println(value);
System.out.println("--------------");
ChildSecond childSecond = new ChildSecond();
childSecond.setValue(100);
Integer value1 = childSecond.getValue();
System.out.println(value1);
}
}
3.泛型接口
3.1 泛型接口的定义语法
interface 接口名称<泛型标识,泛型标识,...> {
泛型标识 方法名();
...
}
3.2 泛型接口的使用
- 实现类不是泛型类,接口要明确数据类型。
- 实现类也是泛型类,实现类和接口的泛型类型要一致。
泛型接口
/**
* 泛型接口
* @param <T>
*/
public interface Generator<T> {
T getKey();
}
非泛型实现类
/**
* 实现泛型接口的类不是泛型类,需要明确指定实现泛型接口的数据类型
* Apple 不是泛型类,需要明确指定Generator接口的数据类型 Generator<String>
*/
public class Apple implements Generator<String> {
@Override
public String getKey() {
return "hello generic";
}
}
泛型实现类
/**
* 泛型接口的实现类是一个泛型类,那么要保证实现接口的泛型类泛型标识包含泛型接口的泛型标识
* 即 Pair<T>是泛型类,接口泛型要相同Generator<T>,
* Pair类可以对泛型进行扩充 Pair<T,E,K,...>
* @param <T>
*/
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 Test05 {
public static void main(String[] args) {
Apple apple = new Apple();
String key = apple.getKey();
System.out.println(key);
System.out.println("-----------------------");
Pair<String, Integer> stringIntegerPair = new Pair<>("张三", 100);
String key1 = stringIntegerPair.getKey();
Integer value = stringIntegerPair.getValue();
System.out.println(key1);
System.out.println(value);
}
}
泛型方法
- 泛型类,是在实例化类的时候指定泛型的具体类型。
- 泛型方法,是在调用方法的时候指定泛型的具体类型
1.语法
-
泛型方法是在调用方法的时候指明泛型的具体类型。
只有包含泛型列表的方法才是泛型方法 <T,E, …>
// 非泛型方法 (泛型类中的使用了泛型的成员方法并不是泛型方法) 修饰符 T 方法名(形参列表) { 方法体... } // 泛型方法 修饰符 <T> T 方法名(形参列表) { 方法体... }
-
语法:
修饰符 <T,E, ...> 返回值类型 方法名(形参列表) { 方法体...}
-
public与返回值中间< T >非常重要,可以理解为声明此方法为泛型方法。
-
只有声明了< T >的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
-
< T >表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
-
与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
2.泛型方法与可变参数
public <E> void print(E... e){
for (E e1 : e) {
System.out.println(e);
}
}
3.泛型方法总结
-
泛型方法能使方法独立于类而产生变化(泛型方法最重要的特点)
(注:实际工作中能够用泛型方法解决的尽量不用定义泛型类,因为泛型方法更加灵活)
-
如果static方法要使用泛型能力,就必须使其成为泛型方法
4.测试
定义泛型方法
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* 抽奖器(奖品)
*/
public class ProductGetter<T> {
// 随机数
private Random random = new Random();
// 奖品
private T product;
// 奖品池
List<T> list =new ArrayList<>();
// 添加奖品
public void addProduct(T t) {
list.add(t);
}
/**
* 抽奖,
* 非泛型方法,泛型类的成员方法
* 不能定义为 static 静态的
* @return
*/
public T getProduct() {
product = list.get(random.nextInt(list.size()));
return product;
}
/**
* 定义泛型方法
* 泛型方法的调用,类型是通过调用方法的时候来指定的
* 泛型方法的泛型是独立,与泛型类定义的泛型无关
* 泛型方法可以为 static 静态的
* @param list 参数
* @param <E> 泛型标识,具体类型由调用方法的时候来指定
* @return
*/
public <E> E getProduct(List<E> list) {
return list.get(random.nextInt(list.size()));
}
/**
* 静态泛型方法,可以采用多个泛型类型
* @param t
* @param e
* @param k
* @param <T>
* @param <E>
* @param <K>
*/
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());
}
/**
* 可变参数泛型方法定义
* @param e
* @param <E>
*/
public static <E> void print(E... e) {
for (E e1 : e) {
System.out.println(e1);
}
}
}
使用
/**
* 泛型方法的使用
*/
public class Test06 {
public static void main(String[] args) {
ProductGetter<Integer> integerProductGetter = new ProductGetter<>();
List<String> list = new ArrayList<>();
list.add("笔记本电脑");
list.add("苹果手机");
list.add("扫地机器人");
// 泛型方法的调用,类型是通过调用方法的时候来指定的
String product = integerProductGetter.getProduct(list);
System.out.println(product);
// 泛型方法的泛型是独立,与泛型类定义的泛型无关
System.out.println(product.getClass().getSimpleName()); // String
System.out.println("--------------------------------");
List<Integer> list1 = new ArrayList<>();
list1.add(1000);
list1.add(2000);
list1.add(3000);
Integer product1 = integerProductGetter.getProduct(list1);
System.out.println(product1);
System.out.println(product1.getClass().getSimpleName()); // Integer
System.out.println("--------------------------------");
// 静态泛型方法的调用,多个泛型类型
ProductGetter.printType(100, "java", true);
System.out.println("--------------------------------");
// 可变参数泛型方法的调用
ProductGetter.print(1,2,3,4,5);
}
}
类型通配符
1.什么是类型通配符?
- 类型通配符一般是使用 “?” 代替具体的类型实参。
- 所以,类型通配符是类型实参,而不是类型形参。
public class Box<E> {
private E first;
public E getFirst() {
return first;
}
public void setFirst(E first) {
this.first = first;
}
}
public class Test07 {
public static void main(String[] args) {
Box<Number> numberBox = new Box<>();
numberBox.setFirst(100);
showBox(numberBox);
System.out.println("-----------------");
Box<Integer> integerBox = new Box<>();
integerBox.setFirst(200);
showBox(integerBox);
}
/**
* 类型通配符上限
* @param box
*/
// public static void showBox(Box<? extends Number> box) {
// Number first = box.getFirst();
// System.out.println(first);
// }
/**
* 类型通配符是类型实参,而不是类型形参
* @param box
*/
public static void showBox(Box<?> box) {
Object first = box.getFirst();
System.out.println(first);
}
// 泛型不支持多态,需用类型通配符替代实参
// public static void showBox(Box<Number> box) {
// Object first = box.getFirst();
// System.out.println(first);
// }
}
2.类型通配符的上限
-
语法:
类/接口<? extends 实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的子类类型。
public class Animal {}
public class Cat extends Animal {}
public class MiniCat extends Cat {}
public class Test08Up {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<>();
ArrayList<Cat> cats = new ArrayList<>();
ArrayList<MiniCat> miniCats = new ArrayList<>();
//showAnimal(animals); // 无法传递
showAnimal(cats);
showAnimal(miniCats);
}
/**
* 泛型上限通配符,传递的集合类型,只能是 Cat 或 Cat的子类
* @param list
*/
public static void showAnimal(ArrayList<? extends Cat> list) {
// 不能填充元素
//list.add(new Animal());
//list.add(new Cat());
//.add(new MiniCat());
for (Cat cat : list) {
System.out.println(cat);
}
}
}
3.类型通配符的下限
-
语法:
类/接口<? super 实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的父类类型。
public class Animal {}
public class Cat extends Animal {}
public class MiniCat extends Cat {
}
public class Test08Down {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<>();
ArrayList<Cat> cats = new ArrayList<>();
ArrayList<MiniCat> miniCats = new ArrayList<>();
showAnimal(animals);
showAnimal(cats);
// showAnimal(miniCats); // 无法床底
}
/**
* 类型通配符下限,要求集合只能是Cat或Cat的父类类型
* @param list
*/
public static void showAnimal(ArrayList<? super Cat> list) {
// 可以填充元素;(自己或子类类型)
list.add(new Cat());
list.add(new MiniCat());
for (Object o : list) {
System.out.println(o);
}
}
}
案例
jdk中TreeSet
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
'}';
}
}
public class Cat extends Animal {
public int age;
public Cat(String name, int age) {
super(name);
this.age = age;
}
@Override
public String toString() {
return "Cat{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
public class MiniCat extends Cat {
public int level;
public MiniCat(String name, int age, int level) {
super(name, age);
this.level = level;
}
@Override
public String toString() {
return "MiniCat{" +
"level=" + level +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
/**
* 类型通配符下限的使用
*/
public class Test08 {
public static void main(String[] args) {
// TreeSet<Cat> treeSet = new TreeSet<>(new Comparator2());
TreeSet<Cat> treeSet = new TreeSet<>(new Comparator1());
treeSet.add(new Cat("jerry", 20));
treeSet.add(new Cat("amy", 22));
treeSet.add(new Cat("frank", 35));
treeSet.add(new Cat("jim", 15));
for (Cat cat : treeSet) {
System.out.println(cat);
}
}
}
// 比较器
class Comparator1 implements Comparator<Animal> {
@Override
public int compare(Animal o1, Animal o2) {
return o1.name.compareTo(o2.name);
}
}
class Comparator2 implements Comparator<Cat> {
@Override
public int compare(Cat o1, Cat o2) {
return o1.age - o2.age;
}
}
class Comparator3 implements Comparator<MiniCat> {
@Override
public int compare(MiniCat o1, MiniCat o2) {
return o1.level - o2.level;
}
}
类型擦除
1.概念
泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为——类型擦除。
2. 无限制类型擦除
直接转成Object
public class Erasure<T> {
private T key;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
}
public class Test09 {
public static void main(String[] args) {
// ArrayList<Integer> integers = new ArrayList<>();
// ArrayList<String> strings = new ArrayList<>();
//
// System.out.println(integers.getClass().getSimpleName()); // ArrayList
// System.out.println(strings.getClass().getSimpleName()); // ArrayList
// System.out.println(integers.getClass() == strings.getClass()); // true
Erasure<Integer> integerErasure = new Erasure<>();
// 利用反射,获取Erasure类的字节码文件的Class类对象
Class<? extends Erasure> clz = integerErasure.getClass();
// 获取所有的成员变量
Field[] declaredFields = clz.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 打印名称和类型,结果已经泛型T转成Object再进入JVM中
// 结果输出 => key:Object
System.out.println(declaredField.getName() + ":" + declaredField.getType().getSimpleName());
}
}
}
3. 有限制的类型擦除
有上限,转成上限类型
/**
* Erasure<T> 无限制类型擦除
* Erasure<T extends Number> 有上限类型擦除
*/
public class Erasure<T extends Number> {
private T key;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
}
public class Test09 {
public static void main(String[] args) {
Erasure<Integer> integerErasure = new Erasure<>();
// 利用反射,获取Erasure类的字节码文件的Class类对象
Class<? extends Erasure> clz = integerErasure.getClass();
// 获取所有的成员变量
Field[] declaredFields = clz.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 打印名称和类型,
// 结果 => 有上限类型擦除Number key:Number
System.out.println(declaredField.getName() + ":" + declaredField.getType().getSimpleName());
}
}
}
4.擦除方法中类型定义的参数
如果没有上下限,转为Object,有则转为上下限的类型
public class Erasure<T extends Number> {
private T key;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
/**
* 泛型方法
* @param t
* @param <T>
* @return
*/
public <T extends List> T show(T t) {
return t;
}
}
public class Test09 {
public static void main(String[] args) {
Erasure<Integer> integerErasure = new Erasure<>();
// 利用反射,获取Erasure类的字节码文件的Class类对象
Class<? extends Erasure> clz = integerErasure.getClass();
// 获取所有的成员变量
Field[] declaredFields = clz.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 打印名称和类型,
// 结果 => 无上限类型擦除 key:Object
// 结果 => 有上限类型擦除Number key:Number
System.out.println(declaredField.getName() + ":" + declaredField.getType().getSimpleName());
}
System.out.println("-----------------------");
// 获取所有的方法
Method[] declaredMethods = clz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
// 打印方法名和方法的返回值类型
/**
* 结果:
* getKey:Number
* show:List
* setKey:void
*/
System.out.println(declaredMethod.getName() + ":" + declaredMethod.getReturnType().getSimpleName());
}
}
}
5.桥接方法
/**
* 泛型接口
* @param <T>
*/
public interface Info<T> {
T info(T t);
}
public class InfoImpl implements Info<Integer> {
@Override
public Integer info(Integer integer) {
return integer;
}
}
public class Test09 {
public static void main(String[] args) {
Class<InfoImpl> infoClass = InfoImpl.class;
Method[] declaredMethods1 = infoClass.getDeclaredMethods();
for (Method method : declaredMethods1) {
/**
* 打印结果: 有两个 info 方法
* info:Integer
* info:Object
*/
System.out.println(method.getName() + ":" + method.getReturnType().getSimpleName());
}
}
}
泛型和数组
1.泛型数组的创建
注:在实际的开发中,尽量使用泛型集合替代泛型数组
-
可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
public class Test10 { public static void main(String[] args) { // 报错,可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象 //ArrayList<String>[] listArr = new ArrayList<>[]; // 此种方式易发生 ClassCastException 异常 // ArrayList[] lists = new ArrayList[5]; // 非泛型数组 // ArrayList<String>[] listArr = lists; ArrayList<String>[] listArr = new ArrayList[5]; // 用 new ArrayList 原生的创建数组,不要用泛型 } }
-
可以通过
java.lang.reflect.Array
的newInstance(Class,int)
创建T[]
数组import java.lang.reflect.Array; /** * @author yzp * @version 1.0 * @data 2021/9/8 - 0:37 */ public class Fruit<T> { // 不允许 //private T[] array = new T[]; private T[] array; public Fruit(Class<T> clz, int length) { // 通过 Array.newInstance 创建泛型数组 array = (T[]) Array.newInstance(clz, length); } /** * 填充数组 * @param index * @param item */ public void put(int index,T item) { array[index] = item; } /** * 获取数组元素 * @param index * @return */ public T get(int index) { return array[index]; } public T[] getArray() { return array; } }
/** * 泛型与数据 */ public class Test10 { 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 banana = fruit.get(2); System.out.println(banana); } }
泛型和反射
1.反射常用的泛型类
- Class< T >
- Constructor< T >
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Test11 {
public static void main(String[] args) throws Exception {
Class<Person> clz = Person.class;
Constructor<Person> constructor = clz.getConstructor();
Person person = constructor.newInstance();
// Class personClass = Person.class;
// Constructor constructor = personClass.getConstructor();
// Object o = constructor.newInstance();
}
}