5

I'm reviewing the HikariCP project on github, and it declares that it supports "Java 7 and Java 8 maven artifact", in its source code, it uses some Java 8 features:

java.util.function.Consumer;
java.util.function.Predicate;
java.util.function.UnaryOperator;

I guess if this project is being referenced by others with Java 7, error will occur. So, how the project makes it to support Java 7 and Java 8 at the same time?

Raedwald
  • 46,613
  • 43
  • 151
  • 237
lawrence
  • 353
  • 2
  • 17
  • Might be an old comment. – Thorbjørn Ravn Andersen Aug 12 '16 at 03:02
  • 1
    There is no way to do it without maintaining two versions of the code http://stackoverflow.com/questions/16143684/can-java-8-code-be-compiled-to-run-on-java-7-jvm – vempo Aug 12 '16 at 05:49
  • Link to the project so that we can answer your question. If it declares that both Java 7 and Java 8 artifacts are supported, it's hard to tell how this is achieved without seeing the actual project. I believe they do have two **different** artifacts, one for Java 7 and the other for Java 8. – vempo Aug 12 '16 at 06:00
  • @vempo pls refer this project https://github.com/brettwooldridge/HikariCP.only one artifact Java 7 and Java 8 maven artifact: com.zaxxer HikariCP 2.4.7 – lawrence Aug 12 '16 at 06:10
  • I guess you mean this class: https://github.com/brettwooldridge/HikariCP/blob/master/src/main/java/com/zaxxer/hikari/util/FastList.java – Puce Aug 12 '16 at 07:54

2 Answers2

5

This is not a mistake (as I myself thought). The project indeed uses classes from Java 8. It does not compile with Java 7, and its Maven build doesn't run with Java 7 either.

However, as Java 8-specific features like Lambdas are used nowhere in the source code, it does run with Java 7.

Try creating a Java 7 project, declaring HikariCP as a dependency, and running the following code:

import com.zaxxer.hikari.util.FastList;

public class Main {

    public static void main(String[] args) {

        FastList<String> fastList = new FastList<>(String.class);
        fastList.add("Hello");
        System.out.println(fastList);
    }
}

It runs successfully. On the other hand, the following code fails:

fastList.removeIf(null);

This is because removeIf() and some other methods use classes from Java 8, and can't therefore run with Java 7. But they all throw UnsupportedOperationException anyway! You may notice that the only class to import Java 8 classes is com.zaxxer.hikari.util.FastList. I'm not sure why they did it.

UPDATE: Just wanted to clarify that the project bytecode's version is 1.7, as can be easily verified with a decompiler or hexdump. Its source code does comply with Java 7 and therefore can be build with

<source>1.7</source>
<target>1.7</target>

as pointed out by @Puce.

On the other hand, it must be compiled with JDK 1.8 so that the Java 8 classes referenced in the source code are available during compilation. Once the code has been compiled, it can run with Java 7 as long as no attempts are made to load a missing Java 8 class (from the java.util.function package in this case).

