论坛首页 编程语言技术论坛

JRuby的类重定义……为什么不行?

浏览 2428 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-06-13  
在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之后也能达到类似的效果。
   发表时间:2008-06-16  
受教了,不太明白jruby对java类的命名,看到这一句:
Java::FullPackageNameInCamelCase::ClassName
有种恍然大悟的感觉.
jruby貌似没有一个正式的文档去说明一下怎么把java集成到jruby中.
0 请登录后投票
   发表时间:2008-06-17  
正式的文档我也没见到,不过也不能怪那些在业余时间坚持开发的开发者。
Sun虽然聘请了Thomas Enebo和Charles Nutter,不过整个小组应该还是人力不足吧?

话说这帖原本有个地方有个重要的错误:原帖里我是说java.lang.Integer这样的类型访问中java是一个变量,不过其实不是的。这里java也是一个方法,是一个不存在的方法。上面已经修正更新了……
0 请登录后投票
   发表时间:2008-06-19  
yangzhihuan 写道
受教了,不太明白jruby对java类的命名,看到这一句:
Java::FullPackageNameInCamelCase::ClassName
有种恍然大悟的感觉.
jruby貌似没有一个正式的文档去说明一下怎么把java集成到jruby中.

wiki上都说的那么详细了,你还要他们怎么说
0 请登录后投票
   发表时间:2008-06-19  
嘛,wiki上那个也算不上正式文档,而且有点过时了。有些已经deprecated的东西还在上面(而没提到deprecated的状态),例如说include_package。
Practical JRuby On Rails Web 2.0 Projects: Bringing Ruby On Rails To Java一书的附录里的介绍至少就比wiki上的新。有机会再写点什么详细点讨论这个。
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics