3

Java の下位互換性の維持に関する興味深い記事がここにあります。ラッパー クラスのセクションでは、ラッパー クラスが何を達成するのか実際には理解できません。次の のコードではMyAppWrapNewClass.checkAvailable()を に置き換えることができますClass.forName("NewClass")

static {
    try {
        WrapNewClass.checkAvailable();
        mNewClassAvailable = true;
    } catch (Throwable ex) {
        mNewClassAvailable = false;
    }
}

が利用できない場合を考慮してくださいNewClass。ラッパーを使用するコード (以下を参照) では、存在しないクラスを存在するクラスに置き換えただけですが、存在しないクラスを使用しているためコンパイルできません。

public void diddle() {
    if (mNewClassAvailable) {
        WrapNewClass.setGlobalDiv(4);
        WrapNewClass wnc = new WrapNewClass(40);
        System.out.println("newer API is available - " + wnc.doStuff(10));
    }else {
        System.out.println("newer API not available");
    }
}

なぜこれが違いを生むのか、誰か説明できますか?Javaがコードをコンパイルする方法と関係があると思いますが、それについてはあまり知りません。

4

3 に答える 3

4

これのポイントは、実行時に利用できない可能性のあるクラスに対してコンパイルされるコードを持つことです。WrapNewClass は、javac のクラスパスに存在する必要があります。そうしないと、コンパイルできません。ただし、実行時にクラスパスに存在しない場合があります。

mNewClassAvailable が false の場合、引用するコードは WrapNewClass への参照を回避します。したがって、「新しい API は利用できません」というメッセージが出力されるだけです。

しかし、私は感動したとは言えません。一般に、例外をキャッチしようとする代わりに、java.lang.reflect を使用してこの種の処理を行うのを見てきました。これにより、コンパイルされた場合でも、クラスがどこにも見えなくなります。

于 2010-05-11T00:53:16.187 に答える
3

私は長い間、JSE で 1.1 以降のすべての JVM をサポートする必要があり、この種のラッピング手法を使用して、オプションの API (つまり、アプリケーションの動作を改善するが必須ではない API) を互換的にサポートしてきました。

私が使用する 2 つの手法は、参照した記事に (不十分に?) 記載されているようです。それについてさらにコメントするのではなく、私がこれをどのように行ったかの実際の例を提供します.

最も簡単 - 静的ラッパー メソッド

必要: API が利用可能であればそれを呼び出すか、そうでなければ何もしないこと。これは、任意の JVM バージョンに対してコンパイルできます。

Methodまず、次のように、反映されたメソッドを持つ静的を設定します。

static private final java.lang.reflect.Method SET_ACCELERATION_PRIORITY;
static {
    java.lang.reflect.Method mth=null;
    try { mth=java.awt.Image.class.getMethod("setAccelerationPriority",new Class[]{Float.TYPE}); } catch(Throwable thr) { mth=null; }
    SET_ACCELERATION_PRIORITY=mth;
    }

直接呼び出しを使用する代わりに、反映されたメソッドをラップします。

static public void setImageAcceleration(Image img, int accpty) {
    if(accpty>0 && SET_ACCELERATION_PRIORITY!=null) {
        try { SET_ACCELERATION_PRIORITY.invoke(img,new Object[]{new Float(accpty)}); } 
        catch(Throwable thr) { throw new RuntimeException(thr); } // exception will never happen, but don't swallow - that's bad practice
        }
    }

Harder - 静的ラッパー クラス

必要: API が使用可能な場合はその API を呼び出すか、それ以外の場合は、同等ではあるが機能が低下した古い API を呼び出します。これは、新しい JVM バージョンに対してコンパイルする必要があります。

最初に静的ラッパー クラスを設定します。これは静的なシングルトン ラッパーである場合もあれば、すべてのインスタンス作成をラップする必要がある場合もあります。次の例では、静的シングルトンを使用しています。

package xxx;

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();
    }

/** 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 milliTick() {
    return System.currentTimeMillis();
    }

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

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

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

    try                  { instance=(SysUtil)Class.forName("xxx.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 value of the most precise available system timer, in milliseconds.
 * <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 milliseconds since some fixed but arbitrary time (perhaps in the future, so
 * values may be negative). This method provides millisecond precision, but not necessarily millisecond accuracy. No
 * guarantees are made about how frequently values change. Differences in successive calls that span greater than
 * approximately 292,000 years 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.getNanoTick();
 *    // ... the code being measured ...
 *    long estimatedTime = SysUtil.getNanoTick() - startTime;
 * </pre>
 * <p>
 * @return          The current value of the system timer, in milliseconds.
 */
static public long getMilliTick() {
    return INSTANCE.milliTick();
    }

/**
 * 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 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.getNanoTick();
 *    // ... the code being measured ...
 *    long estimatedTime = SysUtil.getNanoTick() - startTime;
 * </pre>
 * <p>
 * @return          The current value of the system timer, in nanoseconds.
 */
static public long getNanoTick() {
    return INSTANCE.nanoTick();
    }

} // END PUBLIC CLASS

利用可能になったときに新しい機能を提供するサブクラスを作成します。

package xxx;

import java.util.*;

class SysUtil_J5
extends SysUtil
{

private final Runtime                   runtime;

SysUtil_J5() {
    super();

    runtime=Runtime.getRuntime();
    }

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

long milliTick() {
    return (System.nanoTime()/1000000);
    }

long nanoTick() {
    return System.nanoTime();
    }
} // END PUBLIC CLASS
于 2010-05-11T02:32:14.460 に答える
2

私は春とリッチフェイスでこの動作を見てきました。たとえば、Spring は次のことを行います。

  • コンパイル時に JSF に依存する
  • private staticJSF クラスを参照する内部クラスを宣言します。
  • Class.forName(..)JSF クラスを試行/キャッチします
  • 例外がスローされない場合、内部クラスが参照されます (そして、Spring コンテキストは Faces コンテキストを介して取得されます)
  • 例外がスローされた場合、Spring コンテキストは別のソース (サーブレット コンテキスト) から取得されます。

内部クラスは参照されるまでロードされないことに注意してください。そのため、満たされていない依存関係があっても問題ありません。

(春期講習はorg.springframework.web.context.request.RequestContextHolder)

于 2010-05-11T05:27:53.277 に答える