2008-06-13
JRuby的类重定义……为什么不行?
在jirb里想试试这个的:
但是jirb总是提示self.int_value() + rhs那行有错。到底是为什么呢?
我也就是想这样而已……
结果我发现我犯了个超低级的错误。JRuby实现java.lang.Integer这样的类型访问实际上是通过method_missing机制,先是对java这个方法进行调用,方法不存在而通过method_missing机制,转换成一个值返回过来;然后对这个返回值调用其lang方法(或者还是说“发送"lang"消息”更合适?),不存在,于是在method_missing里做了点手脚来找到对应的package并返回又一个值,再调用Integer方法,同样是不存在然后通过method_missing找到对应的Java类。
在jirb里试试这个就知道上面说的是什么了:
这里的java啊com啊什么的都不是变量,而是不存在的方法而已。
于是,java.lang.Integer只是个会返回一个值的一连串方法调用。Java::JavaLang::Integer才是这个Java类在JRuby里的名字。形式是:
明白了这点之后,只要把上面的代码改一句就行:
T T 这种集成方法还是稍微surprise了一下。
不过回过头来想想,IronRuby/DLR的做法跟这个在表现上非常相似,但具体做法不同。JRuby主要通过method_missing机制,而IronRuby/dlr则通过注入全局变量:可以在host里为脚本运行环境注入一个名为System的NamespaceTracker类型全局变量,它会自动找到System下面的各个类以及各个子命名空间。我还是玩DLR的时间更多些,对IronRuby也比对JRuby更熟悉些。可惜现在的IronRuby离真正“能用”差得还好远。
在John Lam的blog上的一篇文章,Dynamic Silverlight Part 3: Integrating Silverlight with ASP.NET MVC里,就有这种要在IronRuby的代码里复写.NET原有类型的方法的使用场景:
silverlight.rb:
类名直接就UIElement了(多好 T T
UIElement是Silverlight/WPF的一个类。这里是要给RenderTransformOrigin方法写一个adaptor,让它的参数类型从WPF的Point改变为Ruby的数组,让整个类用起来更“Ruby”。很有趣。
当然前面是有相应的require的。JRuby在做了合适的require之后也能达到类似的效果。
class java.lang.Integer
def +(rhs)
self.int_value() + rhs
end
end
但是jirb总是提示self.int_value() + rhs那行有错。到底是为什么呢?
我也就是想这样而已……
irb(main):001:0> i = java.lang.Integer.new(1) # OK irb(main):002:0> i => #<Java::JavaLang::Integer:0x1fbc355 @java_object=1> irb(main):003:0> i.java_class => java.lang.Integer irb(main):004:0> i.int_value => 1 irb(main):005:0> i + 2 # not impl'd => 3 # what I'd like to see
结果我发现我犯了个超低级的错误。JRuby实现java.lang.Integer这样的类型访问实际上是通过method_missing机制,先是对java这个方法进行调用,方法不存在而通过method_missing机制,转换成一个值返回过来;然后对这个返回值调用其lang方法(或者还是说“发送"lang"消息”更合适?),不存在,于是在method_missing里做了点手脚来找到对应的package并返回又一个值,再调用Integer方法,同样是不存在然后通过method_missing找到对应的Java类。
在jirb里试试这个就知道上面说的是什么了:
irb(main):001:0> java => Java::Java irb(main):002:0> java() => Java::Java irb(main):003:0> com => Java::Com irb(main):004:0> com().sun() => Java::ComSun
这里的java啊com啊什么的都不是变量,而是不存在的方法而已。
于是,java.lang.Integer只是个会返回一个值的一连串方法调用。Java::JavaLang::Integer才是这个Java类在JRuby里的名字。形式是:
引用
Java::FullPackageNameInCamelCase::ClassName
明白了这点之后,只要把上面的代码改一句就行:
irb(main):001:0> class Java::JavaLang::Integer # 改了这里 irb(main):002:1> def +(rhs) irb(main):003:2> self.int_value + rhs irb(main):004:2> end irb(main):005:1> end => nil irb(main):006:0> i = java.lang.Integer.new 2 => #<Java::JavaLang::Integer:0x7efa96 @java_object=2> irb(main):007:0> i + 3 => 5
T T 这种集成方法还是稍微surprise了一下。
不过回过头来想想,IronRuby/DLR的做法跟这个在表现上非常相似,但具体做法不同。JRuby主要通过method_missing机制,而IronRuby/dlr则通过注入全局变量:可以在host里为脚本运行环境注入一个名为System的NamespaceTracker类型全局变量,它会自动找到System下面的各个类以及各个子命名空间。我还是玩DLR的时间更多些,对IronRuby也比对JRuby更熟悉些。可惜现在的IronRuby离真正“能用”差得还好远。
在John Lam的blog上的一篇文章,Dynamic Silverlight Part 3: Integrating Silverlight with ASP.NET MVC里,就有这种要在IronRuby的代码里复写.NET原有类型的方法的使用场景:
silverlight.rb:
class UIElement
alias_method :old_render_transform_origin=,
:render_transform_origin=
def render_transform_origin=(point)
self.old_render_transform_origin = Point.new(point.first, point.last)
end
end
类名直接就UIElement了(多好 T T
UIElement是Silverlight/WPF的一个类。这里是要给RenderTransformOrigin方法写一个adaptor,让它的参数类型从WPF的Point改变为Ruby的数组,让整个类用起来更“Ruby”。很有趣。
当然前面是有相应的require的。JRuby在做了合适的require之后也能达到类似的效果。
- by RednaxelaFX
- 浏览 (198)
- 评论 (4)
- 相关推荐


评论
Practical JRuby On Rails Web 2.0 Projects: Bringing Ruby On Rails To Java一书的附录里的介绍至少就比wiki上的新。有机会再写点什么详细点讨论这个。
Java::FullPackageNameInCamelCase::ClassName
有种恍然大悟的感觉.
jruby貌似没有一个正式的文档去说明一下怎么把java集成到jruby中.
wiki上都说的那么详细了,你还要他们怎么说
Sun虽然聘请了Thomas Enebo和Charles Nutter,不过整个小组应该还是人力不足吧?
话说这帖原本有个地方有个重要的错误:原帖里我是说java.lang.Integer这样的类型访问中java是一个变量,不过其实不是的。这里java也是一个方法,是一个不存在的方法。上面已经修正更新了……
Java::FullPackageNameInCamelCase::ClassName
有种恍然大悟的感觉.
jruby貌似没有一个正式的文档去说明一下怎么把java集成到jruby中.