Java, Groovy and (J)Ruby

So, you want to learn few scripting and dynamically typed language(s) for the Java platform? The following will get a Java programmer started with Groovy and (J)Ruby. It is said that you can write Fortran in any language! While you still have to learn language specific idioms, naming, coding conventions/styles etc., the table below would help you with "quick start". To reiterate: you need to pick up language specific idioms as Reg Braithwaite notes in his comment below.

I wrote simple script samples and tried the same with command line wrappers of jrunscript. If you CVS checked out script engines from scripting.dev.java.net, these scripts are in bin directories of respective script engine directories. I used Groovy 1.0 JSR 06 and JRuby 0.9.0 versions respectively.

Feature Java Groovy JRuby
Type System Static with few dynamic checks inserted as needed. Dynamic, optional static types Dynamic
Comments

/\*
 \* multiline comment
 \*/

/\*\*
 \* javadoc comment
 \*/

// single line comment


/\*
 \* multiline comment
 \*/

/\*\*
 \* javadoc comment
 \*/

// single line comment


=begin
 Ruby doc comment
=end

# this is single line comment

Control Statements

if (condition) {
  // statements
}

if (condition) {
  // statements
} else if (condition) {
  // statements
} else {
  // statements
}

while (condition) {
  // statements
}

do {
  // statements
} while (condition);

for (Type t : iterable) {
  // statements
}

for (init; condition; increment) {
  // statements
}

switch (target) {
  case constant_expr:
    // statements
    [break]
  ..
  default:
    // statements
}
\* break can be used in loops to break out of loop.
\* continue statement skips the current iteration of a for, while, or do-while loop and valuates the boolean expression that controls the loop.

if (condition) {
  // statements
}

if (condition) {
  // statements
} else if (condition) {
  // statements
} else {
  // statements
}

while (condition) {
  // statements
}

while (condition) {
  // statements
}

// for-each statement uses "in" keyword 
// unlike Java
for (t in iterable) {
  // statements
}

switch (target) {
  case constant_expr:
    // statements
    [break]
  ..
  default:
    // statements
}
\* do.. while is not supported.
\* for (init; condition; increment) is not supported.
\* null is treated as false. Truth value special cases for data structures.
\* Unlike Java, switch statement works for arbitrary expressions (not just for integral expressions and enums as in Java). isCase method is used on objects.
\* Labeled break and continue are not supported (yet?)

if condition 
  # statements
end

if condition
  # statements
elsif condition
  # statements
else
  # statements
end

while condition 
  # statements
end

unless condition
  # statements
else
  # statements
end

expression if condition
expression unless condition

# no do-while but you can use
# begin..end and put while or 
# until at the end
begin
 # statements
end until condition

begin
 # statements
end while condition

case target_expression
  when comparison 
    # statements
  when comparison
    # statements
  else
    # statements
end

\* nil is treated as false. C/C++ Trap: 0 is true!
\* break terminates loop immediately.
\* redo immediately repeats w/o rerunning the condition.
\* next starts the next iteration through the loop.
\* retry restarts the loop, rerunning the condition.

String Literals "hello world"

// double-quoted
"hello, world"

// single-quotes are okay too
'hello, world'

// multiple line strings for
// embedded "here docs"
""" multiple
line
strings"""

// substituted strings
"String with ${expression}
interpolated"

Example:

name = 'Sundar'
// ${name} is replaced with name's value
println "Hello, ${name}"


'no interpolation'
"#{interpolation}"
%q(no interpolation)
%Q(interpolation)
%(interpolation)

<<identifier   - interpolated 
<<"identifier" - same as above
<<'identifier' - no interpolation
<<-identifier  - indent using "-"

Examples:

name = 'Sundar'
puts "Hello, #{name}"

puts(<<EOF

This is a hello world
in multiple lines

EOF
)

Class Declaration

class Foo {
    // methods
    // fields
}


class Foo {
    // methods
    // fields
}


class Foo 
    # methods
    # fields
end

Instance Variables

int i;
float f;
double d;
String s;

Same as Java or you can omit types. Think "def" means java.lang.Object! John Wilson notes that 'def' is really untyped. If I declare a variable as being of type Object it is not 100% the same as 'def'ing it. Please refer to his comments below.

def i;
def f;
def d;
def s;
instance variable names start with '@'

@i
@f
@d
@s
Static Variables

static int i;
static float f;
static double d;
static String s;

