アプリケーションの必要な動作環境をJava1.4以上としながら、1.5のAPIが使用可能ならば利用する方法

例えば、Java1.5からSystem.getenv()メソッドにより環境変数の一覧が取得できるようになりました。VerticalViewerでその機能を使いたいのですが、それを行うとJava1.4で使えなくなってしまいます。1.4をサポートしながら、1.5の機能を利用することはできるのでしょうか?目標は以下の通りです。

  • 1.5の環境の場合は、その機能が使えます。
  • 1.4の環境の場合は、その機能が使えないことを示すメッセージを表示します。

まず、テスト用プログラムとして環境変数の一覧を表示するプログラムを作ってみます。

import java.util.*;
class TestGetEnv {
    public static void main(String[] args) {
        Map map = System.getenv();
        for (Iterator ite=map.keySet().iterator(); ite.hasNext(); ) {
            Object key = ite.next();
            Object value = map.get(key);
            System.out.println(key+"="+value);
        }
    }
}

Java1.5で普通にコンパイルして、Java1.5で普通に実行すると普通に動きます。

C:\user\java\Test>java -cp . TestGetEnv
ANT_HOME=C:\Java\jakarta-ant-1.5.1
HOMEDRIVE=C:
TMP=C:\DOCUME~1\Asan\LOCALS~1\Temp
windir=C:\WINDOWS
VS_HOME=C:\Program Files\Microsoft Visual Studio
HOME=C:\cygwin\home
SystemDrive=C:
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
...

Java1.5でコンパイルしたものを、Java1.4で動かすと、classファイルのバージョンでエラーになってしまいます。

C:\user\java\Test>java -cp . TestGetEnv
Exception in thread "main" java.lang.UnsupportedClassVersionError: TestGetEnv (Unsupported major.minor version 49.0)
        at java.lang.ClassLoader.defineClass0(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:537)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:123)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:251)
        at java.net.URLClassLoader.access$100(URLClassLoader.java:55)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:194)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:187)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:289)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:274)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:235)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:302)

まず、これを避けるため、Java1.5で[-source 1.4]のコンパイルオプションを付けてコンパイルします。すると、メッセージが以下のように変わります。メソッドがないので期待通りの結果です。

C:\user\java\Test>java -cp . TestGetEnv
Exception in thread "main" java.lang.NoSuchMethodError: java.lang.System.getenv(
)Ljava/util/Map;
        at TestGetEnv.main(TestGetEnv.java:5)

では、このメソッドが存在する場合に処理を行い、存在しない場合にはメッセージを表示するようにしましょう。リフレクションを使います。

import java.util.*;

class TestGetEnv {
    void printEnv() {
        Map map = System.getenv();
        for (Iterator ite=map.keySet().iterator(); ite.hasNext(); ) {
            Object key = ite.next();
            Object value = map.get(key);
            System.out.println(key+"="+value);
        }
    }
    void tryPrintEnv() {
        try {
            System.class.getMethod("getenv", null);
            printEnv();
        } catch (NoSuchMethodException ex) {
            System.out.println("この機能は使えません。java.version="+System.getProperty("java.version"));
        }
    }
    public static void main(String[] args) {
        new TestGetEnv().tryPrintEnv();
    }
}

うまくいきました。コンパイルの仕方は、Java1.5の環境で、-source 1.4を指定してコンパイルします。
Java1.5の環境で実行させれば、環境変数の取得はできますし、Java1.4の環境で動かせば、メッセージを表示します。

C:\user\java\Test>java -cp . TestGetEnv
この機能は使えません。java.version=1.4.2_02

これで、古いバージョンにも対応しながら、新しいバージョンではその機能の恩恵が受けれるようにできます。