1.什么是泛型
泛型即参数化类型,使用时通过传入具体的类型
2.为什么用泛型
- 在编译时进行更强的类型检查
- 代码无需强转
- 可读性更好
- 适用与多种数据类型执行相同的代码
3.如何使用泛型
下面例子中 的类 关系
C–继承–>B–继承–>A
public class A<T>{
}
- <> 泛型标识
- T 泛型类型 (可以任意命名,命名规范:大写)
3.1 泛型作用范围
类
static class A<T>{
}
继承:
- 继承父类时不带泛型
static class B extends A{
}
继承父类时不带泛型时,A类的T因为类型擦除会变为Object
- 继承父类时指定泛型为不明确类型
static class B<T,TT,TTT> extends A<T>{
}
当继承父类时指定泛型为不明确类型时,需要子类带一个与 继承的父类的泛型类型名称相同,这样在new B指定泛型时会一并指定了A的,如何B的<>不带这个T,那么将会报错,因为没人指定A的T
可以理解为 继承父类时指定泛型为不明确类型时,具有延迟加载的性质,需要子类指定
- 继承父类时指定泛型为明确类型
static class B extends A<String>{
}
- 继承父类时泛型带关系
static class A<T extends A>{
}
static class B extends A<B(需要符合 T继承A)>{
}
问题:
为什么List不能传递给List?
泛型具有子类型化的规则,List是原生类型List的一个子类型,而不是参数化类型List的子类,那么如何才能传递呢!接下来的通配符会解释道。
接口
interface D<T>{
}
接口的继承和实现 与泛型类继承的规则一样
方法
static class A<T>{
public <T1> void set(T1 t1){
System.out.println(t1);
}
}
调用:
A<Object> a = new A<>();
a.<String>set("hh");
如果 T与T1名称相同 ,方法里面的T就像局部变量 和类上面的互不干涉
static class A<T>{
public <T> void set(T t1){
System.out.println(t1);
}
}
调用:
A<String> a = new A<>();
a.<Integer>set(1);
作用在返回值
static class A<T>{
public T set(T t1){
System.out.println(t1);
return t1;
}
}
调用:
A<String> a = new A<>();
String s = a.set("");
注意:
静态方法不能使用类的泛型类
static class A<T>{
public static T set(T t1){ //编译失败
System.out.println(t1);
return t1;
}
}
不用强转,以后走路都带风
泛型方法的类型推断
如果 泛型方法 有多少个泛型类型参数,调用时不指定泛型类型,那么返回值的类型是什么?
public static <T> T set(T x,T y){
return y;
}
String v1 = Main.set("", "");
Number v2 = Main.set(1, 1.2f);
A v3 = Main.set(new A<>(), new B<>());
Integer v4 = Main.set(1, 1);
会取同一父类中最小级的
3.2泛型类型
常用泛型 | 说明 |
---|---|
E | Element 表示在集合中存放的元素 |
T | Type 表述Java类 |
K | Key 映射-键 |
V | Value 映射-值 |
N | Number 数值类型 |
3.3 通配符
? 表示未知类型
使用范围:
- 参数
- 字段
- 局部变量
不能用作泛型方法调用,泛型类的类型参数
无限 <?>
表示未知类型
泛型上限 <? extends T>
关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类
List<? extends A> l=new ArrayList<>();
List<B> l1=new ArrayList<>();
l=l1;
泛型下限 <? super T>
关键字声明了类型的下界,表示参数化的类型可能是指定的类型,或者是此类型的父类
List<? super B> s=new ArrayList<>();
List<A> s1=new ArrayList<>();
s=s1;
注意:
super 不能用于类级别, 因为类型擦除后变为,Object又是所有类型的父类型,所有类型就都可以作为T,是没有意义的事情。
问题:
1.为什么需要泛型上下限?
我们之前提到 泛型具有子类型化的规则,List是原生类型List的一个子类型,而不是参数化类型List的子类,那么我如何才能把List传过去呢?
List<? extends Object> e=new ArrayList<>();
List<String> e1=new ArrayList<>();
e=e1; //编译成功
就是为了解决泛型的多态!
有的好奇宝宝可能还发现了其他问题 为什么 我e.add("")不能添加了!直接编译错误! 往下看PECS原则!! 隔着探案呢…一环扣一环
3.4 PECS原则
Producer Extends Consumer Super extends 为生产者,super为消费者
生产者顾名思义 产出东西的 对应 List的get,消费这就是买入东西的 List的add;
问题:
1.为什么 < ? extends >只能读取 不能传入,< ? supe> 传入不需要读取?
< ? extends >
List<? extends A> l=new ArrayList<>();
List<B> l1=new ArrayList<>();
l=l1;
A的子类有B和C,如果可以添加
List<? extends A> l=new ArrayList<>();
List<B> l1=new ArrayList<>();
List<C> l2=new ArrayList<>();
l=l1;
l.add(new B());
l.add(new C());
l 引用的是 l1 我 l1 只能添加类型为B的,你确可以添加个C,那我 l1 的泛型有什么用,所以不能添加
不管你l是等于 l1 还是 l2 里面添加的都是A的子类,所以可以读取返回父类A
< ? supe >
可以添加但是只能添加当前类和其子类,因为编译器无法判断究竟是哪个超类,避免像Serializable也可以添加进去,所有编译器直接静止添加其父类,当前类和子类都是可以确定的所有可以添加!
List<? super Number> v1 = new ArrayList<Object>();
List<? super Number> v2 = new ArrayList<Serializable>();
List<? super Number> v3 = new ArrayList<Number>();
建议:
以集合为例子
如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends 通配符;(Producer Extends)
如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super)
如果既要存又要取,那么就不要使用任何通配符。
3.5 如何获取类的泛型
static class A<T>{
public void get(T t){
}
}
static class B extends A<String>{
}
static class C<T> extends A<T>{
}
A<String> stringA = new A<String>();
A stringB = new B();
A<String> stringC = new C<String>();
A<String> stringC1 = new C<String>(){};
stringA.getClass().getTypeParameters();
try {
Type genericSuperclassA = stringA.getClass().getGenericSuperclass();
ParameterizedType castA = ParameterizedType.class.cast(genericSuperclassA);//报错
for (Type typeArgument : castA.getActualTypeArguments()) {
System.out.println("A"+typeArgument.getTypeName());
}
}catch (Exception e){
System.out.println(e);
}
Type genericSuperclass2 = stringB.getClass().getGenericSuperclass();
ParameterizedType castB = ParameterizedType.class.cast(genericSuperclass2);
for (Type typeArgument : castB.getActualTypeArguments()) {
System.out.println("b"+typeArgument.getTypeName());
}
Type genericSuperclassC = stringC.getClass().getGenericSuperclass();
ParameterizedType castC = ParameterizedType.class.cast(genericSuperclassC);
for (Type typeArgument : castC.getActualTypeArguments()) {
System.out.println("c"+typeArgument.getTypeName());
}
Type genericSuperclassC1 = stringC1.getClass().getGenericSuperclass();
ParameterizedType castC1 = ParameterizedType.class.cast(genericSuperclassC1);
for (Type typeArgument : castC1.getActualTypeArguments()) {
System.out.println("c1"+typeArgument.getTypeName());
}
输出
java.lang.ClassCastException: Cannot cast java.lang.Class to java.lang.reflect.ParameterizedType
bjava.lang.String
cT
c1java.lang.String
总结:
- 类上泛型使用 (ParameterizedType)getGenericSuperclass() 的getActualTypeArguments()
- 如果类上没有指定具体的类型而是泛型类型 只有通过new C(){} (匿名内部类) 然后使用getActualTypeArguments() 才能获取到;
- 如果类上直接指定了具体的类型 则使用getActualTypeArguments() 直接可以获取到
- 如何是接口需要通过class.getGenericInterfaces()[0] 然后使用getActualTypeArguments() 与类获取的类似
为什么加个匿名内部类就可以获取了呢?
我们内部类在编译的时候其实就是生成一个XXX$XX.class的class文件,匿名内部类就是内部类的写法简写了,然后名称由编译器生成 如 XXX$1.class XXX$2.class
我们打开XXX$1.class看看
发现它已经把泛型类型确定为String了 所以 才可以在运行的时候获取到。满足我们上述例子的第三点
3.6 泛型实例
((Class) (((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0])).newInstance();
4.泛型原理
1.类型擦除
Java中的泛型是伪泛型,即在语法上支持泛型,但在编译阶段进行类型擦除保留为原始类型
原始类型 就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。
2.类型检查
ArrayList<String> objects = new ArrayList<>();
objects.add("");
objects.add(1);
报错:
java: 对于add(int), 找不到合适的方法
方法 java.util.Collection.add(java.lang.String)不适用
(参数不匹配; int无法转换为java.lang.String)
类型检查只针对它的调用者
ArrayList objects = new ArrayList<String>();
objects.add("");
objects.add(1);
编译通过
new ArrayList<String>().add(1);
编译不通过
3.类型强转
我们看一下
ArrayList<String> objects = new ArrayList<>();
objects.add("");
String s1 = objects.get(0);
字节码
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String
11: invokevirtual #5 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
14: pop
15: aload_1
16: iconst_0
17: invokevirtual #6 // Method java/util/ArrayList.get:(I)Ljava/lang/Object;
20: checkcast #7 // class java/lang/String
23: astore_2
24: return
LineNumberTable:
line 49: 0
line 50: 8
line 51: 15
line 52: 24
LocalVariableTable:
Start Length Slot Name Signature
0 25 0 args [Ljava/lang/String;
8 17 1 objects Ljava/util/ArrayList;
24 1 2 s1 Ljava/lang/String;
LocalVariableTypeTable:
Start Length Slot Name Signature
8 17 1 objects Ljava/util/ArrayList<Ljava/lang/String;>;
}
SourceFile: "main.java"
Process finished with exit code 0
我们看到 Code表里面 序号20 是 checkcast(类型强转) #7(#7 表示常量池中的) 也就是 class java/lang/String 将类型强转为 String
引入泛型最终的目的就是为了避免繁琐的强转
5.疑问
5.1 List和List<?>和List< Object >的区别?
引用变量类型 | 名称 | 可以接收的类型 | 能否添加元素 | 安全性 | 便利性 | 表述性 |
---|---|---|---|---|---|---|
List | 原始类型 | 任意的List | 可以添加任何元素 | ✔ | × | × |
List<?> | 通配符类型 | 可以接收List< E >的参数化类型 包括 原始类型List | 除了null 不可以添加任何元素 | ✔ | × | ✔ |
List< Object > | 实际类型为Object的参数化类型 | 只可以接收List和其本身类型 | 可以添加任何元素 | ✔ | ✔ | ✔ |
5.2 为什么 泛型类型 不能用于静态变量,静态方法?
static class C<T> extends B{
// 编译错误 'Main.C.this' cannot be referenced from a static context 不能从静态上下文中引用
static T t1;
//编译错误 'Main.C.this' cannot be referenced from a static context
public static T get(){
return t1;
}
}
因为泛型参数实例化是在 new对象的时候指定的,而静态变量和静态方法不需要创建对象
6.泛型工具
源于Mybatis
/**
* Copyright 2009-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.reflection;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
/**
*
* * Type
* * ParameterizedType 参数化类型 List<String>
* * Class 原始类型
* * GenericArrayType 数组类型 且组成元素是ParameterizedType和TypeVariable 如List<String>或T[]
* * TypeVariable 类型变量 List<T>
* * Type[] getBounds() 获取类型变量上边界,如未声明则为Object 如 List<T extends String> T的上界为String
* * D getGenericDeclaration() Declaration神明 获取声明该类型变量的原始类型 List<T extends String> List就为原始类型
* * String getName(); T
* *
* * WildcardType Wildcard通配符 通配符类型 ? extends String
* * Type[] getUpperBounds()
* * Type[] getLowerBounds()
*
* Method
* Class<?>[] method.getParameterTypes(); 参数类型
* Type[] method.getGenericParameterTypes(); 通用参数类型 带泛型
*
* Field
* Type getGenericType();
* Class<?> getType();
* Field getField(); 只能获取本类和父类 public的
* Field getDeclaredField(); 只能获取本类中声明的所有字段
*
*
* @author Iwao AVE!
*/
public class TypeParameterResolver {
/**
* @return The field type as {@link Type}. If it has type parameters in the declaration,<br>
* they will be resolved to the actual runtime {@link Type}s.
*/
public static Type resolveFieldType(Field field, Type srcType) {
Type fieldType = field.getGenericType();
Class<?> declaringClass = field.getDeclaringClass();
return resolveType(fieldType, srcType, declaringClass);
}
/**
* @return The return type of the method as {@link Type}. If it has type parameters in the declaration,<br>
* they will be resolved to the actual runtime {@link Type}s.
*/
public static Type resolveReturnType(Method method, Type srcType) {
Type returnType = method.getGenericReturnType();
Class<?> declaringClass = method.getDeclaringClass();
return resolveType(returnType, srcType, declaringClass);
}
/**
* @return The parameter types of the method as an array of {@link Type}s. If they have type parameters in the declaration,<br>
* they will be resolved to the actual runtime {@link Type}s.
*/
public static Type[] resolveParamTypes(Method method, Type srcType) {
Type[] paramTypes = method.getGenericParameterTypes();//generic 通用的
Class<?> declaringClass = method.getDeclaringClass(); //获取声明类
Type[] result = new Type[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
result[i] = resolveType(paramTypes[i], srcType, declaringClass);
}
return result;
}
/**
* Type
* ParameterizedType 参数化类型 List<String>
* Class 原始类型
* GenericArrayType 数组类型 且组成元素是ParameterizedType和TypeVariable 如List<String>或T[]
* TypeVariable 类型变量 List<T>
* Type[] getBounds() 获取类型变量上边界,如未声明则为Object 如 List<T extends String> T的上界为String
* D getGenericDeclaration() Declaration神明 获取声明该类型变量的原始类型 List<T extends String> List就为原始类型
* String getName(); T
*
* WildcardType Wildcard通配符 通配符类型 ? extends String
* Type[] getUpperBounds()
* Type[] getLowerBounds()
*
*
*
* @param type
* @param srcType
* @param declaringClass
* @return
*/
private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
// TypeVariable 类型变量 List<T>
if (type instanceof TypeVariable) {
return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
//ParameterizedType 参数化类型 List<String>
} else if (type instanceof ParameterizedType) {
return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
// 数组类型 [Ljava.lang.String;
} else if (type instanceof GenericArrayType) {
return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
} else {
return type;
}
}
private static Type resolveGenericArrayType(GenericArrayType genericArrayType, Type srcType, Class<?> declaringClass) {
Type componentType = genericArrayType.getGenericComponentType();
Type resolvedComponentType = null;
if (componentType instanceof TypeVariable) {
resolvedComponentType = resolveTypeVar((TypeVariable<?>) componentType, srcType, declaringClass);
} else if (componentType instanceof GenericArrayType) {
resolvedComponentType = resolveGenericArrayType((GenericArrayType) componentType, srcType, declaringClass);
} else if (componentType instanceof ParameterizedType) {
resolvedComponentType = resolveParameterizedType((ParameterizedType) componentType, srcType, declaringClass);
}
if (resolvedComponentType instanceof Class) {
return Array.newInstance((Class<?>) resolvedComponentType, 0).getClass();
} else {
return new GenericArrayTypeImpl(resolvedComponentType);
}
}
private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType, Class<?> declaringClass) {
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
Type[] typeArgs = parameterizedType.getActualTypeArguments();
Type[] args = new Type[typeArgs.length];
for (int i = 0; i < typeArgs.length; i++) {
if (typeArgs[i] instanceof TypeVariable) {
args[i] = resolveTypeVar((TypeVariable<?>) typeArgs[i], srcType, declaringClass);
} else if (typeArgs[i] instanceof ParameterizedType) {
args[i] = resolveParameterizedType((ParameterizedType) typeArgs[i], srcType, declaringClass);
} else if (typeArgs[i] instanceof WildcardType) {
args[i] = resolveWildcardType((WildcardType) typeArgs[i], srcType, declaringClass);
} else {
args[i] = typeArgs[i];
}
}
return new ParameterizedTypeImpl(rawType, null, args);
}
private static Type resolveWildcardType(WildcardType wildcardType, Type srcType, Class<?> declaringClass) {
Type[] lowerBounds = resolveWildcardTypeBounds(wildcardType.getLowerBounds(), srcType, declaringClass);
Type[] upperBounds = resolveWildcardTypeBounds(wildcardType.getUpperBounds(), srcType, declaringClass);
return new WildcardTypeImpl(lowerBounds, upperBounds);
}
private static Type[] resolveWildcardTypeBounds(Type[] bounds, Type srcType, Class<?> declaringClass) {
Type[] result = new Type[bounds.length];
for (int i = 0; i < bounds.length; i++) {
if (bounds[i] instanceof TypeVariable) {
result[i] = resolveTypeVar((TypeVariable<?>) bounds[i], srcType, declaringClass);
} else if (bounds[i] instanceof ParameterizedType) {
result[i] = resolveParameterizedType((ParameterizedType) bounds[i], srcType, declaringClass);
} else if (bounds[i] instanceof WildcardType) {
result[i] = resolveWildcardType((WildcardType) bounds[i], srcType, declaringClass);
} else {
result[i] = bounds[i];
}
}
return result;
}
/**
*
* 解析类型变量
* @param typeVar
* @param srcType
* @param declaringClass
* @return
*/
private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
Type result = null;
Class<?> clazz = null;
if (srcType instanceof Class) {
clazz = (Class<?>) srcType;
} else if (srcType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) srcType;
clazz = (Class<?>) parameterizedType.getRawType();
} else {
throw new IllegalArgumentException("The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
}
if (clazz == declaringClass) {
Type[] bounds = typeVar.getBounds();
if(bounds.length > 0) {
return bounds[0];
}
return Object.class;
}
Type superclass = clazz.getGenericSuperclass();//获取声明父类
result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);//扫描父类解析
if (result != null) {
return result;
}
Type[] superInterfaces = clazz.getGenericInterfaces();
for (Type superInterface : superInterfaces) {
result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
if (result != null) {
return result;
}
}
return Object.class;
}
/**
* @param typeVar 类型变量
* @param srcType 源类
* @param declaringClass
* @param clazz
* @param superclass
* @return
*/
private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz, Type superclass) {
Type result = null;
if (superclass instanceof ParameterizedType) {
ParameterizedType parentAsType = (ParameterizedType) superclass;
Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
if (declaringClass == parentAsClass) {
Type[] typeArgs = parentAsType.getActualTypeArguments();//{T,T}
TypeVariable<?>[] declaredTypeVars = declaringClass.getTypeParameters();
for (int i = 0; i < declaredTypeVars.length; i++) {
if (declaredTypeVars[i] == typeVar) {
if (typeArgs[i] instanceof TypeVariable) {
TypeVariable<?>[] typeParams = clazz.getTypeParameters();
for (int j = 0; j < typeParams.length; j++) {
if (typeParams[j] == typeArgs[i]) {
if (srcType instanceof ParameterizedType) {
result = ((ParameterizedType) srcType).getActualTypeArguments()[j];
}
break;
}
}
} else {
result = typeArgs[i];
}
}
}
} else if (declaringClass.isAssignableFrom(parentAsClass)) {
result = resolveTypeVar(typeVar, parentAsType, declaringClass);
}
} else if (superclass instanceof Class) {
if (declaringClass.isAssignableFrom((Class<?>) superclass)) {
result = resolveTypeVar(typeVar, superclass, declaringClass);
}
}
return result;
}
private TypeParameterResolver() {
super();
}
static class ParameterizedTypeImpl implements ParameterizedType {
private Class<?> rawType;
private Type ownerType;
private Type[] actualTypeArguments;
public ParameterizedTypeImpl(Class<?> rawType, Type ownerType, Type[] actualTypeArguments) {
super();
this.rawType = rawType;
this.ownerType = ownerType;
this.actualTypeArguments = actualTypeArguments;
}
@Override
public Type[] getActualTypeArguments() {
return actualTypeArguments;
}
@Override
public Type getOwnerType() {
return ownerType;
}
@Override
public Type getRawType() {
return rawType;
}
@Override
public String toString() {
return "ParameterizedTypeImpl [rawType=" + rawType + ", ownerType=" + ownerType + ", actualTypeArguments=" + Arrays.toString(actualTypeArguments) + "]";
}
}
static class WildcardTypeImpl implements WildcardType {
private Type[] lowerBounds;
private Type[] upperBounds;
private WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) {
super();
this.lowerBounds = lowerBounds;
this.upperBounds = upperBounds;
}
@Override
public Type[] getLowerBounds() {
return lowerBounds;
}
@Override
public Type[] getUpperBounds() {
return upperBounds;
}
}
static class GenericArrayTypeImpl implements GenericArrayType {
private Type genericComponentType;
private GenericArrayTypeImpl(Type genericComponentType) {
super();
this.genericComponentType = genericComponentType;
}
@Override
public Type getGenericComponentType() {
return genericComponentType;
}
}
}
欢迎关注公众号
参考:
https://docs.oracle.com/javase/tutorial/java/generics/index.html
https://blog.csdn.net/hanchao5272/article/details/79346471
Effective Java
群里老哥夏洛克