【问题标题】:java8 - annotating compareTo <T> of Comparable<T> adds annotations to compareTo(Object o)java 8 - Comparable<T> 的注解 compareTo <T> 添加注解到 compareTo(Object o)
【发布时间】:2014-04-17 08:35:50
【问题描述】:

我有一个注释

package javaannotationtest;

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
}

这适用于以下类中的 compareTo

package javaannotationtest;

public class Customer implements Comparable<Customer>{
    @Override
    @CustomAnnotation
    public int compareTo(Customer o) {
        return 0;
    }
}

该类使用 java-7 和 java-8 编译代码给出不同的结果。

Java 7

1.7.0_45 -> public int javaannotationtest.Customer.compareTo(javaannotationtest.Customer)
 has  annotation of type javaannotationtest.CustomAnnotation
1.7.0_45 -> public int javaannotationtest.Customer.compareTo(java.lang.Object)
 has no annotation of type javaannotationtest.CustomAnnotation

注意 compareTo(Object) 没有注释。

Java 8

1.8.0 -> public int javaannotationtest.Customer.compareTo(javaannotationtest.Customer)
 has  annotation of type javaannotationtest.CustomAnnotation
1.8.0 -> public int javaannotationtest.Customer.compareTo(java.lang.Object)
 has  annotation of type javaannotationtest.CustomAnnotation

Java 8 在compareTo(java.lang.Object) 方法中添加了注解

这里是用 Java 8 编译的版本的 javap 输出(可能不相关,它显示了添加到两个方法的注释)

  Classfile /C:/code/java8annoation/out/production/java8annoation/javaannotationtest/Customer.class
  Last modified 17 Apr, 2014; size 719 bytes
  MD5 checksum 678e0371f5f9ed5666b513c940f365a7
  Compiled from "Customer.java"
public class javaannotationtest.Customer extends java.lang.Object implements java.lang.Comparable<javaannotationtest.Customer>
  Signature: #20                          // Ljava/lang/Object;Ljava/lang/Comparable<Ljavaannotationtest/Customer;>;
  SourceFile: "Customer.java"
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#23         //  java/lang/Object."<init>":()V
   #2 = Class              #24            //  javaannotationtest/Customer
   #3 = Methodref          #2.#25         //  javaannotationtest/Customer.compareTo:(Ljavaannotationtest/Customer;)I
   #4 = Class              #26            //  java/lang/Object
   #5 = Class              #27            //  java/lang/Comparable
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               LocalVariableTable
  #11 = Utf8               this
  #12 = Utf8               Ljavaannotationtest/Customer;
  #13 = Utf8               compareTo
  #14 = Utf8               (Ljavaannotationtest/Customer;)I
  #15 = Utf8               o
  #16 = Utf8               RuntimeVisibleAnnotations
  #17 = Utf8               Ljavaannotationtest/CustomAnnotation;
  #18 = Utf8               (Ljava/lang/Object;)I
  #19 = Utf8               Signature
  #20 = Utf8               Ljava/lang/Object;Ljava/lang/Comparable<Ljavaannotationtest/Customer;>;
  #21 = Utf8               SourceFile
  #22 = Utf8               Customer.java
  #23 = NameAndType        #6:#7          //  "<init>":()V
  #24 = Utf8               javaannotationtest/Customer
  #25 = NameAndType        #13:#14        //  compareTo:(Ljavaannotationtest/Customer;)I
  #26 = Utf8               java/lang/Object
  #27 = Utf8               java/lang/Comparable
{
  public javaannotationtest.Customer();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0       
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return        
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ljavaannotationtest/Customer;

  public int compareTo(javaannotationtest.Customer);
    descriptor: (Ljavaannotationtest/Customer;)I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: iconst_0      
         1: ireturn       
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       2     0  this   Ljavaannotationtest/Customer;
            0       2     1     o   Ljavaannotationtest/Customer;
    RuntimeVisibleAnnotations:
      0: #17()

  public int compareTo(java.lang.Object);
    descriptor: (Ljava/lang/Object;)I
    flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0       
         1: aload_1       
         2: checkcast     #2                  // class javaannotationtest/Customer
         5: invokevirtual #3                  // Method compareTo:(Ljavaannotationtest/Customer;)I
         8: ireturn       
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Ljavaannotationtest/Customer;
    RuntimeVisibleAnnotations:
      0: #17()
}

