如何参考 Java 1.6 API,同时针对 Java 1.5 进行优雅降级?

发布于 2024-07-30 17:19:00 字数 349 浏览 6 评论 0原文

我想使用 java.text.Normalizer< /a> Java 1.6 中的类来进行 Unicode 规范化,但我的代码必须能够在 Java 1.5 上运行。

我不介意在 1.5 上运行的代码是否不进行规范化,但我不希望它在运行时给出 NoClassDefFoundErrors 或 ClassNotFoundExceptions。

实现这一目标的最佳方法是什么?

I would like to use the java.text.Normalizer class from Java 1.6 to do Unicode normalization, but my code has to be able to run on Java 1.5.

I don't mind if the code running on 1.5 doesn't do normalization, but I don't want it to give NoClassDefFoundErrors or ClassNotFoundExceptions when it runs.

What's the best way to achieve this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

本王不退位尔等都是臣 2024-08-06 17:19:00

通常的方法是通过反射,即不直接引用相关类,而是以编程方式调用它。 如果相关代码不存在,这允许您优雅地捕获异常,并忽略它或尝试其他操作。 反射抛出 ClassNotFoundException,这是一个很好的正常异常,而不是 NoClassDefFoundError,后者有点可怕。

对于 java.text.Normalizer 来说,这应该非常简单,因为它只是几个静态方法,并且很容易通过反射调用。

The usual way of doing this is via reflection, i.e. don't refer directly to the class in question, but invoke it programmatically. This allows you to catch exceptions gracefully if the code in question doesn't exist, and either ignore it, or try something else. Reflection throws ClassNotFoundException, which is a nice normal exception, rather than NoClassDefFoundError, which is a bit scarier.

In the case of java.text.Normalizer, this should be pretty easy, since it's just a couple of static methods, and easy to invoke via reflection.

南风起 2024-08-06 17:19:00
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();
}
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 your client code:

NfcNormalizer normalizer;
try
{
  normalizer = Class.forName("JDK16NfcNormalizer").newInstance();
}
catch(Exception e)
{
  normalizer = new IdentityNfcNormalizer();
}
话少心凉 2024-08-06 17:19:00

我不介意在 1.5 上运行的代码是否不进行规范化,但我不希望它在运行时给出 NoClassDefFoundErrors 或 ClassNotFoundExceptions。

如果你想避免反射,你实际上可以捕获这些错误。

这样,您可以使用 Java6 编译器针对闪亮的新类进行编译,并且它仍然可以在 Java5 上工作(如“不执行任何操作,但也不会崩溃”)。

您还可以结合这两种方法,并使用反射检查该类是否存在,以及是否继续以非反射方式调用它。 这就是安德鲁的解决方案正在做的事情。

如果您还需要在 Java5 上进行编译,那么您需要一直进行反射。

I don't mind if the code running on 1.5 doesn't do normalization, but I don't want it to give NoClassDefFoundErrors or ClassNotFoundExceptions when it runs.

If you want to avoid reflection, you can actually catch those Errors.

This way, you can compile against the shiny new classes with a Java6 compiler, and it will still work (as in "not do anything, but also not crash") on Java5.

You can also combine the two approaches, and check if the class exists using reflection, and if it does continue to call it in a non-reflective way. This is what Andrew's solution is doing.

If you also need to compile on Java5, then you need to go reflection all the way.

谈下烟灰 2024-08-06 17:19:00

我也有同样的需求,因为我们的代码需要在 Java 1.2 以来的所有 Java 版本上运行,但有些代码需要利用较新的 API(如果可用)。

在使用反射获取 Method 对象并动态调用它们的各种排列之后,一般来说,我选择了最好的包装器样式方法(尽管在某些情况下,仅将反射的 Method 存储为静态并调用它更好 - 这取决于) 。

以下是一个示例“系统实用程序”类,它在运行早期版本时公开了 Java 5 的某些较新的 API - 相同的原则也适用于早期 JVM 中的 Java 6。 此示例使用 Singleton,但如果底层 API 需要,可以轻松实例化多个对象。

有两个类:

  • SysUtil
  • SysUtil_J5

