在预先存在的编译上下文中使用新的注释属性

Use of a new annotation attribute in a pre-existing compiled context

我在 JDK8 环境下使用以下代码:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Client", namespace = "http://schemas.datacontract.org/2004/07/BLA.BLA.Model.Client", propOrder = {
"birthDate",
"email",
"ip",
"name",
"phone"
})
@Generated(value = "com.sun.tools.internal.xjc.Driver", date = "2016-12-07T07:34:51+04:00", comments = "JAXB RI v2.2.4-2")
public class Client {

@XmlElementRef(name = "BirthDate", namespace = "http://schemas.datacontract.org/2004/07/BLA.BLA.Model.Client", type = JAXBElement.class, required = false)
@Generated(value = "com.sun.tools.internal.xjc.Driver", date = "2016-12-07T07:34:51+04:00", comments = "JAXB RI v2.2.4-2")

此代码用于在 JDK8 下编译的应用程序,maven 的构建器目标设置为 1.6,运行 在 JDK6 下。 (不要问我为什么 :( )

我想知道它是如何工作的? 属性 @XmlElementRef 在JDK6版本中没有required属性,无法用它构建。

PS。我不问,为什么我可以编译代码。我很感兴趣为什么我可以 运行 它。是因为某些特定于注释的语义,因为我的代码不知道这个 属性 required 并且从不使用它,还是因为其他原因?

这是一个暂定的答案,我并不认为它是完全准确的,但在这个问题上似乎是合理的。

它在 JLS 中说明,在二进制兼容性(强调我的)中:

13.5.7. Evolution of Annotation Types

Annotation types behave exactly like any other interface. Adding or removing an element from an annotation type is analogous to adding or removing a method. There are important considerations governing other changes to annotation types, but these have no effect on the linkage of binaries by the Java Virtual Machine. Rather, such changes affect the behavior of reflective APIs that manipulate annotations. The documentation of these APIs specifies their behavior when various changes are made to the underlying annotation types.

Adding or removing annotations has no effect on the correct linkage of the binary representations of programs in the Java programming language.

如果我们参考接口的说法:

13.5.3. Interface Members

Adding a method to an interface does not break compatibility with pre-existing binaries.

我的理解是 @XmlElementRefrequiered“元素”在内部表示为接口的方法。

因此您可以像对待任何其他接口一样推断它与以前的 JDK 的兼容性。如果您使用给定接口的给定方法,则需要在接口声明中使用它来编译代码。这就是为什么您的代码无法在 2.2 之前的 JAXB 版本上编译(参见 this:JDK6 随 JAXB 2.1.10 一起提供)。

但是,如果您的代码已经编译(“pre-built 二进制文件”),只要不需要访问上述方法(直接通过反射),就可以了此方法的运行时不存在。

这样想:如果你有一个 class 实现一个接口,如果它有比接口指定的方法更多的方法,那也没有坏处。如果您将 class 与方法更少的旧版本接口一起使用,也不会造成任何伤害。

在注释的情况下,它通常更简单,因为典型的反射实现不会生成具有所有这些方法的 class,而是生成一个 proxy,其调用处理程序将查找通过在包含在 class 文件中发现的值的 Map 中查找方法的名称来请求值。因此,在该映射中拥有比通过接口方法调用请求的更多的键也没有坏处。