« September 2006 »
SunMonTueWedThuFriSat
     
2
3
6
7
9
10
11
13
15
16
17
20
23
24
28
29
30
       
Today
XML

Blog::Navigation

GetJava Download Button
Get the Source
Personal Blog

Blog::Referers

Today's Page Hits: 202

Powered by Roller Weblogger.
« Previous day (Sep 16, 2006) | Main | Next day (Sep 18, 2006) »
20060918 Monday September 18, 2006

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");




( Sep 18 2006, 02:01:42 PM IST ) Permalink Comments [11] del.icio.us | furl | simpy | slashdot | technorati | digg

Copyright (C) 2005, A. Sundararajan's Weblog