We're using java 8 for most modules/projects, but for some of the modules, we use java 6 (customer requirements).
The developers have java 8 installed and we compile the java 6 projects using these flags:
compileJava {
sourceCompatibility = 1.6
targetCompatibility = 1.6
}
We thought we're all good until we upgraded guava from v20 to latest - 28.1-jre.
To our surprise, the build was successful but failed at runtime.
We have a workaround for building for java 6 using a specific javac found in JDK 6. See more info here. This workaround wields the error class file has wrong version 52.0, should be 50.0 in compile time. The downside is that it requires a download+config+usage of JDK 6 for developers.
Is there a way to validate the dependencies' java version at compile time when using a higher java version? (without installing lower version java) Thanks.
Setting -source and -target values to 1.6 is insufficient to ensure that the resulting output is compatible with 1.6. The program itself must not have any library API dependencies on later versions, and the -source and -target options don't do that. (GhostCat said pretty much the same thing.)
For example, in Java 8, ConcurrentHashMap added a covariant override for the keySet method that returns a new type ConcurrentHashMap.KeySetView. This type didn't exist in earlier versions of Java. However, in the class binary, the return type is encoded at the call site. Thus, even if the source code is compiled with -source 1.6 -target 1.6, the resulting class file contains a dependency on the Java 8 class library API.
The only solution to this is to ensure that only Java 1.6 compatible libraries are in the classpath at compile time. This can be done using the -Xbootclasspath option to point to a JDK 1.6 class library, or it might be simpler just to use a JDK 1.6 installation in the first place.
This applies to external libraries in addition to the JDK, as you've discovered with Guava. The Animal Sniffer project provides plugins for Ant and Maven that checks library dependencies for version problems. Offhand I don't know if there is something similar for Gradle. There might be a way to get Animal Sniffer to work with Gradle, but I have no experience with doing that.
Is there a way to validate the dependencies' java version at compile time when using a higher java version? (without installing lower version java).
You specify your dependencies. When you tell your built system to explicitly use some library X in version Y, then you made a very clear statement.
And you see, it is not only about the class file version number. What if some person doesn't pay attention, and compiles something with Java8 ... with Java6 target, but forgets that the code bases uses Java8-only API calls?!
In other words: you are looking in the wrong place.
The person who makes updates to the build description, and changes a library version from Y to Y+8, that person needs to carefully assess that change. For example by reading release letters.
I agree that a really clever build system could check if libraries you are using come in with a matching class file version. But as said, that is only one aspect of the problem. So instead of looking into a technical solution, I think the real answer is: don't step version numbers because you can, but because you have to. And that manual step of changing that version number, that is something that requires due diligence (on the side of the human doing it).
Thus: I think the most sane approach here is to compile the Java6 deliverables within their own specific build setup. Which you only touch after careful inspection of such details. And sure: convince your customer to move on, and give up a long dead version of Java.
I am trying to make a simple Interpreter for Java.
Out teacher gave us a code to modify to reach our goal, but Eclipse doesn't like the keyword 'default'
The error is "Syntax error on token "default", delete this token"
I think it depends from my JDK version of java, this is what i am using
What should i modify?
Edit: i have Java 8 installed, but Eclipse doesn't allow me to change my compilance level over 1.7
Interface Default Methods introdced only in Java 8
Download jdk 8 and use it in Eclipse
Download
This is because you are using JRE JavaSE-1.7:
This JRE isis prior to Java 8, where default keyword has been introduced for defining default implementations of interface methods.
Switch JRE version to 1.8 to fix this problem.
Basically you just can not use/define an default method, because those are available since java 8 (and your project is using java7)... so you need to move the project to java8.
Oracle can help you, check this here
http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
is the What's New in JDK 8 part, 3rd element in the list....
Default methods enable new functionality to be added to the interfaces of libraries and ensure binary compatibility with code written for older versions of those interfaces.
I try to write my code compatible with older versions of java, so it will work everywhere.
on the other hand there is very powerful tools in the new version - java 8, and I want use them.
So I'm forced to choose between compatibility or richest code.
And I'm wondering if by any chance I can write some methods in java 8, and somehow prevent the compiler of older version to ignore these methods, so my class is compatible "partially" with older version.
Thanks.
You can write two classes and use some toll like ant, maven or gradle to chose which file use for compiling with concrete Java version.
You can set the java compiler to compile against an older jdk (ie jdk 1.5) even if you use jdk 1.8. see javac source and target options
I think the short and easy answer is no.
See this thread: Can Java 8 code be compiled to run on Java 7 jvm?
You can use the java reflection api to check if methods exist in the jvm the code runs on. This allows you to make your code fail-safe even when a method or class is unavailable in the jvm. Doing this is very cumbersome however and I'm pretty sure it's not what your're looking for.
Just wondering if anyone has tried using new Java 7 language features with Android?
I know that Android reads the bytecode that Java spits out and turns it to dex. So I guess my question is can it understand the bytecode of Java 7?
If you are using Android Studio, the Java 7 language should be enabled automatically without any patches. Try-with-resource requires API Level 19+, and NIO 2.0 stuff are missing.
If you can't use Java 7 features, see #Nuno's answer on how to edit your build.gradle.
The following is for historical interest only.
A small part of Java 7 can certainly be used with Android (note: I have only tested on 4.1).
First of all, you could not use Eclipse's ADT because it is hard-coded that only Java compiler 1.5 and 1.6 are compliant. You could recompile ADT but I find there is no simple way to do that aside from recompiling the whole Android together.
But you don't need to use Eclipse. For instance, Android Studio 0.3.2, IntelliJ IDEA CE and other javac-based IDEs supports compiling to Android and you could set the compliance even up to Java 8 with:
File → Project Structure → Modules → (pick the module at the 2nd pane) → Language level → (choose "7.0 - Diamonds, ARM, multi-catch, etc.")
This only allows Java 7 language features, and you can hardly benefit from anything since a half of improvement also comes from the library. Features you could use are those which do not depend on the library:
Diamond operator (<>)
String switch
Multiple-catch (catch (Exc1 | Exc2 e))
Underscore in number literals (1_234_567)
Binary literals (0b1110111)
And these features cannot be used yet:
The try-with-resources statement — because it requires the non-existing interface "java.lang.AutoCloseable" (this can be used publicly in 4.4+)
The #SafeVarargs annotation — because "java.lang.SafeVarargs" does not exist
... "yet" :) It turns out that, although Android's library is targeting for 1.6, the Android source does contain interfaces like AutoCloseable and traditional interfaces like Closeable does inherit from AutoCloseable (SafeVarargs is really missing, though). We could confirm its existence via reflection. They are hidden simply because the Javadoc has the #hide tag, which caused the "android.jar" not to include them.
There is already as existing question How do I build the Android SDK with hidden and internal APIs available? on how to get those methods back. You just need to replace the existing "android.jar" reference of the current Platform with our customized one, then many of the Java 7 APIs will become available (the procedure is similar to that in Eclipse. Check Project Structure → SDKs.)
In additional to AutoCloseable, (only) the following Java 7 library features are also revealed:
Exception chaining constructors in ConcurrentModificationException, LinkageError and AssertionError
The static .compare() methods for primitives: Boolean.compare(), Byte.compare(), Short.compare(), Character.compare(), Integer.compare(), Long.compare().
Currency: .getAvailableCurrencies(), .getDisplayName() (but without .getNumericCode())
BitSet: .previousSetBit(), .previousClearBit(), .valueOf(), .toLongArray(), .toByteArray()
Collections: .emptyEnumeration(), .emptyIterator(), .emptyListIterator()
AutoCloseable
Throwable: .addSuppressed(), .getSuppressed(), and the 4-argument constructor
Character: .compare(), .isSurrogate(), .getName(), .highSurrogate(), .lowSurrogate(), .isBmpCodePoint() (but without .isAlphabetic() and .isIdeographic())
System: .lineSeparator() (undocumented?)
java.lang.reflect.Modifier: .classModifiers(), .constructorModifiers(), .fieldModifiers(), .interfaceModifiers(), .methodModifiers()
NetworkInterface: .getIndex(), .getByIndex()
InetSocketAddress: .getHostString()
InetAddress: .getLoopbackAddress()
Logger: .getGlobal()
ConcurrentLinkedDeque
AbstractQueuedSynchronizer: .hasQueuedPredecessors()
DeflaterOutputStream: the 3 constructors with "syncFlush".
Deflater: .NO_FLUSH, .SYNC_FLUSH, .FULL_FLUSH, .deflate() with 4 arguments
That's basically all. In particular, NIO 2.0 does not exist, and Arrays.asList is still not #SafeVarargs.
EDIT: At the time this was written, the latest release was Android 9 and Eclipse Indigo. Thing have changed since then.
Practical answer
Yes, I have tried. But this is not a great test as the compatibility was limited to level 6 with no way (no simple way at least) to really use java 7:
First I installed a JDK7 on a machine that had no other JDK installed - Eclipse and Android are not installed either:
Then I installed a brand new Eclipse Indigo and checked it was actually using the JDK 7 (well, as this is the only one and as this is the one I've selected I would have been surprised)
Then I installed the latest version of the Android SDK (EDIT: Honeycomb, API13, at the time this post was written). It found my JDK 7 and installed properly. The same for ADT.
But I had a surprise when trying to compile and run a Hello Word Android app. The compatibility was set to Java 6 with no way to force it to Java 7:
I tried with a non-Android project, a regular Java one, and I had the explanation. The compatibility level seems to be limited by Eclipse (see the message at bottom of the following image):
So I had Hello World working, and also other apps, more complicated and using SQLite, Listview, Sensor and Camera, but this only proves that the compatibility handling of Java 7 seems to be well done and working with Android.
So, did someone try with the good old Ant, to bypass the Eclipse limitation seen above?
Theroetical answer
Anyway, the SDK is designed to be used with Java 5 or 6, as explained here.
We may have something working with Java 7, but it would be working "by accident". The building of the DEX may work properly or not, and once the DEX built, it may work or not. This because using a non-qualified JDK gives unpredictable results by definition.
Even if someone has succesfully built an Android app under plain Java 7, this does not qualify the JDK. The same process applied to another application may fail, or the resulting application may have bugs tied to the use of that JDK. Not recommended.
For those who are involved on webapps development, this exactly the same as deploying a web application built under Java 5 or 6 under an application server qualified for Java 4 only (let's say Weblogic 8 for example). This may work, but this is not something that can be recommended for other purposes than trying.
Quote from dalvikvm.com:
dx, included in the Android SDK, transforms the Java Class files of Java classes compiled by a regular Java compiler into another class file format (the .dex format)
That means, the .java source file does not matter, it's only the .class bytecode.
As far as I know, only invokedynamic was added to the JVM bytecode in Java 7, the rest is compatible to Java 6. The Java language itself does not use invokedynamic. Other new features, like the switch statement using Strings or the multi-catch are just syntatic sugar and did not require byte code changes. For example, the multi-catch just copies the catch-block for each possible exception.
The only problem should be that the new classes introduced in Java 7 are missing in Android, like AutoCloseable, so I'm not sure if you can use the try-with-resources feature (somebody tried it?).
Any comments on that? Am I missing something?
As of the Android SDK v15, along with Eclipse 3.7.1, Java 7 is not supported for Android development. Setting the source compatibility to 1.7 mandates setting the generated .class file compatibility to 1.7, which leads to the following error by the Android compiler:
Android requires compiler compliance level 5.0 or 6.0. Found '1.7' instead. Please use Android Tools > Fix Project Properties.
To expand on the above answer by #KennyTM, if you are targeting 4.0.3 and above (minSdkVersion=15), you can use the hidden APIs by adding a few classes to your target's SDK android.jar.
Once you do this, you can use try-with-resources on any Closeable, as well as implement AutoCloseable in your own classes.
I've made a zip containing sources and binaries of all the classes that needed to be modified in android.jar to make these APIs available. You just need to unpack it and add the binaries to your android-sdk/platforms/android-NN/android.jar
You can download it from here: http://db.tt/kLxAYWbr
Also of note is that, in the past couple of months, Elliott Hughes has made a few commits to the Android tree: finished off AutoCloseable, added SafeVarargs, unhidden various APIs, fixed Throwable's protected constructor and added support for version 51 class files in dx. So, there is finally some progress going on.
Edit (April 2014):
With the release of SDK 19 it is no longer necessary to patch android.jar with the additional APIs.
The best method to use try-with-resources in Android Studio for an app that targets 4.0.3 and above (minSdkVersion=15) is add the following compileOptions to your build.gradle:
android {
compileSdkVersion 19
buildToolsVersion '19.0.3'
defaultConfig {
minSdkVersion 15
targetSdkVersion 19
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}
Android Studio will complain that try-with-resources can't be used with this API level, but my experience is that it can. The project will build and run without issue on devices with 4.0.3 and above. I've experienced no issues with this, with an app that has been installed into 500k+ devices.
To ignore this warning, add the following to your lint.xml:
<issue id="NewApi">
<ignore regexp="Try-with-resources requires API level 19"/>
</issue>
It seems that getting this to work with pure ant is a bit of a kludge.
But it worked for me: http://www.informit.com/articles/article.aspx?p=1966024
In order to use Java 7 features in code build by Android's ant based build system, simply put the following in your custom_rules.xml in your projects root directory:
custom_rules.xml:
<project name="custom_android_rules">
<property name="java.target" value="1.7" />
<property name="java.source" value="1.7" />
</project>
Some people might be interested in this git project I've found, that seems to allow to run Java 7 on android.
https://github.com/yareally/Java7-on-Android
However too much of a risk if I add this in the current project I work on. So I'll wait until Google to officially support Java 7.
I have a 3rd party JAR file that is compiled using Java 1.4. Is there a tool that can make the jar file compatible with Java 1.6? (Something like 'retrotranslator' but what does the reverse of it).
I tried decompiling the class files and re compile them in 1.6 but it fails.
Here is the issue:
My project uses 'rsadapter.jar' for was 5.1 and I had my project setup in Eclipse 2.0 + JDK 1.4 and it used to work fine. Now, I have migrated to Java 1.6 and Eclipse Ganymede (as per the requirements) and the same project (exactly same setup) started complaining about the missing class files in the 'rsadapter.jar'. I put the JAR in classpath explicitly too but it still could not load the classes. Then I changed the Java Compiler version to 1.4 and it started working.
Regards,
- Ashish
Classes compiled by JDK 1.4 should be usable in a Java 6 runtime as-is. If you have actually encountered a problem, please describe it.
Update: I can only reproduce this with types in the "default" package (that is, not in a package). Are the classes you are trying to use in the default package? Also, this happens to me regardless of the JDK version used to compile.
Update: Okay, after a little research, I realized that you can never reference a type in the unnamed package from a named package. Makes sense, but definitely not what you are running into.
I can compile code under JDK 1.4.2_19 and utilize it just fine in a Java 6 Eclipse project. I think that this problem is something specific to your environment. In this situation, I would backup Eclipse and recreate everything (JDK installation, workspace, projects) from scratch, to see if I could clear it up.
I had another issue with some legacy code written in Java 1.4.x: the authors loved enumerations and loved to name the corresponding variables 'enum'. They even used it for package names. And this prevents from compiling the code under Java 1.5 (or higher) quite successfully.
Changing that automatically is quite an issue.
May be you have defined Eclipse to throw compiler errors on use of deprecated methods or classes?