How can I build a Java project with Java 6 versus Java 1.4 libraries?

I have a project that was originally written for Java 1.4, but I only have Java 6 on my Mac, and I cannot install Java 1.4.

I usually use this line to compile:

javac -source=1.4 -target=1.4 MyClass.java 

However, MyClass.java implements the MyClass.java interface, which added several new methods in Java 6, so I get compilation errors such as:

 MyClass is not abstract and does not override abstract method updateNClob(java.lang.String,java.io.Reader) in java.sql.ResultSet 

I can't just implement the missing methods, because many use generics, which are not available in Java 1.4.

It seems that the solution would be to get and compile Java 1.4 JAR files. So, I have a few questions:

  • Is there a better way?
  • How do I point to my Java 1.6 javac that I would like to use 1.4 JAR instead of Java 6 JAR?
  • Will this work, and if so, will the project work in Java 1.4 as well as in Java 6?
  • How to do it in Maven?

Thanks!

+6
source share
3 answers

Your situation seems rather far-fetched. I will try to simplify the matter. First, I'm going to ignore your question about Maven.

So let me first point out some facts:

-source=1.4 means: Dear compiler, please accept only language constructs --- not library functions --- that were available with javac JDK 1.4.

-target=1.4 means: Dear compiler, write class files in a binary file format that is compatible with JRE 1.4.

I believe that you are interested in compatibility with JDK 1.4, that is, you want the class files created in your setup to be able to load JDK 1.4. Is it correct?

Do you also want to maintain source compatibility? So you want to let others compile their code on JDK 1.4?

If the answer to the last question is yes, I will try to install JDK 1.4 on OS X. It supports several installed JDKs. Therefore, I am sure that this is possible. If this is not a use case:

 -source=1.4 -target=1.4 -bootclasspath=[path/to/1.4.jar] 

Note. Do not use -Xbootclasspath. This changes the jvm boot path executing javac.

If the answer to the above question is no. You can control -source=1.4 , allowing you to use general and other Java 5 enhancements in your code. But you still need to ensure binary compatibility using:

 -target=1.4 -bootclasspath=[path/to/1.4.jar] 

Another option is to use Retroweaver .

After reading your question again, I would like to add that you need to get the JDK 1.4 version of the jdbc class files. Otherwise, you will encounter compiler errors that you indicated in your question.

+4
source

If you are not a JDBC provider, it is not practical to implement interfaces such as this.

Consider using a proxy to ensure compatibility in JVM versions.


The transition to the proxy is as follows. Consider this implementation of ResultSet :

 public class ResultSetFoo implements ResultSet { public String getString(int columnIndex) throws SQLException { return "foobar"; } // other Java 1.4 methods 

This will be changed, so no classes implement ResultSet :

 public class ResultBar { public String getString(int columnIndex) throws SQLException { return "foobar"; } // other method signatures matching the 1.4 ResultSet, as before 

Then you will need to build a method mapping between the two types at runtime (primitive form of duck input :)

  private static final Map RESULT_SET_DUCK = initResultSet(); private static Map initResultSet() { Map map = new HashMap(); Method[] methods = ResultSet.class.getMethods(); for (int i = 0; i < methods.length; i++) { try { Method match = ResultBar.class.getMethod(methods[i].getName(), methods[i].getParameterTypes()); map.put(methods[i], match); } catch (SecurityException e) { throw new IllegalStateException(e); } catch (NoSuchMethodException e) { // OK; not supported in 1.4 } } return map; } 

This allows you to call the ResultBar type by proxy:

  /** Create a java.sql.ResultSet proxy */ public static ResultSet proxy(final ResultBar duck) { class Handler implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Method proxiedMethod = (Method) RESULT_SET_DUCK.get(method); if (proxiedMethod == null) { throw new UnsupportedOperationException("TODO: method detail"); } else { return invoke(proxiedMethod, duck, args); } } private Object invoke(Method m, Object target, Object[] args) throws Throwable { try { return m.invoke(target, args); } catch (InvocationTargetException e) { throw e.getCause(); } } } return (ResultSet) Proxy.newProxyInstance(null, RSET, new Handler()); } 

Such implementations should allow code compiled in one JVM to be used in future JVMs, even if new methods are added. Existing method signatures are unlikely to change, because one thing is that database manufacturers do some work; something else to change all API users.

You may need to change the way you instantiate the class. You can no longer use the constructor directly:

 ResultSet nonPortable = new ResultSetFoo(); //becomes... ResultSet portable = proxy(new ResultBar()); 

If you are already using factory / builder / etc. this bit pattern is simple.

Although the reflection in cheap JVMs is relatively cheap, it is less in older versions. this may adversely affect performance.

+4
source

How can I point to Java Java javac that I would like to use 1.4 JAR instead of Java 6 JAR?

In Win. and * nix this will be by specifying the bootclasspath option. See javac: Cross-compilation options for more details.

+2
source

Source: https://habr.com/ru/post/896638/


All Articles