Классы Wrapper для обратной совместимости в Java - PullRequest
3 голосов
/ 11 мая 2010

Здесь есть интересная статья о поддержке обратной совместимости для Java. В разделе класса обертки я не могу понять, что выполняет класс обертки. В следующем коде из MyApp, WrapNewClass.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 компилирует код - о чем я немного знаю.

Ответы [ 3 ]

4 голосов
/ 11 мая 2010

Смысл этого в том, чтобы иметь код, скомпилированный для некоторого класса, который может быть недоступен в время выполнения . WrapNewClass должен присутствовать в classpath javac, иначе эта вещь не может быть скомпилирована. Тем не менее, он может отсутствовать в classpath в время выполнения .

Код, который вы цитируете, избегает ссылок на WrapNewClass, если mNewClassAvailable имеет значение false. Таким образом, он просто напечатает сообщение «новый API недоступен».

Однако я не могу сказать, что впечатлен. В общем, я видел подобные вещи, организованные с помощью java.lang.reflect, вместо того, чтобы пытаться поймать исключение. Это, между прочим, позволяет классу быть нигде невидимым даже при компиляции.

3 голосов
/ 11 мая 2010

Я давно нуждался в поддержке каждой JVM начиная с версии 1.1 в JSE, и я использовал этот вид методов обертывания для совместимой поддержки дополнительных API-интерфейсов, то есть API-интерфейсов, которые улучшают работу приложения, но не являются необходимыми для него.

Кажется, что две техники, которые я использую, (плохо?) Описаны в статье, на которую вы ссылались. Вместо того, чтобы комментировать дальше, я приведу реальные примеры того, как я это сделал.

Самый простой - метод статической обертки

Необходимость: вызывать 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
        }
    }

Сложнее - класс статической обертки

Необходимость: вызывать API, если он доступен, или иным образом вызывать более старый API для эквивалентной, но ухудшенной функциональности. Это должно быть скомпилировано с более новой версией JVM.

Сначала настройте статический класс-оболочку; это может быть статическая одноэлементная оболочка, или вам может понадобиться обернуть каждый экземпляр создания. В следующем примере используется статический синглтон:

<code>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;
 * 
*

* @return Текущее значение системного таймера в миллисекундах. * / static public long getMilliTick () { return INSTANCE.milliTick (); } / ** * Возвращает текущее значение наиболее точного доступного системного таймера в наносекундах. *

* Этот метод может использоваться только для измерения прошедшего времени и не связан с какими-либо другими понятиями системы или настенных часов * время Возвращаемое значение представляет наносекунды с некоторого фиксированного, но произвольного времени (возможно, в будущем, поэтому значения * может быть отрицательным). Этот метод обеспечивает точность наносекунды, но не обязательно точность наносекунды. Нет гарантий * сделаны о том, как часто значения меняются. Различия в последовательных вызовах, превышающие приблизительно 292 * годы не будут точно рассчитывать истекшее время из-за числового переполнения. *

* Например, чтобы измерить, сколько времени занимает выполнение кода: *

 *    long startTime = SysUtil.getNanoTick();
 *    // ... the code being measured ...
 *    long estimatedTime = SysUtil.getNanoTick() - startTime;
 * 
*

* @return Текущее значение системного таймера в наносекундах. * / static public long getNanoTick () { return INSTANCE.nanoTick (); } } // КОНЕЦ ОБЩЕСТВЕННОГО КЛАССА

и создайте подкласс для предоставления новых функциональных возможностей, если они доступны:

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
2 голосов
/ 11 мая 2010

Я видел это поведение весной и richfaces. Spring, например, делает следующее

  • имеет зависимость времени компиляции от JSF
  • объявляет private static внутренний класс, где он ссылается на классы JSF
  • пробовать / ловить Class.forName(..) класс JSF
  • если не сгенерировано исключение, на внутренний класс ссылаются (и контекст пружины получается через контекст граней)
  • если генерируется исключение, контекст весны получается из другого источника (контекст сервлета)

Обратите внимание, что внутренние классы не загружаются до тех пор, пока на них не ссылаются, поэтому нормально иметь зависимость, которая не встречается в них.

(Весенний класс org.springframework.web.context.request.RequestContextHolder)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...