6

これはばかげた質問だと思いますが.. 同じ Java ソース ファイルがあり、アプリを構築するクライアントに応じて異なるバージョンの Java API (jar ファイル) を使用したいと考えています。

API の新しいバージョンには、Java ソースで参照する setAAA() および setBBB() メソッドがあります。

if (...) {
  api.setAAA(a);
  api.setBBB(b);
} 

古い API でコンパイルされた場合、古い API にこれらのセッターがない場合、このコードは失敗します。新しい API を使用している場合、このコードを条件付けしてセッター行のみをコンパイルする方法はありますか?

ありがとう。

4

8 に答える 8

5

最も安全な方法は、サポートする必要がある最も低いバージョンにフォールバックすることです。これは、すべてのバージョンが下位互換性があることを前提としていますが、必ずしもそうとは限りません。

その解決策が適切でないか望ましくない場合は、依存性注入に戻ります。Spring フレームワークは、最も一般的で一般的な DI フレームワークですが、決して唯一のものではありません。ギースはもう一つ。このための完全なフレームワークを追加することが望ましくない場合は、独自のものを作成することもできます。

しかし、Spring を使用せずに行う Java アプリケーション、特に Web/J2EE アプリケーションを思い描くのに問題があります。それはあまりにも便利です。

関連する jar に 4 つのバージョンがあるとします。API はその間に 2 回変更されたため、3 つの異なる API バージョンがあります。その jar の使用を、これらすべてのバージョンで一貫した独自の API に抽象化し、その 3 つの実装 (異なる API バージョンごとに 1 つ) を作成する必要があります。

Spring では、すべての Bean とそれらが他の Bean にどのように注入されるかを定義するアプリケーション コンテキストを作成します。ビルド プロセスの一部としてアプリケーション コンテキストを選択またはビルドできない理由はありません。多くの場合、これにはプロパティが使用されますが、この方法でアプリケーション コンテキストの一部を含めることもできます。

ここで重要な点は、API が異なっていても、コードに関する限り、それらの違いを抽象化する必要があるということです。そうしないと、トラブルを求めているだけで、面倒になります。

于 2009-04-06T22:37:47.277 に答える
2

Java は (C++ とは異なり) この条件付きコンパイルを意図したものではありませんでした。

API のバージョンを返す関数を手動で処理し始めることもできますが、特定のバージョンに適合するクラスファイルができますが、互換性がない可能性はありません。

私は以前にこの状況に遭遇したことがあります (たとえば、異なるバージョンの Eclipse を使用している) が、きれいではありません。私が最終的にやったのは、APIごとに1つずつ、2つの異なる実装を持つインターフェースを持ち、それぞれを別のプロジェクト(私の場合はプラグイン)に入れ、ファクトリーまたはインジェクションでそれらをロードしようとしたことです。できるだけ隔離してください。

于 2009-04-06T22:39:15.010 に答える
1

また、顧客固有 (バージョン固有) のコードを保持するバージョン管理システムの別のブランチを保持することもできます。

于 2009-04-06T22:38:07.277 に答える
1

私が過去に行ったことは次のとおりです。ライブラリのバージョン依存の側面と相互作用する最小限の量のコードをできるだけきれいに記述します。ライブラリのバージョンごとに、このコードのバージョンを用意してください。それらすべてに同じインターフェースを実装させます。アプリケーションの大部分は、最新のライブラリに適したバージョンを動的にロードしようとする必要があります (Class.forNameおそらく構築のための反映が少しあります)。それが失敗した場合は、古いライブラリの静的にリンクされたバージョンにフォールバックします。

ソースパスとクラスパスを適切に使用することで、コア コードが新しいライブラリを使用しないように調整できます。

于 2009-04-06T22:46:38.113 に答える
0

Java イントロスペクションを使用できます。パッケージを見てください:

java.lang.reflect

メソッドというクラスがあります。次を使用して、クラスのすべてのパブリック メソッドを取得できます。

Method[] methodList = obj.getClass().getMethods();

これは API であるため、セッターは公開されます。次に、配列 methodList を実行して、セッターと同じ名前のメソッドを確認できます。それらを見つけたら、それらを使用してください。それ以外の場合は、これが以前のバージョンであることがわかります。

また、よく開発された API のほとんどには、JAR ファイルの現在のバージョンの値を返す関数があります。

例えば ​​:

String currentVersion = api.SomeClass.version() ;

使用している API にそのような関数があるかどうかを確認してみてください。これは簡単です。

于 2009-04-06T22:35:26.937 に答える
0

Java 1.2 からすべてのバージョンの Java で実行する必要があるコードがあるため、これと同じニーズがありましたが、新しい API が利用可能であればそれを利用する必要があるコードもあります。

リフレクションを使用してメソッド オブジェクトを取得し、それらを動的に呼び出すさまざまな順列の後、一般的に、ラッパー スタイルのアプローチが最善であることに落ち着きました (ただし、状況によっては、リフレクトされたメソッドを静的として格納し、それを呼び出す方が良い場合もありますが、場合によって異なります)。 .

以下は、特定の新しい API を公開する「システム ユーティリティ」クラスの例です。この例ではシングルトンを使用していますが、基礎となる API がそれを必要とする場合、複数のオブジェクトを簡単にインスタンス化できます。

