代码之家  ›  专栏  ›  技术社区  ›  Simon Nickerson

我如何引用Java 1.6 API,同时优雅地对Java 1.5进行降级?

  •  10
  • Simon Nickerson  · 技术社区  · 15 年前

    我想用 java.text.Normalizer 类从Java 1.6进行Unicode标准化,但是我的代码必须能够在Java 1.5上运行。

    我不介意在1.5上运行的代码是否没有进行规范化,但我不想给出 NoClassDefFoundError S或 ClassNotFoundException 当它运行时。

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

    7 回复  |  直到 11 年前
        1
  •  13
  •   skaffman    15 年前

    通常的方法是通过反射,即不要直接引用相关的类,而是通过编程方式调用它。这允许您在所讨论的代码不存在时优雅地捕获异常,并忽略它,或者尝试其他方法。反射投掷 ClassNotFoundException 这是一个很好的正常例外,而不是 NoClassDefFoundError 有点吓人。

    在情况下 java.text.Normalizer 这应该很容易,因为它只是两个静态方法,并且很容易通过反射来调用。

        2
  •  9
  •   Andrew Duffy    15 年前
    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();
    }
    
        3
  •  3
  •   Thilo    15 年前

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

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

    通过这种方式,您可以使用Java6编译器针对闪亮的新类进行编译,它仍然可以在Java5上工作(如“不做任何事情,但也不会崩溃”)。

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

    如果你还需要 编译 在Java5上,那么你需要一路反思。

        4
  •  1
  •   L. Cornelius Dol    15 年前

    我有同样的需求,因为我们有代码需要运行在Java 1.2的所有版本的Java上,但是如果代码可用,一些代码需要利用更新的API。

    在使用反射来获取方法对象并动态调用它们的各种排列之后,一般来说,我已经将包装风格的方法定为最佳方法(尽管在某些情况下,仅仅将反射方法存储为静态方法并调用它更好,这取决于具体情况)。

    下面是一个示例“系统实用工具”类,它在运行早期版本时为Java 5公开某些更新的API,在早期JVM中,Java 6使用相同的原则。这个例子使用了一个单例,但是如果底层API需要的话,可以很容易地实例化多个对象。

    有两类:

    • 西索尔
    • 雪松J5

    后者是运行时JVM是Java 5或更高版本时使用的。否则,在契约中兼容的回退方法将从SysUtil的默认实现中使用,该实现仅使用Java 4或更早的API。每个类都是用特定版本的编译器编译的,因此Java 4类中没有意外使用Java 5 + API:

    SyStul(用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
    

    SysTurijJ5(用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
    
        5
  •  1
  •   Marian    15 年前

    检查/使用/修改类 info.olteanu.utils.TextNormalizer 在Phramer项目中( http://sourceforge.net/projects/phramer/ ,www.phramer.org)-代码是BSD许可的。

    该代码可以在Java 5中编译并运行在Java 5或Java 6(或将来的Java版本)中。此外,它可以在Java 6中编译并在Java 5中运行(如果它是用适当的“目标”、字节码兼容)或Java 6或任何其他未来版本编译的。

    这完全解决了你的问题——你可以在任何Java 5 +平台上编译,你可以在任何Java 5 +平台上获得所需的功能(标准化)(*)

    (*)规范化的Sun Java 5解决方案很可能不存在于所有Java 5实现中,所以在最坏的情况下,当调用GealQualEnsithStrufFielter()方法时,最终会得到类NojFuffDestExpTebug。

        6
  •  0
  •   MrPatol    15 年前
        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]");
    
        7
  •  0
  •   sporak    11 年前

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

    通常建议使用反射,如这里其他一些答案所示。但是如果你不想把代码搞得一团糟,你可以使用 icu4j library . 它包含 com.ibm.icu.text.Normalizer 上课用 normalize() 方法执行与java.text.normalizer/sun.text.normalizer相同的作业。ICU库已经(应该)拥有Normalizer的实现,这样您就可以与库共享您的项目,并且应该是Java独立的。
    缺点是重症监护室的图书馆很大。

    如果使用Normalizer类只是为了从字符串中删除重音符号/音调符号,那么还有另一种方法。你可以用 Apache commons lang library (ver. 3) 包含 StringUtils 用方法 stripAccents() :

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

    根据Java版本,Lang3库可能使用反射调用适当的Normalizer。所以优点是您的代码中没有反射混乱。

    推荐文章