(翻译)反射处理java泛型

当我们声明了一个泛型的接口或类,或需要一个子类继承至这个泛型类,而我们又希望利用反射获取这些泛型参数信息。这就是本文将要介绍的ReflectionUtil就是为了解决这类问题的辅助工具类,为java.lang.reflect标准库的工具类。它提供了便捷的访问泛型对象类型(java.reflect.Type)的反射方法。

本文假设你已经了解java反射知识,并能熟练的应用。如果还不了解java反射知识,那么你可以先移步到Oracel反射课程,这可能是你开始学习反射的好起点.

ReflectionUtil中包含以下几种功能:

  1. 通过Type获取对象class;
  2. 通过Type创建对象;
  3. 获取泛型对象的泛型化参数;
  4. 检查对象是否存在默认构造函数;
  5. 获取指定类型中的特定field类型;
  6. 获取指定类型中的特定method返回类型;
  7. 根据字符串标示获取枚举常量;
  8. ReflectionUtil下载地址.

通过Type获取对象class

private static final String TYPE_NAME_PREFIX = "class ";

public static String getClassName(Type type) {
    if (type==null) {
        return "";
    }
    String className = type.toString();
    if (className.startsWith(TYPE_NAME_PREFIX)) {
        className = className.substring(TYPE_NAME_PREFIX.length());
    }
    return className;
}

public static Class<?> getClass(Type type)
            throws ClassNotFoundException {
    String className = getClassName(type);
    if (className==null || className.isEmpty()) {
        return null;
    }
    return Class.forName(className);
}

方法ReflectionUtil#getClass(Type)实现了从java.lang.reflect.Type获取java.lang.Class对象名称。这里利用了Type的toString方法获取所在类型的class。如“class some.package.Foo”,截取后部分class名称,在利用Class.forName(String)获取class对象。

通过Type创建对象

public static Object newInstance(Type type)
        throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    Class<?> clazz = getClass(type);
    if (clazz==null) {
        return null;
    }
    return clazz.newInstance();
}

方法ReflectionUtil#newInstance(Type type)实现根据Type构造对象实例。在这里输入的Type不能是抽象类、接口、数组类型、以及基础类型、Void否则会发生InstantiationException异常。

获取泛型对象的泛型化参数

首先假设我们有如下两个对象:

public abstract class Foo<T> {
    //content
}

public class FooChild extends Foo<Bar> {
    //content
}

怎么获取子类在Foo中传入的泛型Class类型呢?

比较常用的做法有以下两种:

强制FooChild传入自己的class类型(这也是比较常用的做法):

public abstract class Foo<T> {
    private Class<T> tClass;    

    public Foo(Class<T> tClass) {
        this.tClass = tClass;
    }
    //content
}

public class FooChild extends Foo<Bar> {
    public FooChild() {
        super(FooChild.class);
    }
    //content
}

利用反射获取:

public static Type[] getParameterizedTypes(Object object) {
    Type superclassType = object.getClass().getGenericSuperclass();
    if (!ParameterizedType.class.isAssignableFrom(superclassType.getClass())) {
        return null;
    }
    return ((ParameterizedType)superclassType).getActualTypeArguments();
}

方法ReflectionUtil#getParameterizedTypes(Object)利用反射获取运行时泛型参数的类型,并数组的方式返回。本例中为返回一个T类型的Type数组。

为了Foo得到T的类型我们将会如下使用此方法:

...
Type[] parameterizedTypes = ReflectionUtil.getParameterizedTypes(this);
Class<T> clazz = (Class<T>)ReflectionUtil.getClass(parameterizedTypes[0]);
...

注意:

在java.lang.reflect.ParameterizedType#getActualTypeArguments() documentation:的文档中你能看见如下文字:

in some cases, the returned array can be empty. This can occur. if this type represents
a non-parameterized type nested within a parameterized type.

当传入的对象为非泛型类型,则会返回空数组形式。

检查对象是否存在默认构造函数

public static boolean hasDefaultConstructor(Class<?> clazz) throws SecurityException {
    Class<?>[] empty = {};
    try {
        clazz.getConstructor(empty);
    } catch (NoSuchMethodException e) {
        return false;
    }
    return true;
}

方法ReflectionUtil#hasDefaultConstructor利用java.lang.reflect.Constructor检查是否存在默认的无参构造函数。

获取指定类型中的特定field类型

public static Class<?> getFieldClass(Class<?> clazz, String name) {

if (clazz==null || name==null || name.isEmpty()) {
    return null;
}
name = name.toLowerCase();
Class<?> propertyClass = null;
for (Field field : clazz.getDeclaredFields()) {
    field.setAccessible(true);
    if (field.getName().equals(name)) {
        propertyClass = field.getType();
        break;
    }
}
return propertyClass;

}

在某些情况下你希望利用已知的类型信息和特定的字段名字想获取字段的类型,那么ReflectionUtil#getFieldClass(Class<?>, String)可以帮助你。ReflectionUtil#getFieldClass(Class<?>, String) 利用Class#getDeclaredFields()获取字段并循环比较java.lang.reflect.Field#getName()字段名称,返回字段所对应的类型对象。

