martinez314
martinez314

Reputation: 12332

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

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:

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

Upvotes: 5

Views: 270

Answers (4)

zapl
zapl

Reputation: 63945

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.

Upvotes: 1

E-Riz
E-Riz

Reputation: 32895

This could be due to the JRE System Library that's configured on your project's Build Path not matching the compliance level you've selected. In general you almost always want to select the "Use compliance from execution environment" option in the project's compiler settings. Check your project's Build Path and see if you've specified the JRE System Library as an Execution Environment.

Upvotes: 0

Jean Hominal
Jean Hominal

Reputation: 16796

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.

Upvotes: 1

dkatzel
dkatzel

Reputation: 31648

My guess is you are correct about why ECJ will compile somethings and not others when set to Java 6. Generics do just compile down to the same things as casts so it that might be why it works if the target is set to java 6?

See What is the difference between javac and the Eclipse compiler? for the other differences between javac and ECJ.

Upvotes: 0

Related Questions