Monday, February 04, 2008

Sun javac / Eclipse compiler difference

Our primary development environment is Eclipse 3.3.1.1, set to Java 5 compatibility. However during the nightly builds we use Sun's javac, currently version 1.5.0_12 I believe.

Today I noticed one of the builds had failed in this code (reduced to a small test case):

public interface Task<V> {}
public interface RunnableTask extends Runnable, Task {}
public class ProgressMonitorUtil {
public <V> V execute(final Task<V> aBackgroundTask) {
return null;
}
public void execute(final RunnableTask aBackgroundTask) {}
}
public class Test {
public static void main(String[] args) {
new ProgressMonitorUtil().execute(new RunnableTask() {
    public void run() {}
});
}
}

Put these classes into an Eclipse project, either with Java 5 or Java 6 compatibility settings. You will see no problems. However compiling this with Sun's javac (also either 5 or 6 will do) leads to this:

Test.java:4: reference to execute is ambiguous, both method <V>execute(Task<V>) in ProgressMonitorUtil and method execute(RunnableTask) in ProgressMonitorUtil match
new ProgressMonitorUtil().execute(new RunnableTask() {

Note: Test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error

Apparently method dispatching works differently in both compilers. In Eclipse the call goes to the second method (void execute...) which seems reasonable since RunnableTask is a more specific type.

However you can still convince Sun's compiler to compile this if you get rid of the "unchecked" warnings:

public interface RunnableTask extends Runnable, Task<Void> {}
public class ProgressMonitorUtil {
public <V> V execute(final Task<V> aBackgroundTask) {
return null;
}
public void execute(final RunnableTask aBackgroundTask) {}
}
public class Test {
public static void main(String[] args) {
    new ProgressMonitorUtil().execute(new RunnableTask() {
        public void run() {}
    });
}
}

This compiles without warnings or errors on both compilers and leads to the same dispatching that Eclipse does. I guess I will post this as an Eclipse bug nevertheless, because responses there are usually way quicker than at Sun...

Edit: Making RunnableTask generic is not very useful in this context, but instead just declaring it as "extends Task<Void>" is sufficient as well. I updated the samples above.

2 comments:

OrwTech said...

Got the same error using InputSource for XPath and implemented this work-around:

XPathExpression expr = xpath.compile("//root/element");
inputSource.getByteStream().reset(); // this has to be done to avoid the error "org.xml.sax.SAXEception: Premature end of file"
value = (String)expr.evaluate(inputSource, XPathConstants.STRING);

Then you are able to call evaluate method more than once.

Anonymous said...

Hi Daniel,

did you post a bug for this issue and/or have you heared something about it?