some personal, more technical – by Thomas Einwaller
In: Java
27 Dec 2007Lately I came across some differences between the Eclipse Java compiler and the Sun Java compiler. I already experienced them in another case but both have to do with how the compiler handles generics. In most of my projects I am coding in Eclipse and building the product using ant – and the Sun Java compiler. In these cases everything works fine inside Eclipse which means the code compiles and runs but the ant build process fails. Here is some example code:
import java.util.ArrayList;
import java.util.Collection;
public class CompCheck {
private Collection<Class> getClasses() {
final ArrayList<Class> list = new ArrayList<Class>();
list.add(String.class);
list.add(Integer.class);
return list;
}
public static void main(final String[] args) {
final CompCheck compCheck = new CompCheck();
final Collection<Class<?>> classes = new ArrayList<Class<?>>();
classes.addAll((Collection<? extends Class<?>>)
compCheck.getClasses());
for (final Class<?> clazz : classes) {
System.out.println(clazz);
}
}
}
In my case the getClasses method was provided by a library and so I could not change it. The cast Collection<? extends Class<?>> was added by the Eclipse Quick fix feature and it worked in Eclipse. But the ant build script failed with the following message:
[javac] D:\workspace\compcheck\src\CompCheck.java:16: inconvertible types
[javac] found : java.util.Collection<java.lang.Class>
[javac] required: java.util.Collection<? extends java.lang.Class<?>>
[javac] classes.addAll((Collection<? extends Class<?>>)
compCheck.getClasses());
[javac] ^
[javac] 1 error
Who is to blame? Is it the Eclipse compiler or the Sun Compiler? I had to change my code back to the following which produces a lot of warnings:
import java.util.ArrayList;
import java.util.Collection;
public class CompCheck {
private Collection<Class> getClasses() {
final ArrayList<Class> list = new ArrayList<Class>();
list.add(String.class);
list.add(Integer.class);
return list;
}
public static void main(final String[] args) {
final CompCheck compCheck = new CompCheck();
final Collection<Class> classes = new ArrayList<Class>();
classes.addAll(compCheck.getClasses());
for (final Class<?> clazz : classes) {
System.out.println(clazz);
}
}
}
This piece of code compiles with both compilers. The best solution would be the following:
import java.util.ArrayList;
import java.util.Collection;
public class CompCheck {
private Collection<Class<?>> getClasses() {
final ArrayList<Class<?>> list = new ArrayList<Class<?>>();
list.add(String.class);
list.add(Integer.class);
return list;
}
public static void main(final String[] args) {
final CompCheck compCheck = new CompCheck();
final Collection<Class<?>> classes = new ArrayList<Class<?>>();
classes.addAll(compCheck.getClasses());
for (final Class<?> clazz : classes) {
System.out.println(clazz);
}
}
}
This one compiles with both compilers and produces zero warnings in Eclipse. I had to read the Java 5 generics tutorial again to understand what the wildcard generic really means but I did not find any information on the web about this difference between the compilers.
10 Responses to Differences between Java compilers
Martin
December 27th, 2007 at 7:35 pm
Read this FAQ http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html. I have once listen to one of Angelikas presentations during a Java conference where she referenced to this site. It has helped me a couple of times to understand generics in depth. However I haven’t checked your specific problem to see if it would answer it
tompson
December 28th, 2007 at 7:20 pm
You already sent me this link, very useful site. But this problem has nothing to do with generics themself it seems like this is just a compiler problem.
narup
January 3rd, 2008 at 8:25 pm
Ya it’s just the compiler issue and nothing to do with generics, i also had similar issue.
Tonny Madsen
January 3rd, 2008 at 9:35 pm
Have anybody found who is to blame here? It would be nice to know…
Daniel Spiewak
January 3rd, 2008 at 9:53 pm
It’s actually Sun’s fault. I ran into this problem a while back and opened a bug against Eclipse first (after all, suspect the third party). Upon verification, it seems that Eclipse is following the spec more closely than javac does. https://bugs.eclipse.org/bugs/show_bug.cgi?id=205195
I never got around to putting this in the JDK bug parade, but at least it’s nice to know who’s to blame.
tompson
January 4th, 2008 at 11:26 am
Thanks for that information Daniel! I too thought that this is an Eclipse bug but reading the bug entry I am pretty sure this is a bug in javac – anyone should file a bug!
Alaeddin
January 10th, 2008 at 1:39 pm
is there any workaround suggested other than changing the code? is it a bad idea to think of using eclipse compiler from ant file (i.e. command line like javac)
Thank you
Daniel Spiewak
January 10th, 2008 at 4:27 pm
No real workaround. The only thing to be done would be to open a bug against javac and hope they fix it eventually.
Actually the Eclipse compiler works great when parsed out from the main IDE. Tomcat actually uses it to compile JSPs and such because it’s faster and more incremental than javac.
tompson
January 10th, 2008 at 10:13 pm
As Daniel said I have no workaround too. The thing I do is to change my code so that javac is able to deal with it. It should be possible to use the Eclipse compiler inside an ant build but I never tried that.
Saua
March 10th, 2008 at 3:47 pm
An ugly “solution” that compiles in both Eclipse and javac is using two casts:
classes.addAll((Collection<Class>) (Collection) compCheck.getClasses());
This first discards all generic information and then casts to the desired “generified” Collection. Ugly, but at least it compiles in javac.