次の 2 つのクラスがあります。

  • システムユーティリティ
  • SysUtil_J5

後者は、ランタイム JVM が Java 5 以降の場合に使用されるものです。それ以外の場合、コントラクトで互換性のあるフォールバック メソッドが、Java 4 以前の API のみを使用する SysUtil のデフォルトの実装から使用されます。各クラスは特定のバージョンのコンパイラでコンパイルされるため、Java 4 クラスで Java 5+ API が誤って使用されることはありません。

SysUtil (Java 4 コンパイラでコンパイル)

import java.io.*;
import java.util.*;

/**
 * Masks direct use of select system methods to allow transparent use of facilities only
 * available in Java 5+ JVM.
 *
 * Threading Design : [ ] Single Threaded  [x] Threadsafe  [ ] Immutable  [ ] Isolated
 */

public class SysUtil
extends Object
{

/** Package protected to allow subclass SysUtil_J5 to invoke it. */
SysUtil() {
    super();
    }

// *****************************************************************************
// INSTANCE METHODS - SUBCLASS OVERRIDE REQUIRED
// *****************************************************************************

/** Package protected to allow subclass SysUtil_J5 to override it. */
int availableProcessors() {
    return 1;
    }

/** Package protected to allow subclass SysUtil_J5 to override it. */
long milliTime() {
    return System.currentTimeMillis();
    }

/** Package protected to allow subclass SysUtil_J5 to override it. */
long nanoTime() {
    return (System.currentTimeMillis()*1000000L);
    }

// *****************************************************************************
// STATIC PROPERTIES
// *****************************************************************************

static private final SysUtil            INSTANCE;
static {
    SysUtil                             instance=null;

    try                  { instance=(SysUtil)Class.forName("SysUtil_J5").newInstance(); } // can't use new SysUtil_J5() - compiler reports "class file has wrong version 49.0, should be 47.0"
    catch(Throwable thr) { instance=new SysUtil();                                                                    }
    INSTANCE=instance;
    }

// *****************************************************************************
// STATIC METHODS
// *****************************************************************************

/**
 * Returns the number of processors available to the Java virtual machine.
 * <p>
 * This value may change during a particular invocation of the virtual machine. Applications that are sensitive to the
 * number of available processors should therefore occasionally poll this property and adjust their resource usage
 * appropriately.
 */
static public int getAvailableProcessors() {
    return INSTANCE.availableProcessors();
    }

/**
 * Returns the current time in milliseconds.
 * <p>
 * Note that while the unit of time of the return value is a millisecond, the granularity of the value depends on the
 * underlying operating system and may be larger. For example, many operating systems measure time in units of tens of
 * milliseconds.
 * <p>
 * See the description of the class Date for a discussion of slight discrepancies that may arise between "computer time"
 * and coordinated universal time (UTC).
 * <p>
 * @return         The difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
 */
static public long getMilliTime() {
    return INSTANCE.milliTime();
    }

/**
 * Returns the current value of the most precise available system timer, in nanoseconds.
 * <p>
 * This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock
 * time. The value returned represents nanoseconds since some fixed but arbitrary time (perhaps in the future, so values
 * may be negative). This method provides nanosecond precision, but not necessarily nanosecond accuracy. No guarantees
 * are made about how frequently values change. Differences in successive calls that span greater than approximately 292
 * years (263 nanoseconds) will not accurately compute elapsed time due to numerical overflow.
 * <p>
 * For example, to measure how long some code takes to execute:
 * <p><pre>
 *    long startTime = SysUtil.getNanoTime();
 *    // ... the code being measured ...
 *    long estimatedTime = SysUtil.getNanoTime() - startTime;
 * </pre>
 * <p>
 * @return          The current value of the system timer, in nanoseconds.
 */
static public long getNanoTime() {
    return INSTANCE.nanoTime();
    }

} // END PUBLIC CLASS

SysUtil_J5 (Java 5 コンパイラでコンパイル)

import java.util.*;

class SysUtil_J5
extends SysUtil
{

private final Runtime                   runtime;

SysUtil_J5() {
    super();

    runtime=Runtime.getRuntime();
    }

// *****************************************************************************
// INSTANCE METHODS
// *****************************************************************************

int availableProcessors() {
    return runtime.availableProcessors();
    }

long milliTime() {
    return System.currentTimeMillis();
    }

long nanoTime() {
    return System.nanoTime();
    }

} // END PUBLIC CLASS
于 2009-04-07T04:00:01.620 に答える
-1

あなたは試すことができます

  • リフレクションベースの呼び出しまたはコード生成または古い前処理技術または

  • 変化するものをカプセル化するための戦略パターン。

class ThirdPartyApi {
     void foo(){}  // available in all versions
     void bar(){}  // available only in new version
}

ThirdPartyApiV1 extends ThirdPartyApi {
     void foo() {
        thirdpartyV1Object.foo();
     }
}

ThirdPartyApiV2 extends ThirdPartyApi {
     void foo() {
        thirdpartyV2Object.foo();
     }
     void bar() {
        thirdpartyV2Object.bar();
     }
}

DependencyInjectionを使用して、正しいバージョンのThridPartyApi実装を注入します。それ以外の場合は、ThirdPartyApiFactoryを使用して、構成またはシステムプロパティ値に基づいて適切なインスタンスを作成します。

于 2009-04-06T23:22:29.103 に答える