|
Feature
|
JavaScript
|
Groovy
|
JRuby
|
|
Importing Java packages
|
importPackage function.
Examples:
importPackage(java.awt);
importPackage(javax.swing);
Rhino has a built-in variable by the name Packages.
You have to use Package.javax.swing to refer to javax.swing
package. But, "java" is a short-cut for "Packages.java". In
JDK 6, short-cuts have been added to all commonly used package
prefixes as well (like javax, org, com, net etc.). If you are
using Rhino standalone, you can eval the following:
var javax = Packages.javax;
var com = Packages.com;
var net = Packages.net;
var edu = Packages.edu;
Note: java.lang package is not automatically imported
in Java (that would result conflicts - example: JavaScript
Object vs. Java's java.lang.Object).
If you are importing too many packages and classes, you may be
polluting the global namespace. You can avoid that by
using JavaImporter function.
var guiPkgs = new JavaImporter(
java.awt, java.awt.event, javax.swing);
with (guiPkgs) {
// here you can access all classes
// in java.awt, java.awt.event and
// javax.swing classes by simple names
}
// outside the above "with" statement
// you can't use simple class names.
|
Same as Java!
import java.awt.*;
import javax.swing.*;
By default, Groovy imports java.lang, java.util, java.io,
java.net, groovy.lang, groovy.util packages.
|
You can not import all classes of a package in top-level
scope. Instead, you have to import within a module.
module MyJava
include_package 'java.util'
end
$o = MyJava::Hashtable.new
puts $o
|
|
Importing specific class(es)
|
importClass function.
importClass(java.util.HashMap);
importClass(javax.swing.JFrame);
|
Same as Java!
import javax.swing.JFrame;
By default, Groovy imports java.math.BigInteger and java.math.BigDecimal
classes.
|
include_class(classname_or_list_of_classnames)
{|package, name| optional_renaming_block }
require 'java'
include_class('javax.swing.JFrame')
include_class ["JFrame"].map {
|e| "javax.swing." + e
}
Note: The Java import packages/class is improving.
See also:
|
|
Type alias (Referring Java class with different name)
|
var Format = java.text.SimpleTextFormat;
// use "Format" as class name below
|
import java.text.SimpleTextFormat
as Format;
// use "Format" as class name below
|
include_class(classname_or_list_of_classnames) {
|package, name| optional_renaming_block
}
require 'java'
include_class('java.lang.String') {
|package,name| "J#{name}"
}
# after this you can use "JString"
|
|
Creating a Java object
|
importClass(javax.swing.JFrame);
var f = new JFrame("hello");
|
import javax.swing.JFrame;
f = new JFrame("hello");
|
include_class('javax.swing.JFrame');
f = JFrame.new('hello');
|
|
Calling instance methods
|
var f = new javax.swing.JFrame("hello");
f.setSize(100, 100);
f.setVisible(true);
|
f = new javax.swing.JFrame("hello");
f.setSize(100, 100);
f.setVisible(true);
|
include_class('javax.swing.JFrame');
f = JFrame.new('hello');
f.setSize(100, 100);
f.setVisible(true);
|
|
Calling static methods
|
java.lang.System.exit(0)
|
System.exit(0)
|
include_class('java.lang.System');
System.exit(0)
|
|
JavaBean support
|
f = new javax.swing.JFrame("hello");
f.setSize(100, 100);
// calls setVisible
f.visible = true;
// calls getTitle for "title" access
println(f.title);
|
f = new javax.swing.JFrame("hello");
f.setSize(100, 100);
// calls setVisible
f.visible = true;
// calls getTitle
println(f.title);
|
require 'java';
include_class('javax.swing.JFrame');
f = JFrame.new("hello");
# calls setVisible
f.visible = true;
# calls getTitle
puts f.title
|
|
instanceof check
|
importClass(javax.swing.JFrame);
var f = new JFrame("hello");
// prints true
println(f instanceof JFrame);
|
import javax.swing.JFrame;
f = new JFrame("hello");
// prints true
println(f instanceof JFrame);
|
require 'java';
include_class('javax.swing.JFrame');
f = JFrame.new("hello");
# prints true
puts f.kind_of?(JFrame)
|
|
Java overloaded resolution
|
In most cases, Rhino selects proper overload
variant automatically. But, if you want to force selection
of a particular method, you can use the following:
var out = java.lang.System.out;
out["println(double)"](3.14);
Essentially, we specify full singature of the method as a
string. Note that JavaScript objects are associative arrays
- it is always possible to use property/method name within
square bracket (as a string). This is same as ".field" or
".method" syntax.
|
Groovy selects the "most appropriate" overload automatically
(see also Mutlimethods in Groovy).
I am not sure if there is any way to force a particular
overload to be called.. Update: Jochen "blackdrag" Theodorou notes that the overload resolution will be improved in Groovy 1.0. For now, you can use java reflection API directly.
|
require 'java';
include_class('java.lang.System');
System.out.println("hello");
# the following don't print numbers
# I am not sure how to resolve to
# a particular overload variant
# System.out.println(33.33);
# System.out.println(2)
I think it should be possible
to use low-level reflection of
JRuby - may be I can do this
little less verbose than what
is below??
require 'java';
cl = Java::JavaClass.for_name(
"java.io.PrintStream");
m = cl.java_method(:println, :double)
include_class('java.lang.System')
include_class('java.lang.Double')
d = Double.new(3.141596)
m.invoke(System.out.java_object,
d.java_object)
|
|
Handling Java exceptions
|
var Stream = java.io.FileInputStream
try {
s = new Stream("non_existent");
} catch (e) {
// handle FileNotFoundException
}
It is possible to have multiple catch classes
like in Java. JavaScript error objects have
"javaException" property - which points to
the actual underlying Java exception (if error
was thrown due to a Java exception). You can do
instanceof check on the underlying java exception.
var Stream = java.io.FileInputStream
try {
s = new Stream("myfile");
} catch (fnfe
if fnfe.javaException instanceof
java.io.FileNotFoundException) {
println("file not found");
fnfe.javaException.printStackTrace();
} catch (ioe
if ioe.javaException instanceof
java.io.IOException) {
println("some io error");
ioe.javaException.printStackTrace();
}
|
Exception handling is same as Java.
try {
s = new java.io.FileInputStream("myfile");
} catch (FileNotFoundException fnfe) {
println("file not found");
fnfe.printStackTrace();
} catch (IOException ioe) {
println("some io error");
ioe.printStackTrace();
}
|
require 'java';
include_class("java.io.FileInputStream")
include_class("java.io.FileNotFoundException")
include_class("java.io.IOException")
begin
f = FileInputStream.new("myfile");
rescue FileNotFoundException=>fnfe
puts fnfe.backtrace
rescue IOException=>ioe
puts ioe.backtrace
end
|
|
Creating Java Arrays
|
There is no "direct" syntax. But, whenever a
Java array is needed (say as an argument to a
Java method call), JavaScript array can be passed.
Script arrays are automatically converted to Java
arrays as needed. If you want you can use
java.lang.reflect.Array
// create a Java String[] of
// 3 elements
var s = java.lang.reflect.Array.
newInstance(java.lang.String, 3);
println(s);
|
Same as Java!
s = new String[3];
println(s);
|
Update: Seems like no direct support.
Nick Sieger notes that arrays can be created with easier
syntax: (Thanks!)
include_class("java.lang.String") {
"JString"
}
s = JString[].new(2)
puts s
I had the following code before Nick Sieger's comment - You can use java.lang.reflect.Array directly:
include_class("java.lang.reflect.Array") {
"JArray"
}
include_class("java.lang.String") {
"JString"
}
s = JArray.newInstance(JString, 2);
puts s
|
|
Accessing Java Arrays
|
Same as Java! usual [] and .length
are supported.
// create a Java String[] of
// 3 elements
var s = java.lang.reflect.Array.
newInstance(java.lang.String, 3);
s[0] = "hello";
s[1] = "world";
s[2] = "JavaScript";
println(s.length);
println(s[0]);
println(s[1]);
println(s[2]);
|
Same as Java!
s = new String[3];
s[0] = "hello";
s[1] = "world";
s[2] = "Groovy";
println(s.length);
println(s[0]);
println(s[1]);
println(s[2]);
|
Same as Java!
include_class("java.lang.reflect.Array") {
"JArray"
}
include_class("java.lang.String") {
"JString"
}
s = JArray.newInstance(JString, 3);
s[0] = "hello";
s[1] = "world";
s[2] = "JRuby";
puts s.length;
puts s[0];
puts s[1];
puts s[2];
|
|
Implementing a Java interface
|
Use Java anonymous class-like syntax:
var r = new java.lang.Runnable() {
run: function() {
println("I'm run method");
}
};
println(r instanceof java.lang.Runnable);
new java.lang.Thread(r).start();
For single method interfaces, you can pass
JavaScript function directly:
function f() {
println("I'm function f");
}
new java.lang.Thread(f).start();
|
Similar to Java - but anonymous classes are not
supported (yet). There is an RFE to allow auto-conversion of
Closure to single-method interfaces
class MyRunnable implements Runnable {
void run() {
println("I'm run method");
}
}
r = new MyRunnable();
println(r instanceof java.lang.Runnable);
new java.lang.Thread(r).start();
|
require 'java';
include_class('java.lang.Runnable');
class MyRunnable < Runnable
def run
puts "I'm run method"
end
end
r = MyRunnable.new
puts r.kind_of?(Runnable)
include_class('java.lang.Thread') {
|e| "JThread" }
JThread.new(r).start
Another example:
require 'java'
include_class "java.awt.event.ActionListener"
class MyActionListener < ActionListener
def actionPerformed(event)
puts event
end
end
Charles Oliver Nutter notes that there is also a more "anonymous" way to implement interface:
require 'java';
include_class('java.lang.Runnable');
r = Runnable.new { puts "I'm running"; }
puts r.kind_of?(Runnable)
|
|
Implementing multiple interfaces
|
Rhino standalone download supports implementing
multiple interfaces through the use of
JavaAdapter. But, the bundled
JavaScript engine in JDK 6 does not support
this. See also: JDK 6 release notes
- scripting section. But, it is possible to use
java.lang.reflect.Proxy as shown below:
importClass(java.lang.Runnable);
importClass(java.util.concurrent.Callable);
Proxy = java.lang.reflect.Proxy;
r = new Proxy.newProxyInstance(
null,
[ Runnable, Callable ],
new java.lang.reflect.InvocationHandler() {
invoke: function(obj, name, args) {
println(name + " called");
}
}
);
println(r instanceof Runnable);
println(r instanceof Callable);
r.run();
r.call();
|
Same as Java!
import java.util.concurrent.Callable;
class MyClass
implements Runnable, Callable {
void run() {
println("run called");
}
Object call() {
println("call called");
}
}
r = new MyClass();
println(r instanceof Callable);
println(r instanceof Runnable);
r.run();
r.call();
|
It appears that it is not possible currently.
See also: Interfaces Should Be Modules
I think you can use java.lang.reflect.Proxy trick for now.
But, when I tried the following I got StackOverflowError!
I've filed a bug (Attempting to create a java.lang.reflect.Proxy instance results in StackOverflow)
require 'java';
include_class('java.lang.reflect.InvocationHandler');
include_class('java.lang.reflect.Proxy') {
|x| "JProxy"
};
include_class('java.lang.Runnable');
include_class('java.util.concurrent.Callable');
include_class("java.lang.reflect.Array") {
|x| "JArray"
};
include_class("java.lang.Class") {
|x| "JClass"
}
class MyRunnable < InvocationHandler
def invoke(obj, name, args)
puts name + " called"
end
end
include_class('java.lang.Runnable')
types = JArray.newInstance(JClass, 2);
types[0] = Runnable
types[1] = Callable
m = MyRunnable.new
r = JProxy.newProxyInstance(nil, types, m);
puts r.kind_of?(Runnable)
puts r.kind_of?(Callable)
|
|
Extending a Java class
|
Rhino standalone download supports extending a java class
through JavaAdapter. But, the bundled JavaScript
engine in JDK 6 does not support this. Supporting this
requires .class file generation -- which has been removed
in JDK 6 (for security and footprint reasons).
See also: JDK 6 release notes
- scripting section.
|
Same as Java!
import java.awt.*;
import java.awt.event.*;
class MyListener extends WindowAdapter {
// override just one method
void windowClosing(WindowEvent evt) {
println("Why did you close?");
System.exit(0);
}
}
f = new Frame();
f.addWindowListener(new MyListener());
f.setVisible(true);
|
This requires generation of .class files. As of
now, JRuby is interpreted only. And so extending
a Java class and passing it to Java API is not
possible.
|
Posted by Jochen "blackdrag" Theodorou on October 06, 2006 at 03:58 PM IST #
Posted by A. Sundararajan on October 06, 2006 at 04:07 PM IST #
Importing classes
As you note, the syntax is improving for this. In the 0.9.1 release you will be able to do any of the following:
Creating arrays
You can create new arrays like so:
Implementing multiple interfaces and extending classes
There is a patch to allow Java classes to be extended, so this should be working within the next couple of releases (see JRUBY-71). Also, there has been discussion on the list lately concerning an approach for multiple interface implementation, so I would expect that to be available soon as well.
Posted by Nick Sieger on October 07, 2006 at 12:41 AM IST #
Posted by Thomas E Enebo on October 07, 2006 at 12:51 AM IST #
Posted by A. Sundararajan on October 07, 2006 at 01:02 AM IST #
ActionListener = java.awt.event.ActionListener FocusListener = java.awt.event.FocusListener al = ActionListener.new { puts "hello" } al.actionPerformed(nil) => "hello" fl = FocusListener.new(:focusGained) { |sym, *args| ... } fl.focusGained(nil) => ... # fl.focusLost(nil) produces a NameError; it's not implementedThe eventual support for implementing multiple interfaces and concrete classes will likely look as follows: We are actively working to improve the java integration before a 1.0 release, so suggestions from the other language camps are very welcome (so long as they feel "Ruby" enough :)Posted by Charles Oliver Nutter on October 07, 2006 at 06:53 AM IST #
Posted by Jochen on October 08, 2006 at 05:21 AM IST #
Posted by A. Sundararajan on October 08, 2006 at 11:41 AM IST #
Posted by Owen Densmore on October 08, 2006 at 11:03 PM IST #