Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Eclipse let me compile some Java 7 language features into Java 6 class files?

Tags:

java

eclipse

ecj

I have found that in Eclipse (using the Eclipse compiler) I can use some Java 7 language features but still create Java 6 class files. In the below image, you can see two Java 7 language features that are successfully compiled as a Java 6 class file. However, other Java 7 features, those commented out, do not compile.

My assumption is that Eclipse is determining which Java 7 language features are compatible with the Java 6 JVM and which are not. For example, the generic type JComboBox is just a compilation (and not runtime) feature, so I can imagine how it would be compatible. The switch String feature though I would think might make differences in byte code and rely on new JVM features, but I could be wrong...

My questions:

  • Is Eclipse really smart enough to know which Java 7 language features are capable of being compiled into Java 6 class files and which are not?

  • The example below is clearly not 1.6 source compatible, so why does setting "Source compatibility" to 1.6 not cause an error?

  • This "trick" appears to let me use at least some Java 7 language features and still create Java 6 class files. Using javac with source 1.7 and target 1.6 would fail, so why does this work? Does the Ecilpse compiler have a feature that javac does not?

enter image description here

For the sake of comparison here is the result when I switch to a Java 6 compiler, as expected.

enter image description here

like image 486
martinez314 Avatar asked Oct 31 '22 15:10

martinez314


2 Answers

I think two things are happening:

  1. I suspect that the first line (with generic JComboBox) works because the Java 1.7 rt.jar is linked instead of Java 1.6 rt.jar (I have a project which is setup with JavaSE-1.6, and in that case, that first line does not compile even with your first combination of settings). But that is a class library issue, not a language version issue. (You can get lots of issues even with javac if you compile your Java app against a newer rt.jar than when you run it).

  2. The second line probably represents a bug in the Eclipse compiler. While most of the Java 7 new language features can be implemented purely in the compiler (which Android does since late 2013), doing that is obviously not source-compatible with Java 6.

So, in short, you have found at least one bug in a (probably) unusual Eclipse configuration. Take care and do not rely on it.

like image 186
Jean Hominal Avatar answered Nov 15 '22 03:11

Jean Hominal


I don't know why Eclipse allows this or whether this is just a bug. 1.7 javac will tell you that

error: strings in switch are not supported in -source 1.6

I also don't know why JComboBox works,

System.out.println(new JComboBox<String>() {}.getClass().getGenericSuperclass());

> javax.swing.JComboBox<java.lang.String>

has generic information at runtime that should not be there. Allowing to use generics for classes that are not generic should IMO be rejected as incompatible. I didn't run above code on JVM6 though. Maybe it will even crash.

But at least switch is technically no problem. http://www.benf.org/other/cfr/java7switchonstring.html shows that this is just a compiler trick that requires no new language feature, API or bytecode.

Slightly simplified example:

int java7(String string) {
    switch (string) {
        case "BB":
            return 12;
        case "FRED":
            return 13;
    }
    return 0;
}

becomes essentially

int java6(String string) {
    switch (string.hashCode()) {
        case 2112:
            if (string.equals("BB"))
                return 12;
            break;
        case 2166379:
            if (string.equals("FRED"))
                return 13;
            break;
    }
    return 0;
}

That's based on the fact that the outcome of String#hashCode() is specified and must not change. The compiler saves you just some time to write otherwise legal code quicker.

The same should apply to the diamond operator: e.g. new ArrayList<>() can simply be resolved by the compiler.

The android tools, which allow the same semi 7 compatibility, allow you to use it. However, the difference is that they use .class files targeted at Java 7. Android needs to convert .class files to it's internal format anyways, so their .class to .dex compiler can use whatever input works to generate instructions that are understood by the Android runtime.

try-with-resource for example won't work since it requires amongst others the AutoCloseable interface with did not exist in Java 6.

And special features like Lambda expressions do require new types of bytecode as well.

like image 40
zapl Avatar answered Nov 15 '22 03:11

zapl