¿Cómo refiero a Java 1.6 API, mientras que degradan correctamente sobre Java 1.5?

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

  •  16-09-2019
  •  | 
  •  

Pregunta

Me gustaría utilizar el java.text clase .Normalizer de Java 1.6 para hacer la normalización de Unicode, pero mi código tiene que ser capaz de ejecutar en Java 1.5.

No me importa si el código que se ejecuta en 1.5 no hace la normalización, pero yo no lo quiero para dar NoClassDefFoundErrors o ClassNotFoundExceptions cuando se ejecuta.

¿Cuál es la mejor manera de lograr esto?

¿Fue útil?

Solución

La forma habitual de hacer esto es a través de la reflexión, es decir, no se refieren directamente a la clase en cuestión, pero lo invocan mediante programación. Esto le permite capturar las excepciones con gracia si el código en cuestión no existe, y, o bien lo ignoran, o intentar algo más. Reflexión lanza ClassNotFoundException, que es un buen excepción normal, en lugar de NoClassDefFoundError, que es un poco más aterrador.

En el caso de java.text.Normalizer, esto debería ser bastante fácil, ya que es sólo un par de métodos estáticos, y fácil de invocar a través de la reflexión.

Otros consejos

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

En su código de cliente:

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

No me importa si el código que se ejecuta en 1.5 no hace la normalización, pero yo no lo quiero para dar NoClassDefFoundErrors o ClassNotFoundExceptions cuando se ejecuta.

Si se quiere evitar la reflexión, en realidad se puede coger esos errores.

De esta manera, se puede compilar en contra de las nuevas clases brillantes con un compilador java6, y seguirá siendo el trabajo (como en "no hacer nada, sino que además no chocar") en Java5.

También se pueden combinar los dos enfoques, y comprobar si existe la clase mediante la reflexión, y si lo hace seguir llamarlo de una manera no reflectante. Esto es lo que la solución de Andrew está haciendo.

Si también necesita compilación en Java5, entonces tienes que ir a la reflexión hasta el final.

he tenido esta misma necesidad, ya que tenemos el código que necesita para funcionar con todas las versiones de Java de Java 1.2, pero algo de código tiene que tomar ventaja de nuevas API, si están disponibles.

Después de varias permutaciones que utilizan la reflexión para obtener objetos Método e invocando de forma dinámica, que se han asentado en un enfoque de estilo de envoltura de lo mejor, en general (aunque en algunas circunstancias, simplemente almacenar el Método reflejada como estática e invocando es mejor - que depende).

A continuación se presenta un ejemplo "Sistema de utilidad de" clase que expone ciertas nuevas API para Java 5 cuando se ejecuta una versión anterior - los mismos principios para Java 6 en las JVM anteriores. En este ejemplo se utiliza un Singleton, pero fácilmente podría crear una instancia de varios objetos si la API subyacente que necesita.

Hay dos clases:

  • SysUtil
  • SysUtil_J5

Este último es el que se utiliza si el tiempo de ejecución de Java JVM es 5 o posterior. De lo contrario, los métodos de repliegue que son compatibles en el contrato se utilizan de la aplicación por defecto en SysUtil que utiliza Java sólo 4 o anteriores API. Cada clase se compila con el compilador de la versión específica, por lo que no hay uso accidental de una API de Java 5+ en la clase Java 4:

SysUtil (compilado con el 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 con el compilador de 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

Comprobar / uso / modificar la clase info.olteanu.utils.TextNormalizer en el proyecto Phramer ( http://sourceforge.net / proyectos / phramer / , www.phramer.org) -. el código es BSD licencia

Ese código puede ser compilado en Java 5 y funciona tanto en Java 5 o 6 de Java (o futuras versiones de Java). También, puede ser compilado en Java 6 y ejecutar en Java 5 (si se compila con el "-target" adecuado, para la compatibilidad de código de bytes) o Java 6 o cualquier otra versión futura.

En mi humilde opinión esto resuelve totalmente el problema - que son libres de compilar en cualquier plataforma Java 5+, y que son capaces de conseguir la funcionalidad deseada (normalización) en la plataforma Java cualquier 5+ (*)

(*) La solución SUN Java 5 para la normalización más probable es que no estará presente en todos los Java 5 implementaciones, por lo que en el peor de los casos el resultado final será conseguir un ClassNotFoundException cuando llame 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]");

Esto es cuestión de edad, pero todavía real. He encontrado algunas posibilidades que no se mencionan en las respuestas.

Por lo general, se recomienda utilizar la reflexión como se muestra en algunas otras respuestas aquí. Pero si no quiere poner desorden en su código, puede utilizar icu4j biblioteca . Contiene clase com.ibm.icu.text.Normalizer con el método normalize() que realizan el mismo trabajo que java.text.Normalizer / sun.text.Normalizer. biblioteca UCI tiene (debe tener) propia implementación de Normalizador para que pueda compartir su proyecto con la biblioteca y que debería ser independiente de Java.
desventaja es que la biblioteca UCI es bastante grande.

Si el uso de la clase Normalizador sólo para la eliminación de acentos / diacríticos de las cadenas, también hay otra manera. Puede usar Apache Commons Lang (ver 3). que contiene StringUtils con stripAccents() método:

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

biblioteca Lang3 probablemente utilizar la reflexión para invocar Normalizador apropiado de acuerdo a la versión de Java. Así ventaja es que usted no tiene desorden reflexión en el código.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top