Как мне ссылаться на API Java 1.6, одновременно плавно снижая производительность по сравнению с Java 1.5?

StackOverflow https://stackoverflow.com/questions/1277270

  •  16-09-2019
  •  | 
  •  

Вопрос

Я хотел бы использовать java.text.Нормализатор класс из Java 1.6 для нормализации Unicode, но мой код должен работать на Java 1.5.

Я не против, если код, работающий в версии 1.5, не выполняет нормализацию, но я не хочу, чтобы это давало NoClassDefFoundErrorили ClassNotFoundExceptions, когда он работает.

Какой лучший способ добиться этого?

Это было полезно?

Решение

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

В случае java.text.Normalizer, это должно быть довольно просто, поскольку это всего лишь пара статических методов, и их легко вызвать через отражение.

Другие советы

public interface NfcNormalizer
{
  public String normalize(String str);
}

public class IdentityNfcNormalizer implements NfcNormalizer
{
  public String normalize(String str)
  {
    return str;
  }
}

public class JDK16NfcNormalizer implements NfcNormalizer
{
  public String normalize(String str)
  {
    return Normalizer.normalize(str, Normalizer.Form.NFC);
  }
}

В вашем клиентском коде:

NfcNormalizer normalizer;
try
{
  normalizer = Class.forName("JDK16NfcNormalizer").newInstance();
}
catch(Exception e)
{
  normalizer = new IdentityNfcNormalizer();
}

Я не возражаю, если код, работающий в версии 1.5, не выполняет нормализацию, но я не хочу, чтобы он выдавал NoClassDefFoundErrors или ClassNotFoundExceptions при запуске.

Если вы хотите избежать отражения, вы действительно можете поймать эти ошибки.

Таким образом, вы можете скомпилировать новые блестящие классы с помощью компилятора Java6, и он по-прежнему будет работать (как «ничего не делать, но и не давать сбоев») на Java5.

Вы также можете объединить два подхода и проверить, существует ли класс с помощью отражения, и продолжает ли он вызывать его нерефлексивным способом.Это то, что делает решение Эндрю.

Если вам также необходимо компилировать на Java5, то вам нужно полностью задуматься.

У меня была такая же потребность, поскольку у нас есть код, который должен работать на всех версиях Java, начиная с Java 1.2, но некоторый код должен использовать преимущества более новых API, если они доступны.

После различных перестановок с использованием отражения для получения объектов метода и их динамического вызова я в целом остановился на подходе к стилю оболочки как лучшему (хотя в некоторых обстоятельствах лучше просто сохранить отраженный метод как статический и вызвать его - это зависит от ситуации) .

Ниже приведен пример класса «Системная утилита», который предоставляет некоторые новые API для Java 5 при запуске более ранней версии — те же принципы справедливы для Java 6 в более ранних JVM.В этом примере используется Singleton, но можно легко создать экземпляры нескольких объектов, если это необходимо базовому API.

Есть два класса:

  • SysUtil
  • SysUtil_J5

Последний используется, если JVM времени выполнения — Java 5 или новее.В противном случае резервные методы, совместимые по контракту, используются из реализации по умолчанию в SysUtil, которая использует только API Java 4 или более ранних версий.Каждый класс компилируется с помощью компилятора конкретной версии, чтобы исключить случайное использование API Java 5+ в классе Java 4:

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

Проверить/использовать/изменить класс info.olteanu.utils.TextNormalizer в проекте Phramer (http://sourceforge.net/projects/phramer/ , www.phramer.org) — код лицензирован BSD.

Этот код можно скомпилировать в Java 5 и запустить как в Java 5, так и в Java 6 (или в будущих версиях Java).Кроме того, его можно скомпилировать в Java 6 и запустить в Java 5 (если он был скомпилирован с правильным «-target» для совместимости с байт-кодом) или Java 6 или любой другой будущей версии.

ИМХО, это полностью решает вашу проблему - вы можете компилировать на любой платформе Java 5+ и можете получить желаемую функциональность (нормализацию) на любой платформе Java 5+ (*)

(*) Решение SUN Java 5 для нормализации, скорее всего, не будет присутствовать во всех реализациях Java 5, поэтому в худшем случае вы получите исключение ClassNotFoundException при вызове метода getNormalizationStringFilter().

    String str = "éèà";
    try {
        Class c = Class.forName("java.text.Normalizer");
        Class f = Class.forName("java.text.Normalizer$Form");
        Field ff = f.getField("NFD");
        Method m = c.getDeclaredMethod("normalize", new Class[]{java.lang.CharSequence.class,f});
        temp = (String) m.invoke(null, new Object[]{str,ff.get(null)});
    } catch (Throwable e) {
        System.err.println("Unsupported Normalisation method (jvm <1.6)");
    }
    System.out.println(temp+" should produce [eea]");

Это старый вопрос, но все еще актуальный.Я обнаружил некоторые возможности, которые не упоминаются в ответах.

Обычно рекомендуется использовать отражение, как показано в некоторых других ответах здесь.Но если вы не хотите вносить беспорядок в свой код, вы можете использовать библиотека icu4j.Это содержит com.ibm.icu.text.Normalizer класс с normalize() метод, который выполняет ту же работу, что и java.text.Normalizer/sun.text.Normalizer.Библиотека Icu имеет (должна иметь) собственную реализацию Normalizer, чтобы вы могли поделиться своим проектом с библиотекой, и она должна быть независимой от Java.
Недостатком является то, что библиотека ICU довольно большая.

Если вы используете класс Normalizer только для удаления акцентов/диакритических знаков из строк, есть и другой способ.Вы можете использовать Библиотека языков Apache Commons (версия.3) который содержит StringUtils с методом stripAccents():

String noAccentsString = org.apache.commons.lang3.StringUtils.stripAccents(s);

Библиотека Lang3, вероятно, использует отражение для вызова соответствующего нормализатора в соответствии с версией Java.Таким образом, преимущество в том, что в вашем коде нет беспорядка с отражениями.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top