-
-
Notifications
You must be signed in to change notification settings - Fork 924
CallingJavaFromJRuby
All the following examples can be "run" either from the command line, or by using jirb_swing, the Swing-based IRB console that comes with JRuby. You can start jirb_swing like $ jruby -S jirb_swing
.
A special require 'java'
directive in your file will give you access to any bundled Java libraries (classes within your java class path). If you need to access Java libraries not contained with the Java class path we will show you how to do that in a later section.
The following code shows typical use. It should pop up a small window showing "Hello" on your screen.
# This is the 'magical Java require line'.
require 'java'
# With the 'require' above, we can now refer to things that are part of the
# standard Java platform via their full paths.
frame = javax.swing.JFrame.new("Window") # Creating a Java JFrame
label = javax.swing.JLabel.new("Hello")
# We can transparently call Java methods on Java objects, just as if they were defined in Ruby.
frame.add(label) # Invoking the Java method 'add'.
frame.setDefaultCloseOperation(javax.swing.JFrame::EXIT_ON_CLOSE)
frame.pack
frame.setVisible(true)
Note: If you are testing the example above in the Swing IRB console jirb_swing
, change the default close operation to DISPOSE_ON_CLOSE
, or HIDE_ON_CLOSE
unless you want jirb_swing
to also close when you close the second window.
Here's another example (showing results from testing these statements in the jirb
console).
Let's say you wanted to get a list of network interfaces. You can get Java API docs at java.net.NetworkInterface.
Here's how to access the methods from this Java Class from from JRuby:
irb(main):013:0> ni = java.net.NetworkInterface.networkInterfaces
=> #<#<Class:01x7e666f>:0x855a27 @java_object=java.net.NetworkInterface$1@821453>
ni
is a Ruby variable holding a Java Enumeration of NetworkInterfaces. You can see the Class ancestry for ni
like this:
irb(main):029:0> ni.class.ancestors
=> [#<Class:01x7e666f>, Java::JavaUtil::Enumeration, Enumerable, Java::JavaLang::Object, ConcreteJavaProxy, JavaProxy, JavaProxyMethods, Object, Java, Kernel]
Enumeration elements can't be accessed using Array#[]
syntax but they do appear as Arrays for many other purposes. You can find out both the Java and Ruby methods for an Enumeration of NetworkInterfaces like this:
irb(main):032:0> java.net.NetworkInterface.networkInterfaces.methods
=> ["__jsend!", "has_more_elements", "hasMoreElements", "next_element", "nextElement",
"each", "reject", "member?", "grep", "include?", "min", "sort", "any?", "partition",
"each_with_index", "collect", "find_all", "to_a", "inject", "detect", "map", "zip",
"sort_by", "max", "entries", "all?", "find", "select", ...]
You could also call #to_a then use Array#[] on them, etc.
Because JRuby supports the #each
method on Java Enumerations you could also do this:
irb(main):011:0> java.net.NetworkInterface.networkInterfaces.each {|i| puts i; puts }
name:en1 (en1) index: 5 addresses:
/63.138.152.170;
/fe80:0:0:0:21b:63ff:febf:4a9d%5;
name:en0 (en0) index: 4 addresses:
/63.138.152.125;
/fe80:0:0:0:21b:63ff:fe1e:b2da%4;
name:lo0 (lo0) index: 1 addresses:
/127.0.0.1;
/fe80:0:0:0:0:0:0:1%1;
/0:0:0:0:0:0:0:1%0;
To use resources within a jar file from JRuby, the jar file must either be on the classpath or be made available with the require
method:
require 'path/to/mycode.jar'
This require
makes the resources in mycode.jar
discoverable by later commands like import
and include_package
.
Note that loading jar-files via require
searches along the $LOAD_PATH
for them, like it would for normal ruby files.
If you need to load from an existing .class file, if it's relative to the current directory (for instance ./org/JavaFile.class exists) then you can just do java_import org.JavaFile
or java_import 'org.JavaFile'
.
If it's in "some other directory" (or classname isn't camel-case), the following has examples: http://www.ruby-forum.com/topic/216572#939791
Basically like this: $CLASSPATH << "target/classes"; java_import org.asdf.ClassName
where "target/classes/org/asdf/ClassName.class" exists.
If you have a .class file that is in your current directory (like no package, current directory has JavaFile.class in it), then java_import 'JavaFile'
works.
Note also that this will need to be a full path name or relative to the directory the script starts in, as the JVM doesn't seem to respond to Dir.chdir very well.
You can reference a Java class in JRuby in at least two different ways.
- The first is to map Java names like
org.foo.department.Widget
to Ruby nested modules format. This works as follows:
Java: org.foo.department.Widget
Ruby: Java::OrgFooDepartment::Widget
That is:
- Java packages all reside within the
Java
module. - The package path is then transformed by removing the dots and converting to CamelCase
This also means that, just as in Java, packages are not nested, but are each associated with their own unique module name.
- Second way: for the top-level Java packages
java
,javax
,org
, andcom
you can type in a fully qualified class name basically as in Java, for example,java.lang.System
ororg.abc.def.ClassName
You can get the same effect for your own (custom) top-level packages, as follows. Let's assume that your packages are callededu.school.department.Class
. Then, you define
def edu
Java::Edu
end
And then you can use usual Java package names like edu.school.department.Class
Note also that you must use the right capitalization, though see below for how you can change and assign it to your own liking.
You can always access any Java class that has been loaded or is in the classpath by specifying its full name (e.g. java.lang.System). With the java_import
statement, you can access the Java class via a constant that java_import creates for you in the current namespace. Let's consider how to make using java.lang.System
easier to use:
Note: For older scripts where import
was used we have deprecated that for java_import
to not conflict with rake.
require 'java'
java_import java.lang.System
version = System.getProperties["java.runtime.version"]
In this script when java_import executes it will make a new constant for you called System
. As this is happening in a top-level script it will be put into ::Object. Now in the next line you can just use System
.
As this is just a constant you need to be wary of redefining or papering over other constants. Consider an example like this:
require 'set'
java_import java.util.Set
In this case you defined the constant Set
on ::Object
from Ruby's set library and then you overwrote the constant Set
on ::Object
by calling java_import
. Luckily, in this case you will see a warning telling you that you changed a constant definition. This example also gives a cautionary tale.
Java and Ruby classes have an overlap on naming. So if you include a Java Class at a top-level scope it can mess up the world of Ruby since what you thought was a Ruby set suddenly becomes a Java one. Ruby's constant resolution is much more tricky than it seems so reviewing an article on constant resolution may help you to better understand how constants work. Our recommendation is to namespace your java_imports into the non-top-level namespace in a Ruby program of any size.
A more confusing example is when you unintentionally wallpaper over a constant:
require 'set'
module MyClass
java_import java.util.Set
Set # 1. ???
end
Set # 2. ???
The java_import
will define a Set
on MyClass
(e.g. MyClass::Set
). If you access Set
at 1 you will get this new constant. If you access Set
at 2 you will get Object::Set
. Both of these examples are just cases how constants in Ruby work but the implicit constant creation of java_import can trip programmers up.
It is also possible to call java_import from within methods and the rules for java_import is intended to add the constant to the class it is being called on (class method is self and instance method is self.class). We however recommend against doing this as it is not as intuitive as we had hoped. Try and import in module or class bodies like in the examples above.
Some people do not like the implicit side-effect nature of java_import
, so you can also get the same effect by reassigning a Java class to a new constant, like
require 'java'
Sys = java.lang.System
version = Sys.getProperties["java.runtime.version"]
Use include_package "package_name"
in a Ruby Module to support namespaced access to the Java classes in the package. This is similar to Java's package xxx.yyy.zzz;
format. It is also legal to use import "package_name"
, that is similar to import package_name.*
.
Example: using include_package
in a module
module MyApp
include_package 'org.apache.logging.log4j'
# now any class from "org.apache.logging.log4j" will be available within
# this module, ex if "org.apache.logging.log4j.LogManager" exists ...
Logger = LogManager.getLogger('MyApp')
end
Example: create a Ruby Module called JavaLang
that includes the classes in the Java package java.lang
.
module JavaLangDemo
include_package 'java.lang'
# alternately, use the #import method
#import 'java.lang'
end
Now you can prefix the desired Java Class name with JavaLangDemo::
to access the included Classes:
version = JavaLangDemo::System.out # java.lang.System.out
=> #<Java::JavaIo::PrintStream:0x30a7202>
processors = JavaLangDemo::Runtime.getRuntime.availableProcessors
=> 2
All Java classes in the package will become be available in this class/module, unless a constant with the same name as a Java class is already defined.
The use of the Module name to scope access to the imported Java class is also helpful in cases where the Java class has the same name as an existing Ruby class.
For example if you need to create an instance of a java.io.File
object, this code will work:
java_import java.io.File
newfile = File.new("file.txt")
=> #<Java::JavaIo::File:0xdc6f00 @java_object=file.txt>
However you've now redefined the Ruby constant File and can no longer access the Ruby File class. Executing this:
File.open('README', 'r') {|f| puts f.readline }
Will produce this error:
NoMethodError: private method `open' called for Java::JavaIo::File:Class
If instead you create a module called JavaIO
and include the package in the module definition:
module JavaIO
include_package "java.io"
end
You can now create a new instance of the Java class File without shadowing the Ruby version of the File class:
newfile = JavaIO::File.new("file.txt")
=> #<Java::JavaIo::File:0x15619c @java_object=file.txt>
And the Ruby File class is still accessible:
File.open('README.md', 'r') {|f| puts f.readline }
JRuby - A Java implementation of the Ruby language
=> nil
You can set const_missing
to make them accessible in more than just a module. See http://www.ruby-forum.com/topic/216629.
Currently, include_package
lookup doesn't work within any sub-modules or sub-classes e.g. running :
module GUI
include_package 'javax.swing'
include_package 'java.awt.image' # BufferedImage
# bellow won't work without accessing BufferedImage before :
class ShowImage < JFrame
BufferedImage.new(10, 20, BufferedImage::TYPE_BYTE_BINARY)
end
end
NameError: uninitialized constant GUI::ShowImage::BufferedImage
from org/jruby/RubyModule.java:2743:in `const_missing'
from (irb):6:in `ShowImage'
from (irb):5:in `GUI'
from (irb):1:in `evaluate'
You could override const_missing
, though (see above), if you wanted that behavior.
There is a rubygem that implements this already: nested_inherited_jruby_include_package.
It patches include_package
to ensure included packages are nested and inherited, thus working in nested-modules/nested-classes and sub-modules/sub-classes. Note the caveat that nested_inherited_jruby_include_package globally hooks into Module#const_missing
after aliasing to const_missing_without_nested_inherited_jruby_include_package
, which it calls if a constant is not found in any Java package. As such, keep its load order in mind if you have other code hooking into Module#const_missing
.
Example (using nested_inherited_jruby_include_package):
require 'nested_inherited_jruby_include_package'
module GUI
include_package 'javax.swing'
include_package 'java.awt.image' # BufferedImage
# bellow won't work without accessing BufferedImage before :
class ShowImage < JFrame
p BufferedImage.new(10, 20, BufferedImage::TYPE_BYTE_BINARY)
end
end
#<Java::JavaAwtImage::BufferedImage:0x2f112965>
Java enum
s are accessible from Ruby code as constants:
lock.try_lock(5000, java.util.concurrent.TimeUnit::MILLISECONDS)
or
java_import java.util.concurrent.TimeUnit
lock.try_lock(5000, TimeUnit::MILLISECONDS)
JRuby automatically binds the following names in the context of a class to the top-level Java packages: com
, org
, java
, javax
and javafx
. This means that you can reference these packages without having to explicitly require or import them. This takes effect for all Ruby classes in an application where a require 'java'
appears. This binding takes place in precedence to the classes method_missing handling.
If you do not want this behaviour for a specific class, you can undefine it for that class. Here's an example that will execute identically under Ruby and JRuby:
# Note: Comment out for Ruby test. Uncomment for JRuby test.
require 'java'
class MethodMissing
# JRuby: Undefine the standard "automatic" bindings to Java, to avoid any automatic binding.
undef org, com, java, javax, javafx
def method_missing(m, *args)
puts "method_missing: #{m}."
"This is what I return from method missing."
end
end
mm = MethodMissing.new
result = mm.org
puts "Result: #{result} Type: #{result.class.name}"
In Ruby, one usually prefers method_names_like_this
, while Java traditionally uses methodNamesLikeThis
. If you want, you can use Ruby-style method names instead of the Java ones.
For example, these two calls are equivalent
java.lang.System.currentTimeMillis
java.lang.System.current_time_millis
JRuby also translates methods following the 'beans-convention':
x.getSomething becomes x.something
x.setSomething(newValue) becomes x.something = new_value
x.isSomething becomes x.something?
You don't have to use these alternatives, but they can make the interaction with Java code feel more Ruby-like.
JavaClass.new
or JavaClass.new(x,y,z)
generally works as expected. If you wish to select a particular constructor by signature use reflection:
# Get the the three-integer constructor for this class
construct = JavaClass.java_class.constructor(Java::int, Java::int, Java::int)
# Use the constructor
object = construct.new_instance(0xa4, 0x00, 0x00)
If a Java class is defined with Java generics, the types are erased during compilation for backwards compatibility. As a result, JRuby will have problems with automatic type conversion. For example, if you have a Map<String,String>
, it will be seen as a simple Map
, and JRuby will not be able to determine the correct types using reflection.
JRuby defines a number of additional methods for Java objects.
-
java_class
returns the Java class of an object. -
java_object
returns the underlying Java object. This is useful for reflection. -
java_send
overrides JRuby's dispatch rules and forces the execution of a named Java method on a Java object. This is useful for Java methods, such asinitialize
, with names that conflict with built-in Ruby methods (more onjava_send
below). -
java_method
retrieves a bound or unbound handle for a Java method to avoid the reflection inherent injava_send
(more onjava_method
below). -
java_alias
aliases a specific method name and signature to a new Ruby method name
The Ruby kind_of?
/ is_a?
and instance_of?
methods also work for Java objects:
list = java.util.ArrayList.new
puts list.kind_of?(java.util.ArrayList) # true (direct instance)
puts list.kind_of?(java.lang.Object) # true (instance of a subclass)
puts list.kind_of?(java.util.List) # true (instance of an implementing class)
puts list.instance_of?(java.util.ArrayList) # true
puts list.instance_of?(java.lang.Object) # false! (not a direct instance)
puts list.instance_of?(java.util.List) # false! (not a direct instance)
JRuby also used to define a java_kind_of?
method for Java objects, but this was officially deprecated in JRuby 9.2.19 and removed in JRuby 9.3. The standard .kind_of?
method should be used instead.
Sometimes you need to call initialize
AFTER the .new()
call, for example the RTPManager
class in JMF. Unfortunately, this method is masked by Ruby's initialize
constructor method. As of JRuby 1.4, the java_send
method can be used to call this, and any other, masked method:
@mgr = javax.media.rtp.RTPManager.newInstance
localhost = java.net.InetAddress.getByName("127.0.0.1")
localaddr = javax.media.rtp.SessionAddress.new(localhost, 21000, localhost, 21001)
@mgr.java_send :initialize, [javax.media.rtp.SessionAddress], localaddr
Here is another example of calling the ArrayList.add
method with java_send
:
java_import java.util.ArrayList
list = ArrayList.new
list.java_send :add, [Java::int, java.lang.Object], 0, 'foo'
puts list.java_send :toString # => "[foo]"
Note the second argument, which is an array of types indicating the exact method signature desired. This is useful for disambiguating methods that are overloaded on similar types such as int
and long
.
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.
If you'll be calling a specific unreachable Java method a lot, or if a Java method has many overloads and you want to avoid the overhead of selecting the right one every time, you can use java_alias
to alias a specific Java method name + signature to a Ruby name.
java_import java.util.ArrayList
class ArrayList
java_alias :simple_add, :add, [Java::int, java.lang.Object]
end
list = ArrayList.new
list.simple_add 0, 'foo'
Here is an example that shows using java's reflection within jruby
@mgr = javax.media.rtp.RTPManager.newInstance
localhost = java.net.InetAddress.getByName("127.0.0.1")
localaddr = javax.media.rtp.SessionAddress.new(localhost, 21000, localhost, 21001)
method = @mgr.java_class.declared_method(:initialize, javax.media.rtp.SessionAddress )
method.invoke @mgr.java_object, localaddr.java_object
See the JRuby rspec source code dir spec/java_integration
for many more examples.
When calling Java from JRuby, primitive Ruby types are converted to default boxed Java types:
Ruby Type | Java Type |
---|---|
"foo" | java.lang.String |
1 | java.lang.Long |
1.0 | java.lang.Double |
true, false | java.lang.Boolean |
1 << 128 | java.math.BigInteger |
However, this does not mean that you cannot call methods expecting a primitive type. You can also pass an integer to a method expecting a double value. JRuby usually tries quite hard to find a method that can understand your parameters.
If JRuby cannot find a matching method, it tries to pass the actual JRuby objects instead (that is, the Java objects from the JRuby implementation). A consequence of this is that if this fails you will see an error message stating that JRuby hasn't found a method taking an object of class org.jruby.RubyObject
instead of the actual type.
If JRuby is not finding the exact method you want to call, perhaps because of extreme ambiguity like foo(int)
vs. foo(long)
, the java_send
method can be used to disambiguate. See below.
When primitive Java types are passed to JRuby they are converted to the following Ruby types:
Java Type | Ruby Type |
---|---|
public String | String |
public byte | Fixnum |
public short | Fixnum |
public char | Fixnum |
public int | Fixnum |
public long | Fixnum |
public float | Float |
public double | Float |
The Java Booleans true and false are coerced to the Ruby singleton classes TrueClass and FalseClass which are represented in Ruby with the instances true and false.
The null
Java object is coerced to the Ruby class NilClass
which is represented in Ruby as the instance nil
.
Java primitive classes can be found in the Java module. For example, Java::byte
represents the primitive type byte in java. You can get its class as follows:
Primitive type | Java Class |
---|---|
boolean | Java::boolean.java_class |
byte | Java::byte.java_class |
short | Java::short.java_class |
char | Java::char.java_class |
int | Java::int.java_class |
long | Java::long.java_class |
float | Java::float.java_class |
double | Java::double.java_class |
There are two ways of constructing Java arrays. One is to use the to_java
method of the class Array. The other is to use the []
method for the primitive Java types.
Converting a Ruby Array to a Java Array
The to_java
method constructs a Java array from a Ruby array:
[1,2,3].to_java
=> [Ljava.lang.Object;@1a32ea4
By default, to_java
constructs Object
arrays. You can specify the parameter with an additional argument which can either be a symbol or a primitive class like Java::double
["a","b","c"].to_java(:string)
=> [Ljava.lang.String;@170984c
[1, 2, 3.5].to_java Java::double
=> [D@9bc984
Sometimes a Java library will need a fixed-length array, say for example a byte buffer for a stream to read into. For this, you can use the []
method of the primitive types in the Java module:
bytes = Java::byte[1024].new # Equivalent to Java's bytes = new byte[1024];
strings = java.lang.String[1024].new
bytes = 'a string'.to_java_bytes
=> #<#<Class:01x9fcffd>:0x40e825 @java_object=[B@3d476c>
string = String.from_java_bytes bytes
=> "a string"
io = java_input_stream.to_io # works for InputStreams, OutputStreams, and NIO Channels
stream = io.to_inputstream # also to_outputstream and to_channel
Note that closing a stream will close its conversions.
set = java.util.HashSet.new([1,2,3])
set.to_set
=> #<Set: {1, 2, 3}>
hash = java.util.HashMap.new(:a => 123) # or Hashtable
hash.to_hash.class
=> Hash
list = java.util.ArrayList.new([1,2,3]) # or Vector, LinkedList, ...
list.to_a
=> [1, 2, 3]
Note that list.to_array
will return a primitive java array.
t = Time.now
t.to_java
=> #<Java::JavaUtil::Date:0x747541f8>
Note that currently you cannot call java methods from within a ruby class' constructor that inherits from a java class, without first calling super or it results in a stack overflow http://www.ruby-forum.com/topic/217475
If you call a Java class from JRuby and need to pass a Java class as an argument, if you use this form:
DoSomethingWithJavaClass(MyJavaClass.class)
you'll get this error:
TypeError: expected [java.lang.Class];
got: [org.jruby.RubyClass]; error: argument type mismatch
Instead use the method java_class
.
DoSomethingWithJavaClass(MyJavaClass.java_class)
In Ruby, classes are always open, which means that you can later add methods to existing classes. This also works with Java classes.
This comes in handy when adding syntactic sugar like overloaded operators to Java classes, or other methods to make them behave more Ruby-like.
Note that these additions will only be visible on the JRuby side.
You can subclass (i.e. extend) a Java class and then use the JRuby class whenever Java expects the superclass. As of JRuby 9.3, these classes are full java classes, which introduces some restrictions:
-
super()
calls in the constructor (initialize
, or whichever method is configured withconfigure_java_class ctor_name: :your_custom_ctor_method_name
) must integrate with certain JVM features that are complex to expose to Ruby. As such, the Java restriction of exactly 0 or 1super
calls (with no conditionalsuper
s) applies at this time. Similarly,self.to_java
is unusable before this super call. Ruby code before a super is valid, but not recommended.
foo = Class.new(Java::my.package.MyClass) {
def method1(x)
# override method1
end
def method2(y, z)
# override method2
end
# etc...
}.new(my_args)
If you need to access package, protected and private fields on a Java class, you are able to do so with field_reader, field_writer and field_accessor attributes.
The methods field_reader, field_writer, field_accessor are analogues to attr_reader, attr_writer, and attr_accessor and take a symbol name of the field you want to access as a an argument.
For example, if you have a java class of:
public class Something {
protected float somevalue = 1.0f;
}
And you want to access the protected field somevalue
from a subclass that is defined in JRuby, you can do the following:
class SomethingElse < Something
field_accessor :somevalue
def initialise
self.somevalue(2.0)
end
end
If you have a class name ambiguity between Java and Ruby, the class name will reference the Ruby construct within the Ruby code. For instance, if you import java.lang.Thread
and then write JThread < Thread
, JThread
will in fact inherit the Ruby Thread
object, not the Java Thread
. The solution is to use the full Java Class name, such as:
JThread < java.lang.Thread
Java interfaces are mapped to modules in JRuby. This means that you can also reopen the corresponding module and add further methods on the JRuby side.
JRuby classes can now implement more than one Java interface. Since Java interfaces are mapped to modules in JRuby, you implement them not by subclassing, but by mixing them in.
class SomeJRubyObject
include java.lang.Runnable
include java.lang.Comparable
end
JRuby sports a closure conversion feature (a.k.a. proc-to-interface conversion), where a Ruby block (closure) is converted to an appropriate Java interface. For example:
button = javax.swing.JButton.new "Press me!"
button.add_action_listener {|event| event.source.text = "You did it!"}
In this example, JButton
's addActionListener
method takes one parameter, a java.awt.event.ActionListener
. The block is converted to a Proc
object, which is then decorated with a java interface proxy that invokes the block for any method called on the interface.
This works for any interface, but fits best when implementing functional-style interfaces (those that contain one method to be implemented).
In (rare) cases where there are multiple matching Java methods with different interface last parameters, the block arity is taken into consideration. Here's an example of the overloaded java.io.File#listFiles
method:
java.io.File.new('.').listFiles do |pathname|
# matches listFiles( FileFilter#accept(File) )
end
# listFiles( FilenameFilter#accept(File, String) )
java.io.File.new('.').listFiles do |dir, name|
# calls listFiles( FilenameFilter#accept(File, String) )
end
Note: This feature is available since JRuby 1.7.22 and 9.0.1.0.
A final way to implement Java interfaces is via the ::impl
method on the interface module. For example:
java.lang.Runnable.impl {|method_name| puts "Run!" }
java.lang.Comparable.impl {|method_name, other| other.nil? }
java.lang.Appendable.impl {|method_name, *args| p args }
The first argument to the block is a symbol of the method name being invoked, and the remaining block arguments are any arguments on that method.
Hopefully this feature will be added in the planned re-write of the Java integration layer in a future release of JRuby.
Native Java exceptions can be caught in Ruby code as expected:
begin
java.lang.Integer.parse_int("asdf")
rescue java.lang.NumberFormatException => e
puts "Failed to parse integer: #{e.message}"
end
Note: Java exceptions do not inherit from ruby Exception, but they will be caught by rescue
Furthermore, Ruby code can throw Java exceptions:
begin
raise java.lang.IllegalArgumentException.new("Bad param")
rescue java.lang.IllegalArgumentException => e
puts "Illegal argument: #{e}"
end
This is useful if you happen to be implementing a Java interface in Ruby that requires a particular exception to be thrown on error.
Note that this can also be written:
begin
raise Java::JavaLang::IllegalArgumentException.new("Bad param")
rescue Java::JavaLang::IllegalArgumentException => e
puts "Illegal argument: #{e}"
end
When interacting with Java APIs from JRuby, it is occasionally necessary to synchronize on an object for thread safety. In JRuby, a synchronized
method is provided on every wrapped Java object to support this functionality. For example, the following Java code:
synchronized(obj) {
obj.wait(1000);
}
is implement like this in Ruby:
obj.synchronized do
obj.wait 1000
end
The expression evaluates to the result of the block, e.g.,
obj.synchronized { 99 } # => 99
There can be instances (in closures for example) where you need to convert a java Object to a specific class/interface you can do this using a custom to_java
eg
{ |obj| d = obj.to_java(Java::Hype::HDrawable); d.stroke(100) ..etc }
You may find that this is preferable to creating an anonymous class when implementing a simple (one method) interface, especially since jdk8 has lambda support, and even java people have seen the light.
Java Integration changes in JRuby 9.1
Java Integration changes in JRuby 9.2
- FAQs#calling-into-java Other useful examples
- Java Integration in JRuby Short overview article on JRuby and Java integration
- JRuby/Ruby Arrays to Hash Review on Converting Arrays to Hash in JRuby/Ruby
- Ruby Arrays and Java Lists
- Java Streams Meet Enumerator
- Better Closures for (Functional) Java
- Java Integration: JavaScript, Groovy and JRuby Side-by-side comparison of Java integration in JavaScript, Groovy and JRuby.
- Spicing up Java with Ruby (a Rhino Story) Using Java integration to bring Rhino into Ruby.
- JtestR A testing framework for Java based on JRuby.
- Sparrow A JMS client for messaging based on JRuby.