7

我的 API 中有一个带有通用可变参数的方法。"varargs"我希望我的 API 与 Java 6 源代码和二进制兼容,但如果 Java 7 API 使用者不会遭受不必要的警告,那就太好了。

我能想到的一个技巧是将我自己的java.lang.SafeVarargs注释添加到我的 API 中,并将它与我的可交付成果一起发布。作为一个效果:

  • Java 6 编译器无法识别此注解并忽略它。
  • Java 7 编译器会识别此注释,并且可能(?)首先从 JDK 类加载该注释,因此它们不会再产生烦人的警告。

除了许可证问题,这是否保证有效?它似乎适用于 javac。或者是否存在从 JDK 重新定义注释的配置在调用站点有不良副作用?或者还有其他方法可以解决这个 Java 6/7 互操作性问题吗?

一个相关的问题:

4

1 回答 1

2

问题:这保证有效吗?

答:视情况而定。我想指出一个潜在的问题。

“真正的”@SafeVarargs 注释是用 RetentionPolicy.RUNTIME 声明的(见这里)。其原因(与具有 RetentionPolicy.SOURCE 的 @Override 相比)可能是允许编译器在调用站点检查它。然而,

  • 如果您的 SafeVarargs 注释版本是使用 RetentionPolicy.RUNTIME 声明的
  • 并且使用它的代码使用 JRE 6 运行(不包含此注释)
  • 并且您的 SafeVarargs 注释版本位于类路径中

那么任何在运行时使用反射来获取方法注释的代码(例如 Spring 框架)都会因 SecurityException 而失败,因为注释的包名以“java”开头。(参见来自 Sun JRE 1.6.0_31 的 ClassLoader.preDefineClass() 方法和下面的 stacttrace 的相关代码)。

如果注释在编译时位于类路径上,但不在运行时(maven 中的“提供”范围),则不会引发异常(至少对于 Sun JRE)。

最好的解决方案是使用一些 Maven 工件,例如 java.lang.java7-annotations,并在“提供”范围内使用它。我在这里找到了一些东西(工件:com.google.backport.safevarargs),但它不在中央仓库中。


if ((name != null) && name.startsWith("java.")) {
    throw new SecurityException("禁止的包名:" +
                                 name.substring(0, name.lastIndexOf('.')));
}
线程“主”java.lang.SecurityException 中的异常:禁止的包名称:java.lang
    在 java.lang.ClassLoader.preDefineClass(ClassLoader.java:479)
    在 java.lang.ClassLoader.defineClassCond(ClassLoader.java:625)
    在 java.lang.ClassLoader.defineClass(ClassLoader.java:615)
    在 java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
    在 java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
    在 java.net.URLClassLoader.access$000(URLClassLoader.java:58)
    在 java.net.URLClassLoader$1.run(URLClassLoader.java:197)
    在 java.security.AccessController.doPrivileged(本机方法)
    在 java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    在 java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    在 sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    在 java.lang.ClassLoader.loadClass(ClassLoader.java:247)
于 2014-02-16T13:26:30.887 回答