5

Simple question. In Java 8 we have huge number of new methods in JDK classes. Say we have created such class, using Java 7 (or Java 6):

class MyArrayList<E> extends ArrayList<E> {
        public void sort(Comparator<E> c) {
            // some sort
        }
}

This is quite reasonable implementation. Now we try to compile it with Java 8 and receive expectable compile error:

error: name clash: sort(Comparator<E#1>) in MyArrayList and sort(Comparator<? super E#2>) in ArrayList have the same erasure, yet neither overrides the other
            public void sort(Comparator<E> c) {
                        ^   where E#1,E#2 are type-variables:
    E#1 extends Object declared in class I.MyArrayList
    E#2 extends Object declared in class ArrayList

Here I would like to arise 2 questions:

  1. Even with javac -source 1.7 -target 1.7 option using JDK 8 I receive same error - why? I thought these options should allow compile legacy code.

  2. How about backward compatibility in general?

EDIT To be precise, may be I'm doing something wrong? JDK 1.8.0_65, Mac OS X:

bash-3.2$ javac -version
javac 1.8.0_65
bash-3.2$ javac -source 1.7 -target 1.7 MyArrayList.java 
warning: [options] bootstrap class path not set in conjunction with -source 1.7
MyArrayList.java:7: error: name clash: sort(Comparator<E#1>) in MyArrayList and sort(Comparator<? super E#2>) in ArrayList have the same erasure, yet neither overrides the other
    public void sort(Comparator<E> c) {
                ^
  where E#1,E#2 are type-variables:
    E#1 extends Object declared in class MyArrayList
    E#2 extends Object declared in class ArrayList
1 error
1 warning
Andremoniy
  • 34,031
  • 20
  • 135
  • 241
  • 2
    This is described in the javac docs. "But javac also supports cross-compiling, where classes are compiled against a bootstrap and extension classes of a different Java platform implementation. It is important to use the -bootclasspath and -extdirs options when cross-compiling." – pvg Jan 12 '16 at 22:45
  • 1
    `-source 1.7` ensures that language features from Java 8 are not used, and `-target 1.7` ensures that generated bytecode is compatible with a 1.7 JVM. Neither changes the Runtime Library being used. You need more options to change the runtime library to Java 7 during compilation. – Andreas Jan 12 '16 at 22:46
  • 2
    Relevant: http://stackoverflow.com/q/31188231/1743880 – Tunaki Jan 12 '16 at 22:47
  • @Tunaki Thanks, exactly. This is answer to my second question. – Andremoniy Jan 12 '16 at 22:48
  • 1
    The source and target specify the version of the language to use not the version of the libraries to use. – Peter Lawrey Jan 12 '16 at 23:13
  • yes, java8 breaks things like that, and you aren't the only one complaining:) – ZhongYu Jan 13 '16 at 02:00

2 Answers2

8
  1. because even with these options, you're still compiling against the Java 8 classes. The JDK doesn't have any idea which methods appeared in what version of the JDK. All these options do is tell the compiler to accept only Java 7 syntax in the code you're compiling, and to generate Java 7 bytecode. You would have to pass actually link to the JDK 7 classes (using the -bootclasspath option) to cross-compile.

  2. Yes, it's a problem. Not huge, and the benefit of having all thses new default methods is more important than the inconvenience of having some rare non-compiling code.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • That is very strange :) If I have JDK 7, why I should try to compile it with JDK 8? Looks like useless feature, doesn't it? – Andremoniy Jan 12 '16 at 22:51
  • 2
    That's what I would do by default. Unless I have a library/framework that can work with Java 7, but provides an added value when used with Java 8, or must support classes that only exist in Java 8. Take Spring, for example: it natively supports classes in the java.time package, but is nevertheless compatible with Java 7. – JB Nizet Jan 12 '16 at 22:57
  • @Andremoniy: the potential problem of class evolution when methods are added to a superclass always existed in Java. At least, Java 8’s `javac` now warns about using the `-source`/`-target` options without a matching boot class path. This kind of usage still has a point. The Java 8 compiler might contain new bug fixes. See [here](http://stackoverflow.com/q/34659055/2711488) for an example of a difference. – Holger Jan 13 '16 at 15:01
7
  1. -source 1.7 only says that the source uses Java 7 language features. -target 1.7 says that the outputted bytecode targets a specific version of the JVM. However, you are still compiling against JDK 8. Since you are cross-compiling, you have to tell javac where the bootstrap and extension classes for Java 7 exist using -bootclasspath and -extdirs
  2. Default methods in interfaces (that were introduced in Java 8) lets you add new functionality without breaking existing code. It's not a bulletproof solution and there can be minor issues (this answer explains the issues in some detail). But on the whole backwards-compatibility issues are quite rare.
Community
  • 1
  • 1
Vivin Paliath
  • 94,126
  • 40
  • 223
  • 295
  • That is very strange :) If I have JDK 7, why I should try to compile it with JDK 8? Looks like useless feature, doesn't it? – Andremoniy Jan 12 '16 at 22:51
  • 2
    Not necessarily. This way you can just maintain the runtime-library jars and any other extensions you need instead of maintaining the *entire* JDK distribution for a particular version. In addition, you can just use one compiler to compile to different targets, instead having to switch between compiler versions for the target you need (makes things easier for tooling). But yeah, if you already have JDK 7, then you don't strictly *need* to use JDK 8 to compile JDK 7 code. It depends on your usecase. – Vivin Paliath Jan 12 '16 at 22:59