Come faccio a riferisco a Java 1.6 API, mentre degradare con grazia contro Java 1.5?

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

  •  16-09-2019
  •  | 
  •  

Domanda

Vorrei utilizzare il java.text .Normalizer classe da Java 1.6 per fare la normalizzazione Unicode, ma il mio codice deve essere in grado di funzionare su Java 1.5.

Non mi importa se il codice in esecuzione su 1,5 non fa la normalizzazione, ma io non voglio dare NoClassDefFoundErrors o ClassNotFoundExceptions quando viene eseguito.

Qual è il modo migliore per raggiungere questo?

È stato utile?

Soluzione

Il solito modo per farlo è attraverso la riflessione, vale a dire non si riferiscono direttamente alla classe in questione, ma invocano a livello di codice. Ciò consente di rilevare le eccezioni con grazia se il codice in questione non esiste, e neanche lo ignorano, o provare qualcosa di diverso. Riflessione getta ClassNotFoundException, che è una bella eccezione normale, piuttosto che NoClassDefFoundError, che è un po 'più paura.

Nel caso di java.text.Normalizer, questo dovrebbe essere abbastanza facile, dal momento che è solo un paio di metodi statici, e facile da richiamare attraverso la riflessione.

Altri suggerimenti

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

Nel codice client:

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

Non mi importa se il codice in esecuzione su 1,5 non fa la normalizzazione, ma io non voglio dare NoClassDefFoundErrors o ClassNotFoundExceptions quando viene eseguito.

Se si vuole evitare di riflessione, si può effettivamente prendere quegli errori.

In questo modo, si può compilare contro le nuove classi lucide con un compilatore java6, e continuerà a funzionare (come in "non fare nulla, ma anche non in crash") su Java5.

È inoltre possibile combinare i due approcci, e verificare se la classe esiste utilizzando la riflessione, e se lo fa continuare a chiamarlo in un modo non riflettente. Questo è ciò che la soluzione di Andrew sta facendo.

Se hai bisogno anche di di compilazione su Java5, allora avete bisogno di andare fino in fondo riflessione.

Ho avuto questo stesso bisogno, dal momento che abbiamo il codice che deve essere eseguito su tutte le versioni di Java da Java 1.2, ma po 'di codice ha bisogno di sfruttare più recenti API se sono disponibili.

Dopo varie permutazioni utilizzando la riflessione di ottenere oggetti Metodo e li invocano dinamicamente, ho optato per un approccio stile involucro meglio, in generale (anche se in alcune circostanze, basta memorizzare il metodo riportato come statico e invocando è meglio - dipende).

Di seguito è riportato un esempio "Utilità di sistema" classe che espone alcune nuove API per Java 5 quando si esegue una versione precedente - gli stessi principi valgono per Java 6 in JVM precedenti. Questo esempio utilizza un Singleton, ma potrebbe facilmente istanziare più oggetti se l'API sottostante bisogno che.

Ci sono due classi:

  • Sysutil
  • SysUtil_J5

Il secondo è quello usato se il run-time JVM è Java 5 o successivo. In caso contrario, i metodi di ripiego che sono compatibili a contratto sono utilizzati dalla implementazione di default in Sysutil che utilizza solo Java 4 o precedenti API. Ogni classe è compilato con il compilatore della versione specifica, in modo che non ci sia l'uso accidentale di API Java 5+ nella classe Java 4:

Sysutil (compilato con il compilatore 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 (compilato con Java 5 compilatore)

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

Controlla / uso / modificare la classe info.olteanu.utils.TextNormalizer nel progetto Phramer ( http://sourceforge.net / progetti / phramer / , www.phramer.org) -. il codice BSD licenza

Questo codice può essere compilato in Java 5 e funziona sia in Java 5 o Java 6 (o versioni future di Java). Inoltre, può essere compilato in Java 6 e essere eseguito in Java 5 (se è stato compilato con il corretto "-target", per la compatibilità bytecode) o Java 6 o qualsiasi altra versione futura.

IMHO questo risolve completamente il problema - si è liberi di compilazione su piattaforma Java qualsiasi 5+, e si è in grado di ottenere la funzionalità desiderata (normalizzazione) su piattaforma Java qualsiasi 5+ (*)

(*) La soluzione al 5 SUN Java per la normalizzazione molto probabilmente non sarà presente su tutte le Java 5 implementazioni, così nel peggiore dei casi si finirà per ottenere un ClassNotFoundException quando si chiama il metodo () 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]");

Questa è vecchia questione, ma ancora attuale. Ho scoperto alcune possibilità che non sono menzionati nelle risposte.

Di solito si consiglia di utilizzare la riflessione, come è mostrato in alcune altre risposte qui. Ma se non si vuole mettere confusione nel codice, è possibile utilizzare ICU4J biblioteca . Contiene classe com.ibm.icu.text.Normalizer con metodo normalize() che svolgono lo stesso lavoro di java.text.Normalizer / sun.text.Normalizer. libreria ICU ha (dovrebbe avere) una propria implementazione di normalizzatore in modo da poter condividere il progetto con la biblioteca e che dovrebbe essere in java-indipendente.
svantaggio è che la libreria ICU è abbastanza grande.

Se si utilizza classe normalizzatore solo per la rimozione di accenti / segni diacritici dalle stringhe, c'è anche un altro modo. È possibile utilizzare Apache Commons Lang (ver. 3) che contiene StringUtils con metodo stripAccents():

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

biblioteca Lang3 probabilmente utilizzare la reflection per richiamare adeguata Normalizer in base alla versione di Java. Così vantaggio è che non si dispone di riflessione disordine nel codice.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top