如果运行时 JVM 是 Java 5 或更高版本,则使用后者。 否则,将使用 SysUtil 中的默认实现中兼容的后备方法,该实现仅使用 Java 4 或更早的 API。 每个类都使用特定版本的编译器进行编译,因此 Java 4 类中不会意外使用 Java 5+ API:

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

I have had this same need, since we have code that needs to run on all versions of Java from Java 1.2, but some code needs to take advantage of newer API's if they are available.

After various permutations using reflection to obtain Method objects and invoking them dynamically, I have settled on a wrapper style approach as best, in general (although under some circumstances, just storing the reflected Method as a static and invoking it is better - it depends).

Following is an example "System Utility" class which exposes certain newer API's for Java 5 when running an earlier version - the same principles hold for Java 6 in earlier JVMs. This example uses a Singleton, but could easily instantiate multiple objects if the underlying API needed that.

There are two classes:

  • SysUtil
  • SysUtil_J5

The latter is the one used if the run-time JVM is Java 5 or later. Otherwise fallback methods which are compatible in contract are used from the default implementation in SysUtil which utilizes only Java 4 or earlier APIs. Each class is compiled with the specific version's compiler, so that there is no accidental usage of a Java 5+ API in the Java 4 class:

SysUtil (compiled with the Java 4 compiler)

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 (compiled with the Java 5 compiler)

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
萝莉病 2024-08-06 17:19:00

检查/使用/修改类 Phramer 项目中的 info.olteanu.utils.TextNormalizer (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 实现中,因此在最坏的情况下,当您调用 getNormalizationStringFilter() 方法时,您最终会得到 ClassNotFoundException。

Check/use/modify class info.olteanu.utils.TextNormalizer in Phramer project (http://sourceforge.net/projects/phramer/ , www.phramer.org ) - the code is BSD licensed.

That code can be compiled in Java 5 and runs both in Java 5 or Java 6 (or future Java versions). Also, it can be compiled in Java 6 and be run in Java 5 (if it was compiled with the proper "-target", for bytecode compatibility) or Java 6 or any other future version.

IMHO this fully solves your problem - you are free to compile on any Java 5+ platform, and you are able to get the functionality desired (normalization) on any Java 5+ platform (*)

(*) The SUN Java 5 solution for normalization will most likely not be present on all Java 5 implementations, so in the worst case scenario you will end up getting a ClassNotFoundException when you call getNormalizationStringFilter() method.

冷情妓 2024-08-06 17:19:00
    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]");
    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]");
遗弃M 2024-08-06 17:19:00

这是老问题,但仍然是现实的。 我发现了一些答案中没有提到的可能性。

通常建议使用反射,如此处其他一些答案所示。 但如果您不想让代码变得混乱,可以使用 icu4j 库。 它包含 com.ibm.icu.text.Normalizer 类以及 normalize() 方法,该方法执行与 java.text.Normalizer/sun.text.Normalizer 相同的工作。 Icu 库有(应该有)自己的 Normalizer 实现,因此您可以与库共享您的项目,并且应该独立于 java。
缺点是 icu 库相当大。

如果您使用 Normalizer 类只是为了从字符串中删除重音符号/变音符号,还有另一种方法。 您可以使用包含 StringUtilsApache commons lang 库(版本 3)使用方法 stripAccents()

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

Lang3 库可能使用反射根据 java 版本调用适当的 Normalizer。 所以优点是你的代码中不会出现反射混乱。

This is old question, but still actual. I found out some possibilities that are not mentioned in answers.

Usually it is recommended to use reflection as is shown in some other answers here. But if you don't want to put mess in your code, you can use icu4j library. It contains com.ibm.icu.text.Normalizer class with normalize() method that perform the same job as java.text.Normalizer/sun.text.Normalizer. Icu library has (should have) own implementation of Normalizer so you can share your project with library and that should be java-independent.
Disadvantage is that the icu library is quite big.

If you using Normalizer class just for removing accents/diacritics from Strings, there's also another way. You can use Apache commons lang library (ver. 3) that contains StringUtils with method stripAccents():

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

Lang3 library probably use reflection to invoke appropriate Normalizer according to java version. So advantage is that you don't have reflection mess in your code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文