Same as Java or you can omit types. Think "def" means java.lang.Object! John Wilson notes that 'def' is really untyped. If I declare a variable as being of type Object it is not 100% the same as 'def'ing it. Please refer to his comments below.

static def i;
static def f;
static def d;
static def s;
static k;
Static variable names start with '@@'

@@i
@@f
@@d
@@s
Global Variables Not supported. Use public static final variables in a class or use enum as appropriate. Top-level variables are supported. No special naming enforced. [Actually, these are contained in "Script" class - which contains all "global" definitions] Has to start with "$"

$xx 

Method Definition
class Person {
  public void greet() {
    System.out.println("hello world");
  }
}
class Person {
  def greet() {
    println("hello world");
  }
}
class Person
  def greet()
    puts("hello world")
  end
end
Static Method Definition
class Person {
  public static void greet() {
    System.out.println("hello world");
  }
}
class Person {
  static greet() {
    println("hello world");
  }
}
use className.methodName as method name.
class Person
  def Person.greet()
    puts("hello world")
  end
end
Returning from a method

return expression;
return;

Same as Java. But can leave the return statement - in that case the last expression evaluated is returned. Same as Java. But can leave the return statement - in that case the last expression evaluated is returned.
Object Creation
Person p = new Person();
def p = new Person();
'new' is a method in Ruby!
p = Person.new 
Method Call
obj.foo(param1, param2);
p.greet();
can omit parans in some cases
obj.foo(param1, param2);
p.greet();
\* If no arguments, need to use parans
\* Parans can be omitted only for method calls in top-level statements
Example:

obj.foo param1, param2
"hello".substring 1, 3
can omit parans in some cases
obj.foo(param1, param2);
p.greet();
p.greet
Static Method Call
className.foo(param1, param2);
Person.greet();
System.exit(0);
className.foo(param1, param2);
Person.greet();
System.exit(0);
\* If no arguments, need to use parans
\* Parans can be omitted only for method calls in top-level statements
Example:
className.foo param1, param2
"hello world".substring 1, 3
className.foo(param1, param2);
className::foo(param1, param2);
Person::greet();
Person::greet;
Person.greet;
Object Initialization
class Person {
  // declare constructor method
  // same as class name
  public Person() {
    // initialize new object here
  }
}
class Person {
  // declare constructor method 
  // same as class name
  Person() {
    // initialize new object here
  }
}
class Person 
  # declare "initialize" method 
  # not the class name!
  def initialize()
    # initialize new object here
  end
end
Referring to the current object this this self
Null null null - Guillaume Laforge notes that "Null Object Pattern" is supported in Groovy. See also: NullObject. So, you can call null.toString() for example. nil - but "nil" is a proper object. So, methods can be called on it without getting NullPointerException.

nil.nil?
nil.class
nil.methods

Arrays
int a[] = new int[10];
String[] s = new String[2];

a[0] = 3;
s[0] = "hello";

def a = new int[10];
def s = new String[2];

a[0] = 3;
s[0] = "hello";

// Array is a class
a = Array.new(10);
a[0] = 3;
a[1] = "hello";

Array Literals
int a[] = { 10, 4354, 432 }
String[] s = { "dsjfhsd", "sdfsd"}
a[0] = 3;
s[0] = "hello";

Note that Groovy has List literals (represented by a java.util.List instance). The following list literal syntax can be used.

def a = [2, 3, 4354]
def s = ["hello", "world"]

a[0] = 33;



a = [3, 4, 546]
a[0] = 3;
a[1] = "hello";

RegEx Literals Not supported. Regular Expressions are specified as Strings. /pattern/

Note from Guillaume Laforge: The "slashy" string syntax creates strings, in fact, it's not a regular expression per se, but it allows one to avoid to have to escape each and every character as it's the case in Java. But, this syntax doesn't really create some kind. Thanks!

/pattern/
Hash Literals Not supported

[ key: value, key1: value1 ]

Examples:

v = [ 
      hello: "world", 
      Sun: "Network"
    ]

println(v["Sun"]);


{ key=> value, key1=> value1 }

Examples:

v = { 
      "hello" => "world",
      "Sun" => "Network"
    }

puts v["Sun"]


varargs methods Use JDK 1.5+. The "..." after the last formal argument means this is a vararg method.
class Person {
  public void greet(Object...args) {
    // access the "args" array here
  }
}
Declare the last argument of the method as an array with that Groovy packs "excess" arguments in method call into that array argument
class Person {
  public void greet(Object[] args) {
    // access the "args" array here
  }
}