vempo
  • 3,093
  • 1
  • 14
  • 16
  • I haven't tried, but it seems strange that JRE 7 can load a class which references types, which are not available... – Puce Aug 12 '16 at 08:08
  • @Puce, I agree, nothing seemed to make sense to me either :) so I spent some time trying to understand how it was possible and came up with this sample code. Java 7 works fine as long as it doesn't try to actually load the Java 8 classes. – vempo Aug 12 '16 at 08:12
  • 1
    If a plugin is configured in the pluginManagement section, that configuration is used whenever the plugin is used. The compiler plugin is bound to the compile phase of the default lifecycle by default. You don't have to specify it in the plugins section. – Puce Aug 12 '16 at 08:12
  • I just double-checked and it really runs with JDK 7, though I still don't understand the class loading mechanism here... – Puce Aug 12 '16 at 08:19
  • @Puce you're right about ``, but I think it's true only for `maven-compiler-plugin` and maybe some others (built-in?). I am pretty sure a regular plugin declared under `` doesn't actually run unless it's referenced in the `` section of a child pom. Here's what Maven documentation has to say about it https://maven.apache.org/pom.html#Plugin_Management – vempo Aug 12 '16 at 08:32
  • 1
    If you explicitly run a plugin goal using `mvn somePlugin:someGoal`, it would still respect the pluginManagement section. If you run a phase (e.g. `mvn install`), then only goals bound to a matching phase up to the one you're running are executed, of course. – Puce Aug 12 '16 at 08:37
  • @Puce I think I got it. The source code _does_ comply with Java 7. The only thing from Java 8 are the `java.util.function` classes and parent classes that have the same names but additional methods, which does not matter because for example `@Override`'s retention is SOURCE. So there is no problem compiling with source=1.7 and target=1.7, but only with JDK 1.8, so that the new classes are available in the classpath. Consequently, the compiled code runs with Java 7 as long as it doesn't try to load `java.util.function.*`. – vempo Aug 12 '16 at 09:20
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/120778/discussion-between-vempo-and-puce). – vempo Aug 12 '16 at 09:21
  • 1
    @Puce: the JVM specification provides some freedom to when a class referenced by executable code has to be resolved. While, e.g. a super class has to be resolved immediately, resolving a method’s parameter type might be postponed to the moment, the method is actually invoked. So it’s wrong to say that the code here complies to Java 7, it just happens to work with Oracle’s JRE implementation. So, Oracle’s JRE is standard compliant, but a JVM which fails immediately due to the missing classes, would be as well. – Holger Aug 17 '16 at 13:23
  • @Holger The code does compile to Java 7. See my update. The source level is still 1.7, it's just that it references classes from Java 8 (JDK 1.8). I have verified that the byte-code is indeed 1.7. In other words, you could have build the project with JDK1.7 if you had the `java.util.function.*` classes in the classpath. – vempo Aug 17 '16 at 13:59
  • 2
    @vempo: having compatible byte code does not necessarily imply a working, standard conforming application. As said, a JVM implementation is allowed to eagerly resolve (even indirectly) referenced classes, which implies that you have code that could fail on a compliant JVM, even if it happens to work on the specific implementation from Oracle. Of course, it would work on all JVMs, if you add the `java.util.function.‌​*` classes to the bootstrap class path, but that is also an action outside of the standard. – Holger Aug 17 '16 at 14:14
  • @Holger I understand your argument about class reference resolution, but on the other hand your statement that it would work on all JVMs is not true. Java 8 byte-code may not run on a JVM that supports up to Java 7. This project compiles to Java 7 byte-code which allows it to run with JDK 1.7, and except the usage of Java 8 classes is a normal Java 7 project. That's what the initial confusion was all about. – vempo Aug 17 '16 at 14:25
  • 2
    @vempo: I thought, it was obvious from context that we are talking about your Java 7 compliant byte code only. Your project compiles to Java 7 byte-code which allows it to run with **Oracle’s** JDK 1.7, nothing more, nothing less. It’s still not a (fully) compliant Java 7 application as it indirectly refers to classes not present in Java 7. If you can live with that, as it runs on Oracle’s JVM and most other mainstream JVMs, it’s ok, but you just should know… – Holger Aug 17 '16 at 14:38
  • @Holger Sorry misread "compiles to" instead of "complies to/with". In any case, I think using Java 8 classes in a project that targets Java 7 was a bad idea. – vempo Aug 17 '16 at 14:56
  • Hi, principal developer of HikariCP here. HikariCP requires Java 8 to compile, but generates Java 7 bytecode. HikariCP does cheat, in that it depends on the "lazy" resolution behavior of the Oracle and OpenJDK VMs. This is unspecified behavior, and a VM that employed "eager" resolution, and was only at the Java 7 level, would fail. Fortunately, of the hundreds of thousands of HikariCP users we have never had a reported failure. There are possibly some "research" VMs out there with eager resolution, but in production they are rare or non-existent. – brettw Aug 25 '16 at 14:49
0
<plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>3.5.1</version>
               <extensions>true</extensions>
               <configuration>
                  <source>1.7</source>
                  <target>1.7</target>
               </configuration>
</plugin>

It might be an error of the project.

Specifying the source and target level does only respect language features and influence the byte code version. APIs are not checked.

If they're using a JDK 8 to build the project, then the project builds fine. But I doubt it will compile with JDK 7.

They might have thought that specifying 1.7 here will make it support Java SE 7, which is not automatically correct.

Have you tried to access the library with Java SE 7?

The good news for the project is, that it seems easy to support Java SE 7. FastList is just a List implementation. Since the List interface has been extended in Java SE 8, they were forced to implement the new methods as well even when they throw only a UnsupportedOperationException.

By using a JDK 7 to build the project, they could easily remove those seemingly unneeded methods.

Puce
  • 37,247
  • 13
  • 80
  • 152
  • 1
    The new methods are `default` methods. No-one is enforced to implement them, regardless of which JDK you use… – Holger Aug 17 '16 at 13:24