草庐IT

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

coder 2024-03-21 原文

如何使用反射检查给定对象是否是方法的有效参数(其中参数和对象是泛型类型)?

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

在玩反射方法调用时,我认为调用具有特定类型参数的所有方法会很好。这适用于原始类型,因为您可以调用 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, number)) {
                    try {
                        method.invoke(null, number);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static <T> boolean canBeParameterOf(Method 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接口(interface)不提供所需的方法。在这里和那里的 Actor 肯定可以帮助找到这一点(另请参阅我的第二次尝试)
  • reflectiveCall的论据实际上需要是所需参数类型的子类(注意 {} 中的 new 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, number);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
        public static <T> boolean canBeParameterOf(Method method, 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);
        }
    
    }
    

    它实际上打印了正确的解决方案。但这也不是没有消极的一面:
  • reflectiveCall的用户需要传递类型参数,这是不必要的和乏味的。至少在编译时检查正确的调用。
  • 类型参数之间的继承没有完全考虑,肯定还有很多情况需要在canBeParameterOf中实现。 (例如输入参数)。
  • 而最大的问题是:类型参数本身不能是泛型的,所以 ListList Integers的s不能用作论据。

  • 问题

    有什么我可以做的不同的事情来尽可能接近我的目标吗?我是坚持使用匿名子类还是传递类型参数?目前,我将通过提供参数来解决,因为这可以让您在编译时安全。

    递归检查参数类型时有什么需要注意的吗?

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

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

    只是为了让事情清楚,例如,我知道以下内容,但尽量保持示例干净:
  • 这可以在没有反射的情况下解决(同时重新设计一些需求并使用例如接口(interface)和内部类)。这是为了学习目的。我想解决的问题实际上要大得多,但这就是它归结起来的原因。
  • 我可以使用注释,而不是使用命名模式。我实际上是这样做的,但我希望这些示例尽可能独立。
  • getGenericParameterTypes() 返回的数组可能为空,但让我们假设所有方法都有一个参数,并且这是事先检查过的。
  • 可能有非静态方法在 null 时失败叫做。假设没有。
  • catch条件可以更具体。
  • 一些 Actor 需要更安全。
  • 最佳答案

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



    或多或少,但最好使用 super type token 子类化模式而不是子类化您的值类型。毕竟,您的值类型可能不允许子类。使用类型标记模式允许您接受泛型类型,而接受 Class作为类型参数只允许原始类型(这就是为什么您必须采用 List 的组件类型,而不是类型本身)。
    public static <T> void reflectiveCall(TypeToken<T> type, T value)
    

    Guava 非常支持创建和使用 TypeToken s。到目前为止,创建一个最简单的方法是创建一个匿名子类(注意:如果要重用它,请将其设为常量):
    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);
    }
    

    关于java - 反射性地检查对象是否是方法的有效泛型参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16450569/

    有关java - 反射性地检查对象是否是方法的有效泛型参数的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    4. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

      我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

    5. Ruby 方法() 方法 - 2

      我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby​​-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco

    6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    7. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

      exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

    8. ruby - Highline 询问方法不会使用同一行 - 2

      设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

    9. ruby - RSpec - 使用测试替身作为 block 参数 - 2

      我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

    10. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

      我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

    随机推荐