class Person
  # "\*" before the last argument
  def greet(\*args)
    # access args Array here
  end
end
Keyword arguments (a.k.a Named Parameters Not supported Not directly supported. But hash literals can be passed as an argument. When passing a hash literal as an argument, the "[" and "]" chars can be omitted.

def func(arg) {
   println(arg["Sun"]);
}

// Note that we omit "[", "]"
func("Sun": "Network");

Not directly supported. But hash literals can be passed as an argument. When passing a hash literal as an argument, the "{" and "}" chars can be omitted.

def func(arg)
   puts arg["Sun"]
end

# Note that we omit "{", "}"
func("Sun"=>"Network");

Operator Overloading Not supported - except for built-in String + operator

class Complex {
  double x, y;
  Complex(x, y) {
    this.x = x;
    this.y = y;
  }

  def plus(Complex c) {
    new Complex(x + c.x, y + c.y);
  }
 
  def print() {
     println("${x} +i${y}");
  }
}

c1 = new Complex(3, 2);
c2 = new Complex(1, -2);
// + calls plus method
(c1+c2).print();

Special methods are defined in the class to overload operators. For example, "plus" to overload "+".

class Complex 
  def initialize(x, y) 
    @real = x
    @imag = y
  end

  attr :real , true
  attr :imag , true

  def +(c) 
    Complex.new(real + c.real, imag + c.imag);
  end
 
  def print() 
     puts  "#{real} +i#{imag}";
  end
end

c1 = Complex.new(3, 2);
c2 = Complex.new(1, -2);
(c1+c2).print();

Method names can be operators. Also, when calling operator methods, you need not say obj.methodName. instead you say obj methodName param1. But, you can use usual method call syntax as well.
Extending another class

class Employee extends Person {
}


class Employee extends Person {
}


class Employee < Person
end

Referring to super class method super.foo(); super.foo();

class X 
 def func
  puts "I'm func"
 end
end

class Y < X
 def func
  puts "I'm extended func"
  super  
 end 
end

Unlike Java and Groovy, super keyword calls the super class method of the currently executing method. You are not calling arbitrary super method.
Nested, inner and anonymous classes
// Inner class
class Book {
   class Order { }
}

// Nested class
class Node {
   static class AndNode {
   }
   static class OrNode {
   }
}

// Anonymous class
class Main {
  public Runnable getRunnable() {
    return new Runnable() {
      public void run() { 
        // code
      } 
    };
  }
}
\* Nested and inner classes are not supported (yet?).
\* Local and anonymous classes are not supported (yet?)
But, we can use closures ;-)
From what I read, nested class is possible but not "inner" classes. Please correct me if I am wrong!


# Nested class
class A 
  def initialize
    @y = 0
  end

  # "B" is nested in "A"
  class B 
    def initialize
      @x = 0
    end
    def f
      # ...
    end
  end
end

a = "hello"
# create a duplicate
b = a.dup

# anonymous class for "a"
class <<a
  def to_s
    "I'm special '#{self}'"
  end
  def doubleMe
    self + self
  end
end

# prints "I'm special 'hello'"
puts a.to_s

# prints "hello"
puts b.to_s

Closures \* Not yet part of Java. But, may become!
\* Nominal Closures for Java (version 0.2)
\* Full Disclosure
\* For now, use local and anonymous classes ;-)
{ param1, param2 -> /\* statements \*/ }

/\* single param named as "it" \*/
{ /\* statements \*/ }

Examples:

[3, 4, 4].each { x -> println(x) }
[3, 4, 4].each { println(it); }

For detailed explanation, refer to Closures in Groovy
\* Blocks must follow a method invocation.
\* Proc objects can be created by Proc.new or proc method (in Kernel).

{ |param1, param2| ... }
do |param1, param2| ... end

Examples:

[3, 4, 5].each { |x| puts x }
[3, 4, 5].each do |x| 
   puts x 
end

For detailed critic, refer to closures-in-ruby.rb
Exception Handling

try {
  // statements
} catch(Exception e) {
  // statements
} finally {
  // statements
}

Exception handling is same as Java - except that declarations of exceptions in method signature are optional - even for checked exceptions.

begin
  expressions
[rescue [errorType [=> var],..]
  expressions]..
[else
  expressions]
