加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 反射性地检查对象是否是方法的有效泛型参数

发布时间:2020-12-15 00:42:29 所属栏目:Java 来源:网络整理
导读:如何使用反射检查给定对象是否是方法的有效参数(参数和对象是泛型类型)? 为了获得一些背景知识,这就是我想要实现的目标: 在使用反射方法调用时,我认为调用具有特定类型参数的所有方法会很好.这适用于原始类型,因为您可以在其类对象上调用isAssignableFrom(
如何使用反射检查给定对象是否是方法的有效参数(参数和对象是泛型类型)?

为了获得一些背景知识,这就是我想要实现的目标:

在使用反射方法调用时,我认为调用具有特定类型参数的所有方法会很好.这适用于原始类型,因为您可以在其类对象上调用isAssignableFrom(Class<?> c).然而,当你开始在混合中投入泛型时,它突然变得不那么容易,因为泛型不是反射原始设计的一部分,也不是因为类型擦除.

问题更大但它基本归结为以下几点:

理想解决方案

理想情况下代码

import java.lang.reflect.*;
import java.util.*;


public class ReflectionAbuse {

    public static void callMeMaybe(List<Integer> number) {
        System.out.println("You called me!");
    }

    public static void callMeAgain(List<? extends Number> number) {
        System.out.println("You called me again!");
    }

    public static void callMeNot(List<Double> number) {
        System.out.println("What's wrong with you?");
    }

    public static <T> void reflectiveCall(List<T> number){
        for(Method method : ReflectionAbuse.class.getDeclaredMethods()) {
            if(method.getName().startsWith("call")) {
                if(canBeParameterOf(method,number)) {
                    try {
                        method.invoke(null,number);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static <T> boolean canBeParameterOf(Method method,List<T> number) {
        // FIXME some checks missing
        return true;
    }

    public static void main(String[] args) {
        reflectiveCall(new ArrayList<Integer>());
    }

}

会打印

You called me!
You called me again!

无论T看起来如何(即它可能是另一个通用类型,例如List< List< Integer>>),这应该是可能的.

显然这不起作用,因为类型T在运行时被擦除和未知.

尝试1

我能做的第一件事是这样的:

import java.lang.reflect.*;
import java.util.*;


public class ReflectionAbuse {

            public static void callMeMaybe(ArrayList<Integer> number) {
                System.out.println("You called me!");
            }

            public static void callMeAgain(ArrayList<? extends Number> number) {
                System.out.println("You called me again!");
            }

            public static void callMeNot(ArrayList<Double> number) {
                System.out.println("What's wrong with you?");
            }

    public static <T> void reflectiveCall(List<T> number){
        for(Method method : ReflectionAbuse.class.getDeclaredMethods()) {
            if(method.getName().startsWith("call")) {
                if(canBeParameterOf(method,List<T> number) {
        return method.getGenericParameterTypes()[0].equals(number.getClass().getGenericSuperclass());
    }

    public static void main(String[] args) {
        reflectiveCall(new ArrayList<Integer>(){});
    }

}

只打印

You called me!

但是,这有一些额外的警告:

>它仅适用于直接实例,并且不考虑继承层次结构,因为Type接口不提供所需的方法.这里和那里的演员肯定有助于找到这一点(另见我的第二次尝试)
> reflectCall的参数实际上需要是有用参数类型的子类(注意新ArrayList< Integer>(){}中的{},它创建一个匿名内部类).这显然不太理想:创建不必要的类对象并且容易出错.这是我能想到绕过类型擦除的唯一方法.

尝试2

考虑到由于擦除而在理想解决方案中缺失类型,人们也可以将该类型作为一个非常接近理想的参数传递:

import java.lang.reflect.*;
import java.util.*;


public class ReflectionAbuse {

    public static void callMeMaybe(List<Integer> number) {
        System.out.println("You called me!");
    }

    public static void callMeAgain(List<? extends Number> number) {
        System.out.println("You called me again!");
    }

    public static void callMeNot(List<Double> number) {
        System.out.println("What's wrong with you?");
    }

    public static <T> void reflectiveCall(List<T> number,Class<T> clazz){
        for(Method method : ReflectionAbuse.class.getDeclaredMethods()) {
            if(method.getName().startsWith("call")) {
                Type n = number.getClass().getGenericSuperclass();
                if(canBeParameterOf(method,clazz)) {
                    try {
                        method.invoke(null,Class<T> clazz) {
        Type type = ((ParameterizedType)method.getGenericParameterTypes()[0]).getActualTypeArguments()[0];
        if (type instanceof WildcardType) {
            return ((Class<?>)(((WildcardType) type).getUpperBounds()[0])).isAssignableFrom(clazz);
        }
        return ((Class<?>)type).isAssignableFrom(clazz);
    }

    public static void main(String[] args) {
        reflectiveCall(new ArrayList<Integer>(),Integer.class);
    }

}

实际上打印正确的解决方案.但这也不是没有消极方面:

> reflectCall的用户需要传递类型参数,这是不必要和繁琐的.至少在编译时检查正确的调用.
>类型参数之间的继承没有被完全考虑在内,肯定还有许多需要在canBeParameterOf中实现的情况(例如类型参数).
>最大的问题:类型参数本身不能是通用的,因此不能将整数列表列表用作参数.

这个问题

有什么我可以做的不同,以尽可能接近我的目标?我是坚持使用匿名子类还是传递类型参数?目前,我将决定给出参数,因为这样可以节省编译时间.

在递归检查参数类型时,我需要注意什么?

是否有可能允许泛型在解决方案2中使用类型参数?

实际上,出于学习目的,我想推出自己的解决方案而不是使用库,尽管我不介意看一些内部工作.

为了清楚起见,我举例说明了以下内容,但尝试保持示例清洁:

>这可以在没有反射的情况下解决(同时重新设计一些要求并使用例如接口和内部类).这是出于学习目的.我想要解决的问题实际上要大得多,但这就是它归结为什么.
>我可以使用注释,而不是使用命名模式.我实际上是这样做的,但我想让这些例子尽可能保持独立.
> getGenericParameterTypes()返回的数组可能为空,但我们假设所有方法都有一个参数,并事先检查过.
>调用null时,可能存在失效的非静态方法.假设没有.
>捕捞条件可能更具体.
>有些演员需要更安全.

解决方法

Am I stuck with either using anonymous subclasses or passing the type parameter?

或多或少,但更好地使用super type token模式子类而不是子类化您的值类型.毕竟,您的值类型可能不允许子类.使用类型标记模式允许您接受泛型类型,而接受类作为类型参数仅允许原始类型(这就是您必须采用List的组件类型而不是类型本身的原因).

public static <T> void reflectiveCall(TypeToken<T> type,T value)

Guava非常支持创建和使用TypeToken.到目前为止,创建一个最简单的方法是创建一个匿名子类(注意:如果它将被重用,请将其设为常量):

reflectiveCall(new TypeToken<List<Integer>>() {},new ArrayList<Integer>());

一旦你有了这个,canBeParameterOf变得更容易实现.

public static boolean canBeParameterOf(Method method,TypeToken<?> givenType) {
    Type[] argTypes = method.getGenericParameterTypes();

    return argTypes.length != 0 && 
        TypeToken.of(argTypes[0]).isAssignableFrom(givenType);
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读