12

私はJavaSwingアプリケーションを保守しています。

Java 5(Appleマシン用)との下位互換性のために、2つのコードベースを維持しています。1つはJava 6の機能を使用し、もう1つはそれらの機能を使用していません。

Java 6の機能を使用する3〜4つのクラスを除いて、コードはほとんど同じです。

1つのコードベースを維持したいだけです。コンパイル中に、Java 5コンパイラにコードの一部を「無視」させる方法はありますか?

Javaコンパイラのバージョンによっては、コードの一部を単にコメント化/コメント解除したくありません。

4

14 に答える 14

6

カスタムクラスローダーと動的にコメントされたコードの使用に関する提案は、新しい牧草地にシャッフルした後、プロジェクトを拾った可哀想な魂のメンテナンスと正気の維持に関しては、少し信じられない.

解決策は簡単です。影響を受けるクラスを 2 つの別個の独立したプロジェクトに引き出します。パッケージ名が同じであることを確認し、メイン プロジェクトで使用できるように jar にコンパイルします。パッケージ名とメソッド シグネチャを同じにしておく場合、問題はありません。必要なバージョンの jar をデプロイ スクリプトにドロップするだけです。別々のビルド スクリプトを実行するか、同じスクリプト内に別々のターゲットがあると仮定します。ant と maven はどちらも、条件付きでファイルを取得してコピーすることを簡単に処理できます。

于 2008-09-16T19:58:13.073 に答える
4

ここでの最善のアプローチは、おそらくビルドスクリプトを使用することだと思います。すべてのコードを1つの場所に置くことができ、含めるファイルと含めないファイルを選択することで、コンパイルするコードのバージョンを選択できます。ファイルごとよりもきめ細かい制御が必要な場合、これは役に立たない可能性があることに注意してください。

于 2008-09-16T16:23:32.310 に答える
4

クラスが同様の機能を持ち、実装に 1.5 と 6.0 の違いがあると仮定すると、それらを 1 つのクラスにマージできます。次に、ソースを編集してコメント/コメント解除しなくても、コンパイラが常に行う最適化に頼ることができます。if 式が常に false の場合、if ステートメントのコードはコンパイルに含まれません。

クラスの 1 つで静的変数を作成して、実行するバージョンを決定できます。

public static final boolean COMPILED_IN_JAVA_6 = false;

次に、影響を受けるクラスにその静的変数をチェックさせ、コードのさまざまなセクションを単純な if ステートメントに入れます。

if (VersionUtil.COMPILED_IN_JAVA_6) {
  // Java 6 stuff goes here
} else {
  // Java 1.5 stuff goes here
}

次に、他のバージョンをコンパイルする場合は、その 1 つの変数を変更して再コンパイルするだけです。Java ファイルが大きくなる可能性がありますが、コードが統合され、コードの重複がなくなります。エディターは、到達不能なコードなどについて不平を言うかもしれませんが、コンパイラーはそれを喜んで無視する必要があります。

于 2008-09-16T17:00:44.123 に答える
3

条件付きコンパイルが実際には必要なく、条件付きクラスローディングだけが必要になるように、コードをリファクタリングすることができます。このようなもの:

public interface Opener{

public void open(File f);

 public static class Util{
        public Opener getOpener(){
          if(System.getProperty("java.version").beginsWith("1.5")){
           return new Java5Opener();
          }
          try{ 
            return new Java6Opener();
           }catch(Throwable t){
            return new Java5Opener();
           }
        }
 }

}

バージョン固有のコードがいくつあるかによっては、これは大変な作業になる可能性があります。

于 2008-09-16T16:42:31.313 に答える
2

JDK 5 でビルドする「マスター」ソース ルートを 1 つ保持します。JDK 6 以降でビルドする必要がある 2 つ目の並列ソース ルートを追加します。(重複があってはなりません。つまり、両方に存在するクラスはありません。) インターフェイスを使用して、2 つの間のエントリ ポイントを定義し、わずかなリフレクションを行います。

例えば:

---%<--- main/RandomClass.java
// ...
if (...is JDK 6+...) {
    try {
        JDK6Interface i = (JDK6Interface)
            Class.forName("JDK6Impl").newInstance();
        i.browseDesktop(...);
    } catch (Exception x) {
        // fall back...
    }
}
---%<--- main/JDK6Interface.java
public interface JDK6Interface {
    void browseDesktop(URI uri);
}
---%<--- jdk6/JDK6Impl.java
public class JDK6Impl implements JDK6Interface {
    public void browseDesktop(URI uri) {
        java.awt.Desktop.getDesktop().browse(uri);
    }
}
---%<---

異なる JDK などを使用して、IDE でこれらを個別のプロジェクトとして構成できます。要点は、メイン ルートを個別にコンパイルでき、どのルートで何を使用できるかが非常に明確であるということです。単一のルートを別々に使用すると、JDK 6 の使用状況が間違ったファイルに誤って「リーク」する可能性が非常に高くなります。