获取指定类型中的特定method返回类型

public static Class<?> getMethodReturnType(Class<?> clazz, String name) {
    if (clazz==null || name==null || name.isEmpty()) {
        return null;
    }   

    name = name.toLowerCase();
    Class<?> returnType = null;

    for (Method method : clazz.getDeclaredMethods()) {
        if (method.getName().equals(name)) {
            returnType = method.getReturnType();
            break;
        }
    }

    return returnType;
}

方法ReflectionUtil#getMethodReturnType(Class<?>, String)可以帮助你根据对象类型和方法名称获取其所对应的方法返回类型。ReflectionUtil#getMethodReturnType(Class<?>, String)利用Class#getDeclaredMethods()并以java.lang.reflect.Method#getName()比对方法名称,返回找到的方法的返回值类型(Method#getReturnType()).

根据字符串标示获取枚举常量

@SuppressWarnings({ "unchecked", "rawtypes" })
public static Object getEnumConstant(Class<?> clazz, String name) {
    if (clazz==null || name==null || name.isEmpty()) {
        return null;
    }
    return Enum.valueOf((Class<Enum>)clazz, name);
}

方法ReflectionUtil#getEnumConstant(Class<?>, String)为利用制定的枚举类型和枚举名称获取其对象。这里的名称必须和存在的枚举常量匹配。

ReflectionUtil下载地址

你可以从这里下载ReflectionUtil.java. 原英文版地址: http://qussay.com/2013/09/28/handling-java-generic-types-with-reflection/

时间: 2024-10-12 14:33:49

(翻译)反射处理java泛型的相关文章

java反射+java泛型,封装BaseDaoUtil类。供应多个不同Dao使用

当项目是ssh框架时,每一个Action会对应一个Service和一个Dao.但是所有的Ation对应的Dao中的方法是相同的,只是要查的表不一样.由于封装的思想,为了提高代码的重用性.可以使用java中的泛型+反射去实现最终的封装,将所有的Dao层的共同部分写一个BaseDaoUtil.而所有的Dao都继承这个类. 思路: ----->(1)反射+泛型 ----->(2)当生成子类对象(UserDao),调用空构造时(new UserDao()),子类的空构造会默认调用父类的空构造器(new

Java泛型反射机制(二)

/** * @author Administrator * 好处:泛型:1安全 2减少代码重用率 */ package com.test; import java.lang.reflect.Method; public class Test2 { public static void main(String[] args) { // TODO Auto-generated method stub // Gen<String> gen1 = new Gen<String>("

1.熟练的使用Java语言进行面向对象程序设计,有良好的编程习惯,熟悉常用的Java API,包括集合框架、多线程(并发编程)、I/O(NIO)、Socket、JDBC、XML、反射等。[泛型]\

. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java运行环境是一个包含了不同的类和程序的单一进程.线程可以被称为轻量级进程.线程需要较少的资源来创建和驻留在进程中,并且可以共享进程中的资源. 2. 多线程编程的好处是什么? 在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态. 3. 用户线程和守护线程有什么区别? 当我们在Java

java 反射和泛型-反射来获取泛型信息

通过指定对应的Class对象,程序可以获得该类里面所有的Field,不管该Field使用private 方法public.获得Field对象后都可以使用getType()来获取其类型. Class<?> type = f.getType();//获得字段的类型 但此方法只对普通Field有效,若该Field有泛型修饰,则不能准确得到该Field的泛型参数,如Map<String,Integer>; 为了获得指定Field的泛型类型,我们采用: Type gType = f.getG

Java反射的理解(六)-- 通过反射了解集合泛型的本质

Java反射的理解(六)-- 通过反射了解集合泛型的本质 上述写了那么多,我们可能会有个疑问,为什么要用反射,步骤比我们常规的加载类操作复杂多了,别急,这个问题我最后才解答,我们先来了解集合泛型的本质. 直接上代码: import java.lang.reflect.Method; import java.util.ArrayList; public class MethodDemo4 { public static void main(String[] args) { ArrayList li

Java核心技术卷一 6. java泛型程序设计

泛型程序设计 泛型程序设计:编写的代码可以被很多不同类型的对象所重用. 类型参数:使用<String>,后者可以省略,因为可以从变量的类型推断得出.类型参数让程序更具更好的可读性和安全性. 通配符类型:很抽象,让库的构建者编写出尽可能灵活的方法. 定义简单泛型类 泛型类就是具有一个或多个类型变量的类. //引用类型变量 T ,可以有多个类型变量,public class Pair<T, U>{...} public class Pair<T> { //类定义的类型变量制

通过反射了解集合泛型的本质

通过反射了解集合泛型的本质 import java.lang.reflect.Method; import java.util.ArrayList; /** * 通过反射了解集合泛型的本质 * @author shm * */ public class MethodDemo02 { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("hello"); list.add(

1月21日 - (转)Java 泛型

java泛型 什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样. 可以在集合框架(Collection framework)中看到泛型的动机.例如,Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象. 因为 M

java泛型的讲解

java泛型 什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样. 可以在集合框架(Collection framework)中看到泛型的动机.例如,Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象. 因为 M