新手问题 Ruby - 反射

PeishuaiLee · 2017年07月16日 · 最后由 jakit 回复于 2017年07月17日 · 2614 次阅读

在 java 中:

String classPath="path1.path2.path3.path4...className";
Objectp[] args=new Object[new Object(),new Object()...];
Class clazz=Class.forName(classPath,args);
Object obj=clazz.newInstance();
String methodName="methodName";
Method method=clazz.getMethod(methodName);
Object returnObject=method.invoke(obj,args);

请问各位 大牛:在 ruby 中如何实现以上的机制,利用给定的类文件路径以及指定调用的方法名称,类名称与 ruby 文件名称一样

Java 的反射真难受。 Ruby 可以操作语言构建轻松自由的多,具体看看元编程这个书. 1, 通过字符串给你一个类 ==> '类名'.constantize 2, 通过字符串调用方法 ==> 对象.send('方法名')

关键词 method_missing

'ClassName'.constantize.new(args1).send('method_name', args2)

不过 constantize 是 rails 里的方法,把字符串转变成一个常量 http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html

看你代码是想动态加载一个类,然后知道方法名希望调用他的方法。

那你只需要 require 加载那个文件,如果可以使用 ActiveSupport,可以直接使用 ActiveSupport::Inflector#constantize 将字符串反射成类(和 Java 的 Class.forName 做的事情差不多,不过结果已经是具体的那个类型了),如果不能用 AS,可以看下他的源码,非常简单。

有类了,创建实例只需 klass.new(args),动态调用方法也仅需利用 send,如 obj.send :foo

劝你不要这么做,代码跳转都不能

用原生的的方法的话,取到你需要的类的模块和类常量,比如: ::Path1::Path2::Path3::Klass.new.send(method_name, args_arr)

如果拆分开来:

klass = ::Path1::Path2::Path3::Klass #取到类常量

instance = klass.send(:new, args_if_needed) #对应 java 的 new instance

instance.send(method_name, args)# 对应 invke

没有像 java 那么繁琐,而且 send 是可以调用私有方法的,不需要 像 java 一样 setAccessible

我只是提点建议:少用反射,效率很低,建议用 map(Hash)关联起来,另外,这样的用法你是在为以后、别的同事维护带来麻烦,因为字符串没有索引 parse 到,相当于你制造了 DSL 那样,后续只能看上下文,而且还不好排错,而且还跟危险

需要 登录 后方可回复, 如果你还没有账号请 注册新账号