I am using NetBeans, which has a particular file structure for projects, and also makes the creation of a JAR file very easy (just press F11 and the main project gets built and compressed into a JAR file inside the dist folder). For testing purposes, I created a project called JavaForRuby (and so a package name javaforruby), with a simple test class,
TestObject
, which has two instance variables, with setters and getters, and a static method, getStatus
, which returns a String
object.And so to the Ruby side. You need to be running JRuby, of course. I had some problems getting my Java classes to work, and this seemed to be resolved by using a more recent version of JRuby (1.3.0RC1), though I am sure it should work with older versions.
The first thing to do is introduce Java to Ruby, then you need to get your JAR linked up, and then your class loaded. Once that is done, the class can be accessed:
include Java
require "#{File.dirname(__FILE__)}/../../JavaForRuby/dist/javaforruby.jar"
include_class "javaforruby.TestObject"
p TestObject.getStatus
Creating and using an instance of a Java object is simple:
tobj = TestObject.new 'My Tester', 5
p tobj.class # => Java::Javaforruby::TestObject
p tobj.java_class # => class javaforruby.TestObject
p tobj.get_name # => "class javaforruby.TestObject"My Tester"
p tobj.get_value # => 5
tobj.set_name 'My Renamed Tester'
p tobj.get_name # => "My Renamed Tester"
Note that I used the conventions of Java to define the object, but the conventions of Ruby to access it from Ruby; JRuby has associated
get_name
in Ruby with getName()
in Java.Let us try using an array. JRuby adds a
to_java
method to Array
, and this takes a single parameter, the class of the objects for the array.ja = %w(one two three).to_java :string
p ja.java_class # => [Ljava.lang.String;
p ja[0] # => "one"
p ja[0].class # => String
ja.each { |e| p e } # => "one"
"two"
"three"
What happens if you do not tell it the object type? Well, the
each
method still works the same; the system works out what each object is. Accessing elements by their index is not so good (I did wonder if fetch might work, but throws an NoMethodError
exception).ja = %w(one two three).to_java
p ja.java_class # => [Ljava.lang.Object;
p ja[0] # => #<Java::JavaLang::String:0x155d3a3 @java_object=#<Java::JavaObject:0x57e787>>
p ja[0].class # => "Java::JavaLang::String
ja.each { |e| p e } # => "one"
"two"
"three"
We can combine the array and the custom class to make a Java array of Java objects in Ruby.
toja = [
TestObject.new('My First Tester', 5),
TestObject.new('Second', 93),
TestObject.new('Last Tester', 42),
].to_java
toja.each { |e| p e.get_name }
Hashes are no problem either.
hash = {:name => 'Fred', :age => 27}
java_hash = java.util.HashMap.new(hash)
p java_hash.get :name # => "Fred"
Rather than using the full path name in the method call, you can import each class. You should be able to import a complete package, though I could not get it to work with JRuby 1.3.0RC1 (but I could with 1.1.6).
java_import "java.util.HashMap"
# import "java.util"
# include_package "java.util"
hash = {:name => 'Fred', :age => 27}
java_hash = HashMap.new(hash)
p java_hash.get :name
You can use
import
rather than java_import
, and many tutorials indeed do this. However, import conflicts with Rake, and so, although your system will work as expected, your tests will fail with a NameError and complaints about a 'const_missing' (thanks to Charles Oliver for pointing this out).Also, I had a problem with
import java.io.File
. Ruby warns that the constant File
already exists ("warning: already initialized constant File"), and I think that this gets overwritten, and the code that is expecting the original value gets very confused... You need to restart your web server after that. Again, java_import
seems to fix this.See also:
http://wiki.jruby.org/wiki/Calling_Java_from_JRuby
Struggling with Ruby: Contents Page
2 comments:
Hi F2Andy,
thanks for pointing out the issue with import vs. java_import and rake.
I've run into exactly the same problem, with my unit tests working fine on the command line, but not within netbeans (whose custom unit test runner apparently loads rake).
I'll be using java_import form now on... .
-M
This is a great post - easy to read and follow! Thanks!
As I too am struggling, I followed your example, but continue to fail to launch my Java application. Can you see something wrong with what I'm doing?
[code]
include Java
require "#{File.dirname(__FILE__)}/h4j.jar"
include_class "com.nemon.h4j.H4JFrame"
#instead of example: p TestObject.getStatus
# try to launch holter
H4JFrame.new
[code]
This continues to throw a :
TypeError: no public constructors for Java::ComNemonH4j::H4JFrame
(root) at h4j_shorterTest.rb:9
Any thoughts or pointers would be greatly appreciated!
Thanks again!
Post a Comment