このように Class.forName を使用するのではなく、何らかのサービス登録システムを使用することもできます - java.util.ServiceLoader (main が JDK 6 を使用でき、オプションで JDK 7 のサポートが必要な場合!)、NetBeans Lookup、Spring など。等

同じ手法を使用して、新しい JDK ではなく、オプションのライブラリのサポートを作成できます。

于 2008-09-16T16:37:57.830 に答える
1

これは、すべての Java 純粋主義者をうんざりさせます (これは楽しいですね)。makefile、rakefile、またはビルドを制御するものは何でも、cpp を実行してコンパイラに供給する一時ファイルを作成する必要があります。アリにこれをさせることができるかどうかはわかりません。

stackoverflow がすべての答えの場所になるように見えますが、 Java の知恵を求めてhttp://www.javaranch.comに目を向ける人は誰もいないでしょう。私は、この問題がそこで取り扱われたと思います。

于 2008-09-16T16:30:42.823 に答える
1

そうではありませんが、回避策があります。http://forums.sun.com/thread.jspa?threadID=154106&messageID=447625を参照して ください。

とはいえ、Java 5 用と Java 6 用に少なくとも 1 つのファイル バージョンを保持し、必要に応じてビルドまたは make を介してそれらを含める必要があります。すべてを 1 つの大きなファイルにまとめて、5 のコンパイラに理解できないものを無視させようとするのは、良い解決策ではありません。

HTH

-- にっき --

于 2008-09-16T16:26:49.057 に答える
1

すべてのコンパイルを Java6 だけで実行してから、 System.getProperty("java.version") を使用して Java5 または Java6 コード パスを条件付きで実行できます。

クラスに Java6 のみのコードを含めることができ、Java6 のみのコード パスが実行されない限り、クラスは Java5 上で正常に動作します。

これは、古代の MSJVM から真新しい Java Plug-in JVM までずっと実行されるアプレットを作成するために使用されるトリックです。

于 2008-09-16T20:54:20.677 に答える
1

使用する Java 6 機能によって異なります。行ソーターを JTables に追加するなどの簡単なことについては、実行時に実際にテストできます。

private static final double javaVersion =
         Double.parseDouble(System.getProperty("java.version").substring(0, 3));
private static final boolean supportsRowSorter =
         (javaVersion >= 1.6);

//...

if (supportsRowSorter) {
    myTable.setAutoCreateRowSorter(true);
} else {
    // not supported
}

このコードは Java 6 でコンパイルする必要がありますが、どのバージョンでも実行できます (新しいクラスは参照されません)。

編集: より正確に言うと、1.3 以降のどのバージョンでも動作します (このページによると)。

于 2008-09-16T19:39:52.650 に答える
0

Java にはプリコンパイラがありません。したがって、C のように #ifdef を実行する方法はありません。ビルド スクリプトが最適な方法です。

于 2008-09-16T16:32:20.527 に答える
0

条件付きコンパイルを取得できますが、あまりうまくいきません.javacは到達できないコードを無視します. したがって、コードを適切に構成すると、コンパイラにコードの一部を無視させることができます。これを適切に使用するには、javac に正しい引数を渡して、到達不能なコードがエラーとして報告されないようにし、コンパイルを拒否する必要もあります :-)

于 2008-09-16T16:35:22.517 に答える
0

上記の public static final ソリューションには、著者が言及していない追加の利点があります。私が理解しているように、コンパイラはコンパイル時にそれを認識し、その final 変数を参照する if ステートメント内のコードをコンパイルします。

だから私はそれがあなたが探していた正確な解決策だと思います.

于 2008-09-16T17:06:30.973 に答える
0

簡単な解決策は次のとおりです。

  • 通常のクラスパスの外に分岐クラスを配置します。
  • シンプルなカスタム クラスローダーを作成し、デフォルトとして main にインストールします。
  • 5/6 以外のすべてのクラスについて、cassloader はその親 (通常のシステム クラスローダー) に任せることができます。
  • 5/6 のもの (親が見つけられない唯一のものであるべきです) については、「os.name」プロパティを介してどちらを使用するか、独自のものを使用するかを決定できます。
于 2008-09-16T17:07:55.433 に答える
0

リフレクション API を使用できます。すべての 1.5 コードを 1 つのクラスに配置し、1.6 API を別のクラスに配置します。Ant スクリプトで、1.6 クラスをコンパイルしない 1.5 用と 1.5 用のクラスをコンパイルしない 1.6 用の 2 つのターゲットを作成します。コードで Java のバージョンを確認し、リフレクションを使用して適切なクラスをロードします。これにより、javac は機能の欠落について文句を言いません。これは、Windows で MRJ (Mac Runtime for Java) アプリケーションをコンパイルする方法です。

于 2008-09-28T15:00:03.140 に答える