Java反射的那些事(1)- 泛型和类型

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> Java反射的那些事(1)- 泛型和类型

最近在做一个 Map数据结构转换为指定 Class类型的工具,涉及了反射相关的知识点。借这个机会,整理下这方面的知识。这篇文章先来了解下泛型和Type类型的知识点。 

Java JDK从1.5开始引入泛型这个概念,在这之前只有原始类型而没有泛型。泛型实现了参数化类型的概念,使代码可以应用于多种类型。泛型这个术语的意思是指:“适用于许多许多类型“。但是Java这种泛型仅仅存在于编译阶段,在JVM运行的过程中,泛型相关信息将会被擦除。所以泛型只是Java语言的一个语法糖。比如 ListString ListInteger在运行时事实上是相同的类型。这两种形式都被擦除成它们的”原生“类型,即 List。下面我们先来看下Java中泛型的使用姿势。

``(泛型变量)

Java的泛型可以应用在接口、类和方法上,平时看到了``、 K V这些泛型变量,也叫 类型变量(Type Variable),就表示这里声明了一个泛型。常见通用的的一些泛型变量名如下:

  • `T(Type)`,表示`Type`(类型),常用的泛型变量名;
  • `K(Key)`、`V(Value)`,表示键值,通常用在`Map`里;
  • `E(Element)`,表示元素,通常用在`Collection`中;
  • `N(Number)`,表示数字; 泛型变量名可以完全自定义,大小写不限、字母个数不限,但是为了规范,最好采用通用的泛型变量名;
  • 
    // 接口上声明了泛型变量K,V
    public interface MapK,V {
            // 方法上使用接口声明的泛型变量K,V
        V put(K key, V value);
    }
    // 这里表示Collection和List使用的是同一个泛型变量E
    public interface ListE extends CollectionE {
       // 方法上使用接口声明的泛型变量E
       E get(int index);
    }
    // 类或者接口上未声明泛型变量
    public class QueryResponseUtils {
        // 只在方法中使用泛型变量,方法前要加声明泛型变量
        public  QueryResponse success(T data) {
            QueryResponse response = new QueryResponse();
            response.setSucceeded(true);
            response.setData(data);
            return response;
        }
    }
    

    ?(无限定通配符)

    无限定通配符经常与容器类配合使用,其中 ?代表 未知类型,即表示所有类型。当类型不确定的时候,可以使用。

    
        public void testWildcard() {
            printList(new ArrayListString());
            printList(new ArrayListInteger());
        }
    
        public void printList(List? list) {
            System.out.println(list);
        }
    

    但是涉及到 ?时的操作,一定与具体类型无关。比如 List size()方法。

    
        public void addNewElement(List? list) {
            // 编译失败,不能操作和类型相关的操作
            list.add("abc");
            //  只能添加null,因为null与类型无关
            list.add(null);
        }
        public void printSize(List? list) {
            // 操作和类型无关的方法,类似的还有remove等
            System.out.println(list.size());
        }
    

    ? extends (有上限的通配符)

    在这里, ? 代表类型 T T的子类,表示里面存的是 T及T的子类
    如果需要读取 T类型的元素,需要声明成 ? extends ,取出的元素的类型是 T。例如 List? extends Number,表示 List里面存的是 Number及其子类。此时不能往列表中添加元素,取出的元素类型是 Number

    
        public void addElement(List? extends Number list) {
            // 编译失败
            list.add(1);
        }
    
        public Number getElement(List? extends Number list) {
            // 得到上限类型Number
            Number number = list.get(0);
            return number;
        }
    

    由于使用了上界限定,那么它保证其中的元素一定是 Number,可以调用 get()方法得到类型为 Number的元素;
    但是 ? extends Number只是限定了上界,表示是 Number的子类,无法确定具体是哪个子类,故无法对其进行写入。对 ? extends来说,禁止所有的写入操作。

    ? super (有下限的通配符)

    在这里, ? 代表类型 T T的父类,表示里面存的是 T及T的父类。如果需要添加 T类型的元素,需要声明成 ?super ,例如 List? super Number,表示 List里面存的是 Number的父类,即 Object。此时只能向其中添加 Number及其子类。
    从其中取元素的时候,要注意取出元素的类型是 Object

    
        public void addElement1(List? super Number list) {
            // 添加Number类型及其子类
            list.add(1);
            list.add(1.0);
        }
    
        public Object getElement1(List? super Number list) {
            // 只能拿到Object类型
            Object number = list.get(0);
            return number;
        }
    

    由于使用了下界限定,那么需要保证写入的元素一定是 Number及其子类,可以调用 add()方法, 向其中添加 Integer Double Number等元素,不能添加 Number的父类。
    取数据时,只能取到 ? super ,相当于是没有上限的父类,那么就是 Object

    T ?的不同点

  • 类型参数`T`是一个确定的类型,而通配符`?`表示一个不确定的类型。
  • `T`只有`extends`一种限定方式,`T extends List`是合法的,`T super List`是不合法的。
  • `?`有`extends`与`super`两种限定方式,即`? extends List` 与`? super List`都是合法的。
  • `T`可用于多重限定,如 `T extends A & B`,通配符`?`不能进行多重限定。
  • 类型存储读取写入实例|------

    java.lang.reflect.Type

    Type是Java 1.5 为了引入泛型而新增的接口,是Java中所有类型的公共父接口,继承关系如下图:
    Java反射的那些事(1)- 泛型和类型如上图, Type主要包括四个子接口 ParameterizedType TypeVariable GenericArrayType WildcardType,以及最重要的实现类 Class。下面来详细介绍这四种类型的使用:

    java.lang.reflect.ParameterizedType(参数化类型)

    参数化类型代表通常的泛型类型,比如 CollectionString MapString, Integer。下面来看看它的三个方法:
    Java反射的那些事(1)- 泛型和类型

  • `Type getRawType()` 该方法是获取当前参数化类型的`类型`,比如上面的`MapString, Integer`,返回的是`Map.class`。
  • `Type getOwnerType()` 该方法是获取当前参数化类型`所在类的类型`,比如`Map.EntryString, String`,返回是`Map.class`。
  • `Type[] getActualTypeArguments()` 该方法是获取当前参数化类型的实际类型数组,即``里面的按顺序排列的所有类型,比如`MapString, Integer`,返回的是`[String.class, Integer.class]`。
  • 需要注意的是,只返回最外层``的类型,里面嵌套的类型不返回,比如 MapString, MapString, String,返回 [String.class, MapString, String]。其中 MapString, String也是一个 ParameterizedType
    下面我们来看下具体的例子。

    
    public class ParameterizedTypeTest {
    
        private MapString, Integer map;
    
        private Map.EntryString, String entry;
    
        private MapString, MapString, String nestedMap;
    
        private Field getField(Class? clazz, String fieldName) {
            return ReflectionUtils.findField(ParameterizedTypeTest.class, fieldName);
        }
    
        @Test
        public void testRawType() {
            Field field = getField(ParameterizedTypeTest.class, "map");
            Type genericType = field.getGenericType();
    
            Assert.assertTrue(genericType instanceof ParameterizedType);
    
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            // 获取本身的类型
            Assert.assertEquals(Map.class, parameterizedType.getRawType());
        }
    
        @Test
        public void testOwnerType() {
            Field field = getField(ParameterizedTypeTest.class, "entry");
            Type genericType = field.getGenericType();
    
            Assert.assertTrue(genericType instanceof ParameterizedType);
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            // 获取所在类的Type
            Assert.assertEquals(Map.class, parameterizedType.getOwnerType());
        }
    
        @Test
        public void testActualTypeArguments() {
            Field field = getField(ParameterizedTypeTest.class, "map");
            Type genericType = field.getGenericType();
            Assert.assertTrue(genericType instanceof ParameterizedType);
    
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            // 获取实际参数类型
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            Assert.assertEquals(String.class, actualTypeArguments[0]);
            Assert.assertEquals(Integer.class, actualTypeArguments[1]);
        }
    
        @Test
        public void testNestedActualTypeArguments() {
            Field field = getField(ParameterizedTypeTest.class, "nestedMap");
            Type genericType = field.getGenericType();
            Assert.assertTrue(genericType instanceof ParameterizedType);
            
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            // 获取实际参数类型
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            Assert.assertEquals(String.class, actualTypeArguments[0]);
    
            // actualTypeArguments[1]也是参数化类型
            Assert.assertTrue(actualTypeArguments[1] instanceof ParameterizedType);
            Assert.assertEquals(Map.class, ((ParameterizedType)actualTypeArguments[1]).getRawType());
        }
    }
    

    java.lang.reflect.TypeVariable(类型变量)

    类型变量指的是常见的 T K V等这些范型变量。由于 Java的泛型信息在编译时会被转换成一个特定的类型,而 TypeVariable就有用来反映在虚拟机编译该泛型前的信息。以下是它拥有的具体方法:
    Java反射的那些事(1)- 泛型和类型

  • `Type[] getBounds()` 获取当前类型的上边界数组(可存在多个,因为`extends`可用`&`限定多个上边界),如果没有指定上边界,则默认为`Object`;
  • `D getGenericDeclaration()` 返回当前类型所在的类的Type;
  • `String getName()`获取当前类型的名称;
  • 
    public class TypeVariableTest {
        class TypeVariableObjectK, V extends Number {
            private K key;
            private V value;
        }
        @Test
        public void testTypeVariable() {
            Field kField = ReflectionUtils.findField(TypeVariableObject.class, "key");
            Field vField = ReflectionUtils.findField(TypeVariableObject.class, "value");
    
            Type kGenericType = kField.getGenericType();
            Type vGenericType = vField.getGenericType();
    
            Assert.assertTrue(kGenericType instanceof TypeVariable);
            Assert.assertTrue(vGenericType instanceof TypeVariable);
    
            TypeVariable kTypeVariable = (TypeVariable) kGenericType;
            TypeVariable vTypeVariable = (TypeVariable) vGenericType;
    
            Assert.assertEquals("K", kTypeVariable.getName());
            Assert.assertEquals("V", vTypeVariable.getName());
    
            Assert.assertEquals(TypeVariableObject.class, kTypeVariable.getGenericDeclaration());
            Assert.assertEquals(TypeVariableObject.class, vTypeVariable.getGenericDeclaration());
    
            Assert.assertEquals(Object.class, kTypeVariable.getBounds()[0]);
            Assert.assertEquals(Number.class, vTypeVariable.getBounds()[0]);
        }
    }
    

    java.lang.reflect.GenericArrayType(泛型数组类型)

    组成数组的元素中有泛型,那么就实现了该接口,它的组成元素可以是 ParameterizedType类型或者 TypeVariable类型。如果元素不是泛型,那就是普通的 Class类型。
    Java反射的那些事(1)- 泛型和类型

  • `Type getGenericComponentType()` 获取泛型数组的实际参数化类型,比如`Map[]`,就返回`Map.class`;
  • 
    public class GenericArrayTypeTest {
    
        private class GenericComponentTypeObjectT {
            private T[] typeVariableArray;
            private MapString, String[] parameterizedTypeArray;
            private Integer[] wrapperArray;
            private int[] primitiveArray;
    
        }
        @Test
        public void testGetGenericComponentType() {
            // 元素为TypeVariable
            Field typeVariableArrayField = ReflectionUtils.findField(GenericComponentTypeObject.class, "typeVariableArray");
            Type typeVariableArrayGenericType = typeVariableArrayField.getGenericType();
            Assert.assertTrue(typeVariableArrayGenericType instanceof GenericArrayType);
            Type typeVariableArrayGenericComponentType = ((GenericArrayType) typeVariableArrayGenericType).getGenericComponentType();
            Assert.assertTrue(typeVariableArrayGenericComponentType instanceof TypeVariable);
            Assert.assertEquals("T", typeVariableArrayGenericComponentType.getTypeName());
            // 元素为ParameterizedType
            Field parameterizedTypeArrayField = ReflectionUtils.findField(GenericComponentTypeObject.class, "parameterizedTypeArray");
            Type parameterizedTypeArrayGenericType = parameterizedTypeArrayField.getGenericType();
            Assert.assertTrue(parameterizedTypeArrayGenericType instanceof GenericArrayType);
            Type parameterizedTypeArrayGenericComponentType = ((GenericArrayType) parameterizedTypeArrayGenericType).getGenericComponentType();
            Assert.assertTrue(parameterizedTypeArrayGenericComponentType instanceof ParameterizedType);
            // 元素为原生基本类型
            Field primitiveArrayField = ReflectionUtils.findField(GenericComponentTypeObject.class, "primitiveArray");
            Type primitiveArrayGenericType = primitiveArrayField.getGenericType();
            Assert.assertTrue(primitiveArrayGenericType instanceof Class);
            Assert.assertEquals(int.class, ((Class) primitiveArrayGenericType).getComponentType());
            // 元素为普通Class类型
            Field wrapperArrayField = ReflectionUtils.findField(GenericComponentTypeObject.class, "wrapperArray");
            Type wrapperArrayGenericType = wrapperArrayField.getGenericType();
            Assert.assertTrue(wrapperArrayGenericType instanceof Class);
            Assert.assertEquals(Integer.class, ((Class) wrapperArrayGenericType).getComponentType());
        }
    }
    

    java.lang.reflect.WildcardType(通配符类型)

    通配符类型,所有带 ?的类型,比如 ? ? extend Number WildcardType包含两个方法,如下:
    Java反射的那些事(1)- 泛型和类型

  • `Type[] getUpperBounds()` 获取上边界类型的数组。如果没有限定上边界,则上边界为`Object.class`,返回数组`Type[Object.class]`。
  • `Type[] getLowerBounds()` 获取下边界类型的数组。如果没有限定下边界,则返回空数组`Type[0]`。 下面我们来看下具体的例子。
  • 
    public class WildcardTypeTest {
    
        private class WildcardTypeObject {
            private List? wildcardList;
    
            private List? extends Number upperBoundList;
    
            private List? super Number lowerBoundList;
        }
        @Test
        public void testGetGenericComponentType() {
            Field wildcardListField = ReflectionUtils.findField(WildcardTypeObject.class, "wildcardList");
            Type wildcardListGenericType = wildcardListField.getGenericType();
            Type wildcardListActualType= ((ParameterizedType) wildcardListGenericType).getActualTypeArguments()[0];
            Assert.assertTrue(wildcardListActualType instanceof WildcardType);
    
            Field upperBoundListField = ReflectionUtils.findField(WildcardTypeObject.class, "upperBoundList");
            Type upperBoundListGenericType = upperBoundListField.getGenericType();
            Type upperBoundListActualType= ((ParameterizedType) upperBoundListGenericType).getActualTypeArguments()[0];
            Assert.assertTrue(upperBoundListActualType instanceof WildcardType);
            Assert.assertEquals(Number.class, ((WildcardType) upperBoundListActualType).getUpperBounds()[0]);
            Assert.assertEquals(0,   ((WildcardType) upperBoundListActualType).getLowerBounds().length);
    
            Field lowerBoundListField = ReflectionUtils.findField(WildcardTypeObject.class, "lowerBoundList");
            Type lowerBoundListGenericType = lowerBoundListField.getGenericType();
            Type lowerBoundListActualType= ((ParameterizedType) lowerBoundListGenericType).getActualTypeArguments()[0];
            Assert.assertTrue(lowerBoundListActualType instanceof WildcardType);
            Assert.assertEquals(Number.class, ((WildcardType) lowerBoundListActualType).getLowerBounds()[0]);
            Assert.assertEquals(Object.class,   ((WildcardType) lowerBoundListActualType).getUpperBounds()[0]);
        }
    }
    

    原文始发于微信公众号(xiaogan的技术博客):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> Java反射的那些事(1)- 泛型和类型


     上一篇
    11. Container With Most Water 11. Container With Most Water
    本题来自 LeetCode:11. Container With Most Water[1] 题目描述Given n non-negative integers a1, a2, …, an , where each represents a
    2021-04-05
    下一篇 
    80. 删除排序数组中的重复项 II 80. 删除排序数组中的重复项 II
    本题来自 LeetCode:80. 删除排序数组中的重复项 II[1] 题目描述给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O
    2021-04-05