[ensure
  expr..]
end

In addition, there is another way to do non-local jump:

catch (:done) do
    # some statements
    throw :done 
end

The catch is searched by symbol (symbol is an internalized string).
Modules, Namespaces

package com.acme.foo;

import java.net.\*;

There may be future improvements in this area.
Same as Java

module ModuleName
  # module level "functions" 
  # module level constants
  # zero or more classes
end

Example:

module Math
  PI = 3.141592654
  def Math.sin(x)
   # ..
  end
  def Math.cos(x)
   # ..
  end
end

require "name_of_file"

require is like import. There is also load - but unlike "require", "load" loads the file everytime when called (i.e., require loads and caches - subsequent require on the same file is a no-op).
Mixins Not supported. Mix-in categories can be used. But, there seems to another Mixin proposal as well.

class Greeter {
  static greet(Person p) {
    println(p.msg);
  }
}

class Person { 
  @Property def msg;
}

p = new Person();
p.msg = "hello"
use(Greeter) {
  // with "use" we can call 
  // "greet" on "Person" object

  p.greet();
}


Guillaume Laforge notes that the @Property notation (striked above) is not needed anymore and will be forbidden in the RC-1 release. See also Groovy Beans

module Greeter
 def greet
   puts msg
 end
end

class Person
 # "include" zero or more 
 # modules to get restricted
 # (and safe!) form of multiple
 # inheritance

 include Greeter
 attr :msg, true
end

p = Person.new
p.msg = "hello"
p.greet

Accessing Class object obj.getClass(); obj.class // or obj.getClass() obj.class
Constructing object using reflection

Class c = obj.getClass();
// assuming default constructor
Object o = c.newInstance();

Same as Java - except that not forced to handle exceptions

klass.new
klass.new(param1, param2);

Calling a method using reflection


Method m = obj.getClass().getMethod(
        name, classArray);
m.invoke(thisObj, argsArray);


obj.invokeMethod(name, args);

Example:

class Person {
  def getName(name) {
     println("hello ${name}");
  }
}

p = new Person();
p.invokeMethod("getName", "Sundar");


obj.send(:name_symbol, args)

Example:

class Person 
  def greet (name)
   puts "hello, #{name}"
  end
end

p = Person.new
p.send(:greet, "Sundar")

# or alternatively

# get method object
m = p.method(:greet);

# ... and call it
m.call("Sundar");


Comments:

[Trackback] mr. sundararajan of sun wrote a nice syntax comparision of java, groovy and jruby. worth watching while switching from one language to another. ...

Posted by stotti.blog() on September 18, 2006 at 07:13 AM IST #

Thanks, nice work. At first glance, it looks like you are showing "how to write Java programs in Ruby." This obviously has some value. However, if someone is interested in Ruby they really should be aware that idiomatic Ruby doesn't look anything like the examples you have given. You almost need a fourth column for "idiomatic Ruby." A few examples:
  1. Rubyists generally prefer to iterate over collections using each, collect, inject and other methods mixed in by the Enumerable module.

  2. Class methods are not really static methods. For example, Ruby class methods are polymorphic. The syntax given works, but most Rubyists use:

    class << self
      def some_class_method_name
        ...
      end
      def another_class_method_name
        ...
      end
    end

Posted by Reg Braithwaite on September 18, 2006 at 12:08 PM IST #

Hi Reg Braithwaite: Yes, I agree with you -- this post is meant for a Java programmer to start with Groovy and JRuby quickly. BTW, I did say this: "It is said that you can write Fortran in any language! While you still have to learn language specific idioms, naming, coding conventions/styles etc., the table below would help you with 'quick start'."

Posted by A. Sundararajan on September 18, 2006 at 12:19 PM IST #

Sundarajan, I noticed your comments about JavaScript as a good utility scripting language also. The question I have is that unlike JRuby, Groovy, etc. it doesn't seem to have any wrapper-style libraries that make things (xml processing, file i/o, soap) simpler/easier - it just has better syntax. Is this right?

Posted by Gary Furash on September 18, 2006 at 12:20 PM IST #

Hi Gary Furash: For XML handling, there is E4X (XML for ECMAScript) support. This is supported by browsers (like Firefox) and Rhino JavaScript implementation as well. Also, as part of Phobos project, there are libraries for JSON (JavaScript Object Notation - lightweight alternative for XML), logging, resource handling etc. With Rhino implementation, it is possible to call any JDK API directly or wrap the same as convenient script API. For example, please refer to Using Apache DB Derby from JavaScript

