昨晚在开发PDF工具的时候,执行了一小段JRuby代码,结果报错如下:

pdPCS = Java::OrgApachePdfboxPdmodel::PDPageContentStream.new(doc, page)pdPCS.drawImage(pdImage, 0, 0, 100, 100)

(NoMethodError) undefined method `drawImage' for #<Java::OrgApachePdfboxPdmodel::PDPageContentStream:0x4ff0bfdf>

下意识的想是不是方法drawImage的名称错了,或是方法参数的类型错了,检查也没发现问题。接着打印了一下pdPCS.methods,搜索居然也没找到drawImage方法,感觉有小坑在等我了。

错误提示让我莫名其妙,赶紧把写了个原生的 Java 版本的代码,运行结果完全正确,没有任何问题。只能看看PDPageContentStream的源码了,发现 drawImage 是继承父类 PDAbstractContentStream 的方法,并且是重载方法。

事情到这里反而明朗了,基本知道错误的原因了。由于Ruby支持缺省参数和可变参数,在定义方法时不指定参数类型,因此JAVA因为参数类型不同而产生的重载也没必要了。但是 JRuby 怎么调用 Java 类的重载(overload)方法,这个问题以前在脑海中浮现过。但是转念想JRuby项目的开发大神组坑定有解决方案的,不可能有这种小坑让人不爽,当时想还是留待以后碰到再研究。写代码想放弃思考,这是不可能的,总有一天让你补回来。

找到问题的根本原因,不用再纠结啥了。解决错误有2个方法,一个是 workaround,变通的方法快,写个Java类传参调用就可以了。另一个,是研究 JRuby 的文档,找官方给的解决方案。Workaround 这种临时变通的方案肯定不行,不解决总像一根刺在肉中让人不那么顺畅。为了节约时间 Google 和百度了先,没搜索到答案。看文档前先试了下 :java_send 方法,差不多一样的错误。

pdPCS.java_send(:drawImage,[Java::OrgApachePdfboxPdmodelGraphicsImage::PDImageXObject, Java::float, Java::float, Java::float, Java::float], pdImage, 0, 0, 100, 100)

org.jruby.embed.InvokeFailedException: (NameError) java method not found: org.apache.pdfbox.pdmodel.PDPageContentStream. drawImage(org.apache.pdfbox.pdmodel. graphics.image.PDImageXObject,float,float,float,float)

粗略浏览了一下,看到 “bound-and-unbound-java-methods-with-java_method”,顺便贴下原文。

bound-and-unbound-java-methods-with-java_method

Bound and Unbound Java methods with java_method

java_send relies on reflection and may lead to poor performance in some cases. Each time it is called, the desired method must be relocated. With the java_method method you can get a reference to any overloaded Java method as a Ruby Method object:

# get a bound Method based on the add(int, Object) method from ArrayList

add = list.java_method :add, [Java::int, java.lang.Object]

add.call(0, 'foo')

Similarly, an Unbound method object can be retrieved:

# get an UnboundMethod from the ArrayList class:

toString = ArrayList.java_method :toString

toString.bind(list).call # => [foo, foo]

Note: When specifying parameters to java_method, you access primitive data types via Java::, i.e. Java::char, Java::int. On the other hand, Classes must be specified directly. java.lang.String, java.io.InputStream, etc.

不仅明白了 JRuby 调用 Java 父类的重载方法,还彻底搞清楚了 :java_send 方法和 :java_method 方法的区别。照葫芦画瓢,2行代码就解决了 JRuby 调用Java父类的重载方法的问题。

drawImage = Java::OrgApachePdfboxPdmodel::PDPageContentStream.java_method :drawImage,[Java::OrgApachePdfboxPdmodelGraphicsImage::PDImageXObject, Java::float, Java::float, Java::float, Java::float]

drawImage.bind(pdPCS).call(pdImage, margin, margin, width, height)

  • 231
  • 0

共 0 回复