Kotlin使用反射获取泛型信息

  • 在java中,使用反射的代码实例如下:

class Student {
    String name;
    Integer age;

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

abstract class BaseService<T> {
    abstract int save(T t);
}

interface StudentService<T> {
    List<T> findStudent(String name, Integer age);
}

class StudentServiceImpl extends BaseService<Student> implements StudentService<Student> {

    @Override
    int save(Student student) {
        return 0;
    }

    @Override
    public List<Student> findStudent(String name, Integer age) {
        return Arrays.asList(new Student[]{new Student("Jack", 20), new Student("Li", 30)});
    }
}

public class JDemo {

    public static void main(String[] args) {
        StudentServiceImpl studentService = new StudentServiceImpl();
        studentService.save(new Student("Tom", 30));
        studentService.findStudent("Jack", 20);
        //反射API调用示例
        final Class<? extends StudentServiceImpl> studentServiceClass = studentService.getClass();
        //getClasses得到该类及其父类所有的public的内部类。
        //getDeclaredClasses得到该类所有的内部类,除去父类的。
        Class<?>[] classes = studentServiceClass.getDeclaredClasses();
        Annotation[] annotations = studentServiceClass.getAnnotations();
        ClassLoader classLoader = studentServiceClass.getClassLoader();
        Field[] fields = studentServiceClass.getDeclaredFields();
        Method[] methods = studentServiceClass.getDeclaredMethods();
        try {
            methods[0].getName();
            methods[0].invoke(studentService,"jack",20);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

通过反射,可以获取一个类的注解、方法、成员变量等。那么能不能通过反射获取到泛型信息呢?我们知道java中的泛型擦除。在程序运行时,无法得到自己本身的泛型信息。而当这个类继承了一个父类,父类中有泛型的信息时,那么可以通过getGenericSuperclass()方法得到一个父类的泛型信息。getGenericSuperclass()是Generic继承的特例,对于这种情况子类会保持父类的Generic参数类型。返回一个ParameterizedType。另外我们所说的java泛型在字节码中会被擦除,并不总是擦除为Object类型,而是擦除到上限类型。
在Kotlin也是一样的泛型机制。所以通过反射能拿到的也只能是有继承父类泛型信息的子类泛型。

class A<T>
open class C<T>

class B<T> : C<Int>()

fun fooA() {
    val parameterizedType = A<Int>()::class.java.genericSuperclass as ParameterizedType
    val actualTypeArguments = parameterizedType.actualTypeArguments
    for (type in actualTypeArguments) {
        val typeName = type.typeName
        println("typeName=$typeName")
    }
}

运行fooA(),会报错

Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
	at com.k.d.TestKt.fooA(Test.kt:11)
	at com.k.d.Test.main(Test.kt:24)
fun fooB(){
    val parameterizedType = B<Int>()::class.java.genericSuperclass as ParameterizedType
    val actualTypeArguments = parameterizedType.actualTypeArguments
    for (type in actualTypeArguments) {
        val typeName = type.typeName
        println("typeName=$typeName")
    }
}

执行下面的代码可以运行成功。

typeName=java.lang.Integer
  • 下面通过一个简单的实例来说明Kotlin中反射怎样获取泛型代码的基本信息。

/**
 * 声明一个父类 BaseContainer
 */
open class BaseContainer<T>

/**
 * 声明一个Container 继承BaseContainer
 */
class Container<T : Comparable<T>> : BaseContainer<Int> {
    val elements: MutableList<T>

    constructor(elements: MutableList<T>) : super() {
        this.elements = elements
    }

    fun sort(): Container<T> {
        elements.sort()
        return this
    }

    override fun toString(): String {
        return "Container(elements=$elements)"
    }

}


object Test {


    @JvmStatic
    fun main(args: Array<String>) {
        //声明一个Container实例
        val container = Container(mutableListOf(1, 2, 3, 4, 5))
        // 获取Container的KClass对象引用
        val kClazz = container::class
        // KClass对象的typeParameters属性中存有类型参数的信息
        val typeParameters = kClazz.typeParameters
        //typeParameters 取数组的第一个
        val kTypeParameter: KTypeParameter = typeParameters[0]
        // kTypeParameter有下面等属性
        println(kTypeParameter.isReified) // false
        println(kTypeParameter.name) // T
        println(kTypeParameter.upperBounds) // [kotlin.Comparable<T>]
        println(kTypeParameter.variance) // INVARIANT

        val constructors = kClazz.constructors
        for (KFunction in constructors) {
            KFunction.parameters.forEach {
                val name = it.name
                val type = it.type
                println("name=$name") // name=elements
                println("type=$type") // type=kotlin.collections.MutableList<T>
                for (KTypeProjection in type.arguments) {
                    println(KTypeProjection.type) // T
                }
            }
        }
    }
}

发布了119 篇原创文章 · 获赞 28 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/ldxlz224/article/details/98376852