Como faço para se referir a Java 1.6 APIs, degradando graciosamente contra Java 1.5?

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

  •  16-09-2019
  •  | 
  •  

Pergunta

Eu gostaria de usar o java.text .Normalizer classe de Java 1.6 para fazer Unicode normalização, mas meu código tem que ser capaz de rodar em Java 1.5.

Eu não me importo se o código em execução no 1.5 não faz normalização, mas eu não quero isso para dar NoClassDefFoundErrors ou ClassNotFoundExceptions quando ele é executado.

Qual é a melhor maneira de conseguir isso?

Foi útil?

Solução

A maneira usual de fazer isso é através da reflexão, ou seja, não se referem diretamente à classe em questão, mas chamá-lo de forma programática. Isso permite que você capturar exceções graciosamente se o código em questão não existe, e ignorá-la, ou tentar outra coisa. Reflexão joga ClassNotFoundException, que é uma exceção normal, bom, em vez de NoClassDefFoundError, que é um pouco assustador.

No caso de java.text.Normalizer, este deve ser muito fácil, já que é apenas um par de métodos estáticos, e fácil de invocar via reflexão.

Outras dicas

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

Em seu código de cliente:

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

Eu não me importo se o código em execução no 1.5 não faz normalização, mas eu não quero isso para dar NoClassDefFoundErrors ou ClassNotFoundExceptions quando ele é executado.

Se você quiser evitar reflexo, você pode realmente pegar esses erros.

Desta forma, você pode compilar contra as novas classes brilhantes com um compilador Java6, e ele vai trabalhar ainda (como em "não fazer nada, mas também não falhar") em Java5.

Você também pode combinar as duas abordagens, e verificar se existe a classe usando reflexão, e se não continuar a chamá-lo de uma forma não-reflexivo. Isto é o que a solução de Andrew está fazendo.

Se você também precisa compilação em Java5, então você precisa ir reflexão todo o caminho.

Eu tive essa mesma necessidade, uma vez que temos um código que precisa ser executado em todas as versões do Java de Java 1.2, mas algumas necessidades de código para tirar proveito da mais recente API é se eles estiverem disponíveis.

Depois de várias permutações usando reflexão para obter Método objetos e invocando-los dinamicamente, eu ter resolvido em uma abordagem de estilo invólucro como o melhor, em geral (embora em algumas circunstâncias, apenas armazenar o método refletido como um estático e invocando é melhor - depende).

A seguir é um exemplo "System Utility" classe que expõe certa mais recente API é para Java 5 ao executar uma versão anterior - os mesmos princípios segurar para Java 6 em JVMs anteriores. Este exemplo usa um Singleton, mas poderia facilmente instanciar vários objetos se a API subjacente precisava disso.

Existem duas classes:

  • SysUtil
  • SysUtil_J5

O último é o utilizado se o tempo de execução JVM é Java 5 ou posterior. métodos de outro modo fallback que são compatíveis no contrato são usados ??a partir da implementação padrão em SysUtil que utiliza apenas Java 4 ou APIs anteriores. Cada classe é compilado com o compilador da versão específica, de modo que não há nenhum uso acidental de um Java 5+ API na classe Java 4:

SysUtil (compilado com o compilador 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 (compilado com o compilador 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

Verifique / uso / modificar classe info.olteanu.utils.TextNormalizer no projeto Phramer ( http://sourceforge.net / projetos / phramer / , www.phramer.org) -. o código é BSD licenciado

Esse código pode ser compilado em Java 5 e funciona tanto em Java 5 ou Java 6 (ou futuras versões de Java). Além disso, ele pode ser compilado em Java 6 e ser executado em Java 5 (se ele foi compilado com o bom "-target", para compatibilidade bytecode) ou Java 6 ou qualquer outra versão futura.

IMHO isso resolve totalmente o problema - você está livre para compilar na plataforma Java qualquer 5+, e você é capaz de obter a funcionalidade desejada (normalização) em qualquer plataforma Java 5+ (*)

(*) O 5 solução SUN Java para a normalização provavelmente não estar presente em todas as Java 5 implementações, assim, no pior dos casos você vai acabar recebendo um ClassNotFoundException quando você chamar getNormalizationStringFilter () método.

    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]");

Esta é a pergunta de idade, mas ainda real. Eu descobri algumas possibilidades que não são mencionados nas respostas.

Normalmente, recomenda-se usar a reflexão como é mostrado em algumas outras respostas aqui. Mas se você não quer colocar bagunça no seu código, você pode usar ICU4J biblioteca. Ele contém classe com.ibm.icu.text.Normalizer com o método normalize() que realizam o mesmo trabalho que java.text.Normalizer / sun.text.Normalizer. biblioteca ICU (deve ter) própria implementação do Normalizer assim você pode compartilhar seu projeto com a biblioteca e que deve ser independente de java.
Desvantagem é que a biblioteca UTI é bastante grande.

Se você usar classe Normalizer apenas para a remoção de acentos / diacríticos de Cordas, há também uma outra maneira. Você pode usar Apache commons lang (ver 3). que contém StringUtils com stripAccents() método:

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

Lang3 provavelmente usar a reflexão para invocar Normalizer apropriado de acordo com a versão java. Então vantagem é que você não tem reflexo confusão em seu código.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top