Saturday, October 13, 2007

Java 5 features in Groovy

Groovy 1.1 RC-1 has been released yesterday and I thought this is a perfect time for giving a complete list of all the Java 5 features that are available in Groovy and how much of it. I will not explain the Java 5 features itself, I will just show what you can do and what not. So let us start right away:

Variable Arguments

Groovy is able to use this for a long time now. Since Groovy does not know exactly which method it will invoke at compile time we use a vargs (variable arguments) when selecting a method for invocation. but not only the vargs Java has...I mean these things with the funny little triple dotted type as last parameter, no Groovy just requires an array there. so if you want to use a vargs method from Groovy just do it like in Java. vargs methods defined in Groovy can be used as such from Groovy too. Currently Groovy does not add the special modifier for vargs to the array, so it won't be a vargs method for Java, just a method taking an array as last parameter. I just filled an issue for that and it will be fixed before the 1.1 release. Ah yes, before I forget... of course you can use the triple dot notation instead of using an array in Groovy too. The tripple dot notation will be exactly the same as an array as last parameter.

To use vargs no java5 vm is needed from the groovy side.

New For-Loop

Groovy had always a for-loop in that style, only we don't use only the ":", we use allow also the usage of the keyword"in" at that place. The Groovy for-loop asks for an iterator by calling the iterator() method, which is available on any object. Groovy adds a dynamic iterator() method to the MetaClass for every class as long as the class does not define it on its own.

To use the new for-loop no java5 vm is needed from the groovy side.

Covariant Return Types

It took a while before Groovy was able to use these (available since 1.1 RC-1). Originally we wanted to be able to overload a method if the return type differs, not override it. But it seemed to make no sense to differ here from Java. So Groovy follows here Java now and if you write a subclass, that has a method that matches a method in the parent class in name and argument types, but has a different return type, then covariants kick in. If the the new method has a return type derived from the old method, then the new method overrides the old method. If it does not derive from that, then you get a compile time error. As an example:

class A{
Object foo(){1}
}
class B extends A{
String foo() {2}
}
def b=new B()
assert b.foo()==2
To use covariant return types no java5 vm is needed from the groovy side.

Annotations

Again these are available for some time now. The syntax is equal to Java. Even though you can define interfaces in Groovy you can not define an annotation in Groovy yet. But using Annotations in Groovy is basically the same as in Java.

If annotations are used Groovy needs to produce java5 bytecode.

Generics

Well... originally we didn't want to add them. They are complicated and look kind of surplus in a dynamic world. But some applications can make use of generics. for example List<book> books contains much more informations for a persistence layer like for example JPA. But maybe I should first explain that we do not support checks where type erasure is used. For example if you define a script like
List<String> list = ["a","b"]
list << 1
then Groovy won't complain. This is simply ignored. But if you use generics for a method, a field or in the class header (class X<A,B> extends Y<A> implents Z<B>), then Groovy will write the information in bytecode the same way as Java would do. That means if you use the Groovy class in Java, you have the same pain... ehm pleasure... what ever... as with a class defined in Java. With the 1.1 RC-1 release Groovy now also checks the header information, so you won't be able to write class X extends Map<String> anymore and get a compile time error now.

If generics are used Groovy needs to produce java5 bytecode. Groovy needs to be build with >java5 for this (only important if you build Groovy by yourself).

Generics+Covariant Return Types

I already covered these two, but I think the combination is worth an extra word. If you have a program like this
class A<T> {
T foo(T t) {1}
}
class B extends A<Long> {
String foo(Long l) {"2"}
}
then Groovy will do as Java and throw a compile time error. If you use raw types you will see that the following script for example works:
class A<T> {
T foo(T t) {1}
}
class B extends A {
String foo(Long l) {"2"}
}
def b = new B()
assert b.foo(new Object( )) == 1
assert b.foo((Long) 1) == "2"
If used with raw types the method A#foo returns Object and takes Object. Since B#foo takes a Long Groovy will not care about the covariant return types and compile it. So you end up with two version of foo in B, one derived from A returning 1 and one from B returning 2. If the usage is like in the first case above, then A<Long> "kind of" defines a foo method taking a Long and returning a Long. I say "kind of", because the class does not really define that method, it is just the view B gets of A. Anyway, since B defines also a foo(Long) the covariant return type function kicks in, but finds that the return types String and Long are incompatible. So it will give a compile time error. The following script would for example compile:
class A<T> {
T foo(T t) {1}
}
class B extends A<Llon>> {
Long foo(Long l) {2}
}
def b = new B()
assert b.foo((Long) 1) == 2
The foo defined in B will now override the foo defined in A.

Enums

Groovy now supports with 1.1-rc-1 also simple enums. With simple enums I mean you can declare them in Groovy, you can even declare them inside a nnormal class as long as you don't use references from the surrounding class. You can also define methods/fields/properties that will be in all enums. This should cover almost all of the features you need for enums. What we do not support is writing code that is special to one enum value. For example overwritng a method in the enum value, or addding a method/field/property. I have never seen such enums in real live, so I think it is not too important.... On the other hand I haven't seen much enums at all and my picture might be wrong. Anyway, Groovy is most possibly not going to support these enums with additional methods.
Groovy enums will differ a little from Java enums, they will implement GroovyObject and thus you can have a meta class per enum value. That relativates the missing feature with additional methods a bit I think.

If enums are used Groovy needs to produce java5 bytecode.

Asking The User!

I hope all the people out there, that like Groovy so much, will help us in finding bugs for these features. Some features are quite new, sometimes the tests lack fantasy (especially if written at 3 o'clock in then morning) and do not cover all needed cases. Sometimes something is simply misunderstood. I encourage all users to find the bugs and enter issues for them so we can deliver a pleasant 1.1 release to you.