有人能解释一下 Java 8 的相关变化吗? (将在符合条件时提供赏金)。

【问题讨论】:

  • 我不明白这个问题。你问的是什么相关的变化?在这方面,注释在两个 java 版本中的两个方法上都有。
  • 没有。在 java 7 中, java8.Customer.compareTo(java.lang.Object) 没有注释。 (编辑问题以明确这一点)

标签: java annotations java-8


【解决方案1】:

问题JDK-6695379 - Copy method annotations and parameter annotations to synthetic bridge methods 中描述了此更改。似乎没有太多关于此功能的讨论,但请求和理由对我来说确实有意义。

请求描述: 当一个类扩展一个泛型类或实现一个泛型接口时,由于擦除,可能会生成合成方法以在采用特定参数/返回的方法与用 Objects 定义的超类/接口之一之间进行桥接。根据 Java 语言规范,桥接方法将调用重定向到实际方法。但是桥接方法缺少为原始方法及其参数定义的注释。

理由: 尝试在运行时检索此类方法的注释时会出现问题。由于不可能可靠地找出哪些类替代了泛型参数,因此我们不知道将什么参数发送到 getMethod(...) 以接收正确的方法。发送 Object.class 时(而泛型参数是不同的类)getMethod 将返回桥接方法,该桥接方法不会包含有关原始方法注释的信息。

它也记录在JDK 8 compatibility guide:

区域:工具/javac

概要
从这个版本开始,参数和方法注释被复制到合成桥方法。这个修复意味着现在对于像这样的程序:

@Target(value = {ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@interface ParamAnnotation {}
 
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MethodAnnotation {}
 
abstract class T<A,B> {
    B m(A a){return null;}
 }
 
 class CovariantReturnType extends T<Integer, Integer> {
     @MethodAnnotation
     Integer m(@ParamAnnotation Integer i) {
         return i;
     }
 
    public class VisibilityChange extends CovariantReturnType {}
 
}

每个生成的桥接方法都将具有它重定向到的方法的所有注释。参数注释也将被复制。行为的这种变化可能会影响某些注释处理器或一般来说任何使用注释的应用程序。

不兼容的性质
行为

RFE
6695379

【讨论】:

  • @kapep,只是好奇,您是如何发现这是特定的变更集?
  • @vrz 花了一些时间才找到它 :) 更改日志没有包含太多关于注释的内容,也不够详细。字节码中的标志ACC_BRIDGE, ACC_SYNTHETIC 让我很好奇,它们似乎很相关。我想我用谷歌搜索了类似java 8 annotations on synthetic methods.
  • @kappep,我明白了,谢谢你的回答。我是您在回答中提到的变更集的作者。我想知道如何减轻为外部用户查找给定变更集的任务。似乎向变更集/代码添加更多 cmets 可能会有所帮助,但我愿意接受建议/想法。
  • @vrz 只要知道要搜索什么,我认为没问题。对于这个特定问题,我希望 JDK 8 兼容性指南更加突出,但在 Jaylan 将其添加到我的答案之前我从未听说过。
【解决方案2】:

@kapep:非常好的答案。我想补充一些关于这个问题的补充信息。不仅注释被复制到桥接方法。参数名称也被复制。如果您使用 -parameters 编译器选项编译示例,您将获得以下 javap 输出:

public int compareTo(Customer);
descriptor: (LCustomer;)I
flags: ACC_PUBLIC
Code:
  stack=1, locals=2, args_size=2
     0: iconst_0      
     1: ireturn       
  LineNumberTable:
    line 11: 0
MethodParameters:
  Name                           Flags
  o                              
RuntimeVisibleAnnotations:
  0: #15()

public int compareTo(java.lang.Object);
descriptor: (Ljava/lang/Object;)I
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
  stack=2, locals=2, args_size=2
     0: aload_0       
     1: aload_1       
     2: checkcast     #2                  // class Customer
     5: invokevirtual #3                  // Method compareTo:(LCustomer;)I
     8: ireturn       
  LineNumberTable:
    line 7: 0
MethodParameters:
  Name                           Flags
  o                              synthetic  <-- see the name copied to the bridge method
RuntimeVisibleAnnotations:
  0: #15()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多