java泛型知识总结
1.泛型产生的背景?
java推出泛型以前,程序员可以构建一个元素类型为Object的集合,该集合可以存储任意的数据类型对象,而在使用该集合的过程中,需要程序员明确知道存储每个元素的数据类型,否则很容易引发ClassCastException异常如下图代码实例:
package com.mage.domo1;
import java.util.ArrayList;
/**
* @author qzp
* @create 2020-02-19 13:19
*/
public class Test01 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("java");
arrayList.add(1314);
arrayList.add(true);
for (int i = 0; i < arrayList.size(); i++) {
Object o = arrayList.get(i);
System.out.println(o);
}
}
}
运行结果:
java
1314
true
package com.mage.domo1;
import java.util.ArrayList;
/**
* @author qzp
* @create 2020-02-19 13:19
*/
public class Test01 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("java");
arrayList.add(1314);
arrayList.add(true);
for (int i = 0; i < arrayList.size(); i++) {
Object obj = arrayList.get(i);
//将类型进行强转,问题就出现了,编译器还不会报错,但是一运行就报异常
String str = (String) obj;
System.out.println(str);
}
}
}
结果:
java
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at com.mage.domo1.Test01.main(Test01.java:17)
2.泛型的概念
Java泛型是jdk5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构,避免了运行时期报类型转换异常ClassCastException,消除了强制类型转换的风险
泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数
package com.mage.domo1;
import java.util.ArrayList;
/**
* @author qzp
* @create 2020-02-19 13:19
*/
public class Test01 {
public static void main(String[] args) {
//泛型代表指定了集合元素添加类型
ArrayList<String> arrayList = new ArrayList();
arrayList.add("java");
arrayList.add("C++");
//编译时期就报错,避免了运行时期报类型转换异常ClassCastException
/*arrayList.add(1314);
arrayList.add(true);*/
for (int i = 0; i < arrayList.size(); i++) {
String str = arrayList.get(i);
System.out.println(str);
}
}
}
运行结果:
java
C++
3.泛型类
- 泛型类的定义语法:
class 类名称 <泛型标识1,泛型标识2,....>{
private 泛型标识 变量名;
.......
}
- 常用的泛型标识:T,E,K,V
package com.mage.domo1;
/**泛型类的定义
* @author qzp
* @create 2020-02-19 13:50
* @param <T> 泛型标识--类型形参
* T 创建对象时,指定具体的类型的数据类型
*/
public class People<T> {
private T key;
public People(T key) {
this.key = key;
}
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
@Override
public String toString() {
return "People{" +
"key=" + key +
'}';
}
}
- 泛型类的使用语法:
类名<具体的数据类型> 对象名 = new 类名<>();
package com.mage.domo1;
/**
* @param
* @author qzp
* @create 2020-02-19 13:58
*/
public class Test02 {
public static void main(String[] args) {
//泛型类在创建对象的时候来指定操作的数据类型
People<String> people = new People<>("java");
String key = people.getKey();
System.out.println(key);
People<Integer> people1 = new People<>(11);
Integer key1 = people1.getKey();
System.out.println(key1);
//泛型类在创建对象的时候来没有指定操作的数据类型,将按照Object类型来操作
People people2 = new People(true);
Object key2 = people2.getKey();
System.out.println(key2);
//泛型类不支持基本数据类型
/*People<int> people3 = new People<int>(100);*/
//同一泛型类,根据不同的数据类型的创建的对象,本质上是一样的类型
System.out.println(people.getClass());
System.out.println(people1.getClass());
System.out.println(people2.getClass());
System.out.println(people.getClass()==people1.getClass());
}
}
运行结果:
java
11
true
class com.mage.domo1.People
class com.mage.domo1.People
class com.mage.domo1.People
true
注意事项:
- 泛型类在创建对象的时候来指定操作的数据类型
- 泛型类在创建对象的时候来没有指定操作的数据类型,将按照Object类型来操作
- 泛型类不支持基本数据类型
- 同一泛型类,根据不同的数据类型的创建的对象,本质上是相同的类型
4.泛型类派生子类
- 父类和子类都是泛型类,子类和父类的泛型类型要一致
class ChilderClass<T> extends FatherClass<T>
- 子类不是泛型类,父类要明确泛型的数据类型
class ChilderClass extends FatherClass<String>
5.泛型接口
- 泛型接口的定义语法
interface 类名称 <泛型标识1,泛型标识2,....>{
private 泛型标识 方法名();
.......
}
6.泛型接口的使用
- 实现类不是泛型类,接口要明确数据类型
interface ChilderClass extends FatherClass<String>
- 实现类是泛型类,实现类和接口的泛型数据类型要一致
interface ChilderClass<T> extends FatherClass<T>
7.泛型方法
- 泛型方法的定义语法
修饰符 <T,E,V,....> 返回值类型 方法名(形参列表){
方法体....
}
- 修饰符与返回值类型中间非常重要,可以理解为声明此方法为泛型方法
- 只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法
- 表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T
- 与泛型类的定义相同,此处的T可以随便写任意标识,常用的如:T,E,K,V等形式的参数常用于表示泛型
package com.mage.domo1;
import java.util.ArrayList;
import java.util.Random;
/**
* @param
* @author qzp
* @create 2020-02-19 14:33
*/
public class ProductGetter<T> {
//产生随机数
Random random = new Random();
//奖品
private T product;
//奖品池
ArrayList<T> arrayList = new ArrayList<>();
//添加奖品
public void addProduct(T t){
arrayList.add(t);
}
//抽奖
//成员方法
public T getProduct(){
return product = arrayList.get(random.nextInt(arrayList.size()));
}
/*
* 定义泛型方法
* list:参数
* <E>:泛型标识,具体类型,由调用方法的时候指定类型
* */
public <E> E getProduct(ArrayList<E> list){
return list.get(random.nextInt(arrayList.size()));
}
/*
* 定义静态泛型方法,采用多个泛型
* */
public static <E,K,V,X> void getProduct(E e,K k,V v,X x){
System.out.println(e+"--"+k+"--"+v+"--"+x);
}
}
测试:
package com.mage.domo1;
import java.util.ArrayList;
/**
* @param
* @author qzp
* @create 2020-02-19 14:42
*/
public class Test03 {
public static void main(String[] args) {
//泛型类的使用
ProductGetter<String> productGetter1 = new ProductGetter<>();
//调用泛型类的成员方法
String[] str = {"苹果手机","ipad3","扫地机器人"};
for (int i = 0; i <str.length ; i++) {
productGetter1.addProduct(str[i]);
}
String product = productGetter1.getProduct();
System.out.println(product);
System.out.println(product.getClass().getSimpleName());
//泛型方法的调用,类型通过调用方法的时候来指定
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(10000);
arrayList.add(20000);
arrayList.add(30000);
Integer product1 = productGetter1.getProduct(arrayList);
System.out.println(product1);
System.out.println(product1.getClass().getSimpleName());
ArrayList<String> arrayList1 = new ArrayList<>();
arrayList1.add("语文");
arrayList1.add("数学");
arrayList1.add("英语");
String product2 = productGetter1.getProduct(arrayList1);
System.out.println(product2);
System.out.println(product2.getClass().getSimpleName());
//调用多个泛型类型的静态泛型发方法
ProductGetter.getProduct("java",100,true,100.0);
}
}
测试结果:
苹果手机
String
10000
Integer
语文
String
java--100--true--100.0
注意事项:
- 泛型类和泛型方法没有任何关系,泛型方法能使方法独立于类而产生变化
- 泛型类的成员方法不支持静态
- 泛型方法支持静态
8.泛型方法和可变参数
public <E> void print(E.... e){
for(E e1:e){
System.out.println(e1);
}
}
9.类型通配符
- 类型通配符一般是使用“?”代替具体的类型实参
注意:类型通配符是类型实参,而不是类型形参
package com.mage.domo2;
/**
* @param
* @author qzp
* @create 2020-02-19 18:37
*/
public class Animals<E> {
private E first;
public E getFirst() {
return first;
}
public void setFirst(E first) {
this.first = first;
}
}
package com.mage.domo2;
/**
* @param
* @author qzp
* @create 2020-02-19 18:38
*/
public class Test01 {
public static void main(String[] args) {
Animals<Number> animals = new Animals<>();
animals.setFirst(110);
show(animals);
Animals<Integer> animals1 = new Animals<>();
animals.setFirst(100);
show(animals1);
}
//使用类型通配符
public static void show(Animals<?> animals){
Object first = animals.getFirst();
System.out.println(first);
}
}
i.类型通配符的上限
- 语法:
类/接口中使用:<? extends 实参类型>
- 注意:要求该泛型的类型,只能是实参类型,或实参类型的子类类型
package com.mage.domo2;
/**
* @param
* @author qzp
* @create 2020-02-19 18:38
*/
public class Test01 {
public static void main(String[] args) {
Animals<Number> animals = new Animals<>();
animals.setFirst(110);
show(animals);
Animals<Integer> animals1 = new Animals<>();
animals.setFirst(100);
show(animals1);
}
/*
*泛型上限通配符,传递的参数类型,只能是Number及其子类,如:Integer,否则报错
* */
public static void show(Animals<? extends Number> animals){
//animals.setFirst(100);不可以进行值的设置和添加,无法判断具体类型
Number first = animals.getFirst();
System.out.println(first);
}
}
ii.类型通配符的下限
- 语法:
类/接口中使用:<? super 实参类型>
- 注意:要求该泛型的类型,只能是实参类型,或实参类型的父类类型
package com.mage.domo2;
/**
* @param
* @author qzp
* @create 2020-02-19 18:38
*/
public class Test01 {
public static void main(String[] args) {
Animals<Number> animals = new Animals<>();
animals.setFirst(110);
show(animals);
Animals<Integer> animals1 = new Animals<>();
animals.setFirst(100);
show(animals1);
}
/*
*泛型下限通配符,传递的参数类型,只能是Integer及其父类,如:Number,否则报错
* */
public static void show(Animals<Animals<? super Integer> animals){
//取值和设置值,返回类型皆有Object来接收
Object object = animals.setFirst(100);
Object first = animals.getFirst();
System.out.println(first);
}
}
10.类型擦除
泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,称之为类型擦除
public class Test01 {
public static void main(String[] args) {
ArrayList<Integer> arrayList1 = new ArrayList<>();
ArrayList<String> arrayList2 = new ArrayList<>();
System.out.println(arrayList1.getClass().getSimpleName());
System.out.println(arrayList2.getClass().getSimpleName());
System.out.println(arrayList1.getClass()==arrayList2.getClass());
}
}
运行结果:
ArrayList
ArrayList
true
1.无限制类型擦除
package com.mage.domo3;
import java.util.List;
/**泛型类的定义
* @author qzp
* @create 2020-02-19 13:50
* @param <T> 泛型标识--类型形参
* T 创建对象时,指定具体的类型的数据类型
*/
public class People<T extends Number> {
private T key;
public People(T key) {
this.key = key;
}
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
public <T extends List> T show(T t){
return t;
}
}
public class Test01 {
public static void main(String[] args) {
People<Integer> people = new People<>(10);
Class<? extends People> aClass = people.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
System.out.println(declaredField.getName()+":"+ declaredField.getType().getSimpleName());
}
}
}
运行结果:
private java.lang.Object com.mage.domo3.People.key
key:Object
2.有限制类型擦除
public class Test01 {
public static void main(String[] args) {
People<Integer> people = new People<>(10);
Class<? extends People> aClass = people.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
System.out.println(declaredField.getName()+":"+ declaredField.getType().getSimpleName());
}
}
}
运行结果:
private java.lang.Object com.mage.domo3.People.key
key:Number
3.泛型方法中类型擦除
package com.mage.domo3;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* @param
* @author qzp
* @create 2020-02-19 19:42
*/
public class Test01 {
public static void main(String[] args) {
People<Integer> people = new People<>(10);
Class<? extends People> aClass = people.getClass();
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod.getName()+
":"+declaredMethod.getReturnType().getSimpleName());
}
}
}
运行结果:
getKey:Number
setKey:void
show:List
4.桥接方法
package com.mage.domo3;
/**
* @param
* @author qzp
* @create 2020-02-19 20:28
*/
public interface Info<T> {
T info(T values);
}
package com.mage.domo3;
/**
* @param
* @author qzp
* @create 2020-02-19 20:29
*/
public class InfoImpl implements Info<Integer> {
@Override
public Integer info(Integer values) {
return values;
}
}
测试:
public class Test04 {
public static void main(String[] args) {
Class<InfoImpl> infoClass = InfoImpl.class;
Method[] declaredMethods = infoClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName()+":"+declaredMethod.getReturnType().getSimpleName());
}
}
}
运行结果:
info:Integer
info:Object
11.泛型数值创建
- 可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
public class Test05 {
public static void main(String[] args) {
//不可以直接创建泛型数组
//ArrayList<String>[] arrayList1 = new ArrayList<>(5);
//报异常:Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
/*ArrayList[] lists = new ArrayList[5];
ArrayList<String>[] arrayList = lists;
ArrayList<Integer> arrayList1 = new ArrayList<>();
arrayList1.add(100);
lists[0]=arrayList1;
String str = arrayList[0].get(0);
System.out.println(str);*/
ArrayList<String>[] arrayList = new ArrayList[5];
ArrayList<String> arrayList1 = new ArrayList<>();
arrayList1.add("100");
System.out.println(arrayList[0] = arrayList1);
}
}
- 可以通过java.lang.reflect.Array的newInstance(Class,int)创建T[ ]数组
package com.mage.domo3;
import java.lang.reflect.Array;
/**
* @param
* @author qzp
* @create 2020-02-19 21:14
*/
public class Account<T> {
private T[] array;
public Account(Class<T> clz,int length) {
//通过Array.newInstance创建泛型数组
this.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;
}
}
package com.mage.domo3;
import java.lang.reflect.Method;
public class Test04 {
public static void main(String[] args) {
Class<InfoImpl> infoClass = InfoImpl.class;
Method[] declaredMethods = infoClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName()+":"+declaredMethod.getReturnType().getSimpleName());
}
}
}
12.泛型和反射
package com.mage.domo3;
/**
* @param
* @author qzp
* @create 2020-02-19 21:44
*/
public class Service {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Service(String name) {
this.name = name;
}
}
package com.mage.domo3;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @param
* @author qzp
* @create 2020-02-19 21:45
*/
public class Test06 {
public static void main(String[] args) throws Exception {
//泛型和反射
Class<Service> serviceClass = Service.class;
Constructor<Service> constructor = serviceClass.getConstructor();
Service service = constructor.newInstance();
//无泛型和反射
Class serviceClass1 = Service.class;
Constructor constructor1 = serviceClass1.getConstructor();
Object obj = constructor1.newInstance();
}
}