Wie kann ich auf Java 1.6 APIs beziehen, während graziös gegen Java 1.5 zu verschlechtern?

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

  •  16-09-2019
  •  | 
  •  

Frage

Ich möchte die java.text .Normalizer Klasse von Java 1.6 zu tun Unicode-Normalisierung, aber mein Code hat auf Java 1.5 laufen zu können.

Ich habe nichts dagegen, wenn der Code auf 1.5 läuft Normalisierung nicht tun, aber ich will es nicht NoClassDefFoundErrors oder ClassNotFoundExceptions geben, wenn es ausgeführt wird.

Was ist der beste Weg, dies zu erreichen?

War es hilfreich?

Lösung

Der übliche Weg, dies zu tun, durch Reflexion ist, das heißt beziehen sich nicht direkt auf die Klasse in Frage, aber es programmatisch aufrufen. Dies ermöglicht Ihnen, Exceptions zu fangen, wenn der betreffende Code nicht vorhanden ist, und entweder ignorieren oder etwas anderes versuchen. Reflexion wirft ClassNotFoundException, was eine nette normale Ausnahme ist, anstatt NoClassDefFoundError, das ist ein bisschen gruselige.

Im Falle von java.text.Normalizer, sollte dies recht einfach sein, da es nur ein paar statische Methoden ist, und einfach über Reflexion aufzurufen.

Andere Tipps

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

In Ihrem Client-Code:

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

Ich habe nichts dagegen, wenn der Code auf 1.5 läuft Normalisierung nicht tun, aber ich will es nicht NoClassDefFoundErrors oder ClassNotFoundExceptions geben, wenn es ausgeführt wird.

Wenn Sie Reflexion vermeiden möchten, können Sie tatsächlich diese Fehler fangen.

Auf diese Weise können Sie die glänzenden neuen Klassen mit einem Java6 Compiler kompilieren gegen, und es wird funktionieren nach wie vor (wie in „nichts tun, aber auch nicht abstürzen“) auf Java5.

Sie können auch die beiden Ansätze kombinieren, und prüfen Sie, ob die Klasse mithilfe von Reflektion vorhanden ist, und wenn ja weiterhin in einer nicht reflektierenden Art und Weise nennen. Dies ist, was Andrew Lösung tut.

Wenn Sie auch brauchen Compile auf Java5, dann müssen Sie Reflexion den ganzen Weg gehen.

Ich habe dieses gleiche Bedürfnis gehabt, da wir Code haben, der auf allen Versionen von Java von Java 1.2 ausgeführt werden muss, aber einige Code muss die neueren API nehmen, wenn sie verfügbar sind.

Nach verschiedenen Permutationen Reflexion unter Verwendung von Method-Objekte zu erhalten und sie dynamisch aufrufen, ich habe auf einem Wrapper-Stil Ansatz angesiedelt, wie am besten in der Regel (wenn auch unter Umständen nur das reflektierte Methode als statisches Speichern und Aufrufe es besser ist - es hängt).

Es folgt ein Beispiel „System Utility“ Klasse, die 5 bestimmte neuere APIs für Java aussetzt, wenn eine frühere Version ausgeführt wird - die gleichen Grundsätze gelten für Java 6 in früheren JVMs. In diesem Beispiel wird ein Singleton, aber könnte leicht mehrere Objekte instanziiert, wenn die zugrunde liegende API, dass benötigt wird.

Es gibt zwei Klassen:

  • sysutil
  • SysUtil_J5

Letzteres ist derjenige, wenn die Laufzeit verwendet JVM ist Java 5 oder höher. Ansonsten Ausweichverfahren, die in Vertrag vereinbar sind, von der Standardimplementierung in sysutil verwendet, die nur Java 4 oder früher APIs verwendet. Jede Klasse wird mit der speziellen Version des Compilers kompiliert, so dass es keine versehentliche Verwendung eines Java 5+ API in der Java-4-Klasse ist:

sysutil (mit der Java-4-Compiler kompiliert)

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 (mit Java 5-Compiler kompiliert)

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

Überprüfen / Verwendung / ändern Klasse info.olteanu.utils.TextNormalizer in Phramer Projekt ( http://sourceforge.net / projects / phramer / , www.phramer.org.) - der Code lizenziert ist BSD-

Dieser Code kann in Java 5 kompiliert werden und läuft sowohl in Java 5 oder Java 6 (oder zukünftigen Java-Versionen). Auch kann es in Java 6 kompiliert werden und in Java 5 ausgeführt werden (wenn es mit dem richtigen „-Ziel“, für Bytecode-Kompatibilität kompiliert wurde) oder Java 6 oder andere zukünftige Version.

IMHO dieses vollständig löst Ihr Problem - Sie auf jedem Java 5+ Plattform zu kompilieren frei sind, und Sie sind in der Lage, die Funktionalität zu erhalten gewünschte (Normalisierung) auf jedem Java 5+ Plattform (*)

(*) Die SUN Java 5-Lösung für die Normalisierung wird höchstwahrscheinlich nicht vorhanden sein, auf allen Java 5-Implementierungen, also im schlimmsten Fall Sie eine ClassNotFoundException wird am Ende bekommen, wenn Sie getNormalizationStringFilter () -Methode aufrufen.

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

Das ist alte Frage, aber immer noch aktuell. Ich fand einige Möglichkeiten, die nicht in den Antworten erwähnt werden.

In der Regel empfiehlt es sich Reflexion zu verwenden, wie in einigen anderen Antworten hier gezeigt wird. Aber wenn Sie nicht wollen, Chaos in Ihrem Code setzen, können Sie icu4j Bibliothek . Es enthält com.ibm.icu.text.Normalizer Klasse mit normalize() Methode, die die gleiche Arbeit wie java.text.Normalizer / sun.text.Normalizer auszuführen. ICU-Bibliothek hat (soll) eine eigene Implementierung von Normalizer so können Sie Ihr Projekt mit Bibliothek teilen und das sollte Java-unabhängig sein.
Nachteil ist, dass die ICU-Bibliothek ist ziemlich groß.

Wenn Sie Normalizer-Klasse nur für Akzente / diakritische Zeichen von Strings, gibt es auch eine andere Art und Weise zu entfernen. Sie können Apache commons lang Bibliothek verwenden (ver 3.) die StringUtils mit Methode stripAccents() enthält:

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

LANG3 Bibliothek wahrscheinlich verwenden Reflexion geeignete Normalizer aufzurufen nach Java-Version. So Vorteil ist, dass Sie nicht über Reflexion Chaos in Ihrem Code haben.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top