在jirb里想试试这个的:
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之后也能达到类似的效果。