Java 泛型从精通到陌生

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泛型类型

常用泛型说明
EElement 表示在集合中存放的元素
TType 表述Java类
KKey 映射-键
VValue 映射-值
NNumber 数值类型

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://www.pdai.tech/md/java/basic/java-basic-x-generic.html#%E6%B3%9B%E5%9E%8B%E7%9A%84%E4%B8%8A%E4%B8%8B%E9%99%90

https://blog.csdn.net/hanchao5272/article/details/79346471

Effective Java

群里老哥夏洛克
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值