|
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.
|