Posted by A. Sundararajan on September 18, 2006 at 12:51 PM IST #

A \*very\* small clarification on the meaning of 'def' in Groovy:) A variable which is 'def'ed is untyped. If I declare a variable as being of type Object it is not 100% the same as 'def'ing it. If I pass the variable as a parameter then, if the variable is typed, the type of the variable will be used to select the method. If the variable is 'def'ed than the value will be examined and the type of the value will be used to select the method. This has not yet been completely implemented but will be the behaviour of the 1.0 release.

Posted by John Wilson on September 18, 2006 at 01:54 PM IST #

Some small additions...

You wrote:
switch (target) {
  case constant_expr:
while this is basically right, the expression there does not have to be a constant expression. It could be a range, a class, regexpr or anything else, that supports an isCase method. For example
switch ("foo") {
  case String: println "it is a string"
  case /.+/: println "it matches a regexpr"
  case {it.length()==3}: println "it has a length of 3"
}
should print out all 3 comments.

Then global variables... we define classes and scripts in groovy, where classes is what you introduce with the class expression and scripts is anything else. Global variables are in fact more a Hashmap named Binding. Each script can have one, mulitple scripts can share the same binding and via MetaClass also "normal" classes can have them without using any extra syntax. But having a script and a class sharing a binding is uncommon.

Then for Closures, it is not on the page, and maybe little known, but the closures parameters can also have a type. The type is checked at runtime, so you get no "type savety in the java way" from it, but it allows you to add an additional runtime check. For example you can have a String[] as parameter and the closure then takes any arbitary number of Strings as parameters.

Next is for hash literals. def m = [foo:bar] creates a map and uses the String "foo" as key for whatever bar is and saves the map in m. Then you can not only do m["foo"], you can also do m."foo" or simply m.foo.

Modules and Namespaces... Groovy does have the "import foo.Bar as foobar" syntax. Meaning that the class foo.Bar is now known as foobar.

Ah, yes, array literals... Groovy does have list literals, yes, but you can do
[1,2,3] as int[]
to get an array of ints.

Next is Mixins... what Groovy has is.. I would call it runtime mixins. Yes there is another proposal and Groovy will have an implementation of that on the AST level for 1.0, but there wil possibly no syntax right now. Grails will then use the mixins as additional compiler phase.

Then.. "Calling a method using reflection". Thanks to duck typing I can use p.getName("Sundar"). I don't recommend the usage of invokeMethod in this case.

And one last thing maybe.. you can use Strings and Gstrings not only as normal Strings, but also for method, property or field names.
def printMethod = "println"
System.out."$printMethod"("I am using a print method customized through a GString")
Oh, and maybe you should think about adding "builder" to the page as well? But well, I am sure, there are more missing elements. but this is not thought as complete overview, just a quick start ;)

Posted by Jochen Theodorou on September 18, 2006 at 03:30 PM IST #

Nice work, but I think you should include Jython in the list also.

Posted by Lawrence Oluyede on September 18, 2006 at 03:32 PM IST #

Great job! For the people that would like to try Ruby, they can use the interactive Ruby page, where it's possible to execute Ruby statements directly from the browser.

Posted by Christian Caldeiro on September 18, 2006 at 04:16 PM IST #

Default decimal number types are different between the languages. Also note that the first puts() using the addition is misleading in Ruby.

java: 1.0 is a double.
System.out.println(1.1 + 0.1) -> 1.2000000000000002
System.out.println((1.1 + 0.1) == 1.2)) -> false

groovy: 1.0 is a BigDecimal
println (1.1 + 0.1) -> 1.2
println ((1.1 + 0.1) == 1.2) -> true

ruby: 1.0 is double
puts (1.1 + 0.1) -> 1.2
puts (1.1 + 0.1 == 1.2) -> false

Posted by Scott Hickey on September 18, 2006 at 04:42 PM IST #

jruby: 1.0 is a double 1.1 + 0.1 => 1.2000000000000002 1.1 + 0.1) == 1.2 => false

Posted by Scott Hickey on September 18, 2006 at 04:53 PM IST #

Post a Comment:
Comments are closed for this entry.
About

sundararajan

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today
Bookmarks
Links

No bookmarks in folder

Blogroll

No bookmarks in folder

News

No bookmarks in folder