Java8 - аннотирование compareTo <T> of Comparable <T> добавляет аннотации к compareTo (Object o)
У меня есть аннотация
package javaannotationtest;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
}
Это применяется для сравнения в следующем классе
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)
Здесь выводится из javap для версии, скомпилированной с Java 8 (вероятно, не актуально, она показывает аннотацию, добавленную к обоим методам)
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? (Будет предлагать Bounty, когда он станет подходящим).
Ответы
Ответ 1
Это изменение описано в выпуске JDK-6695379 - Копировать аннотации методов и аннотации параметров к синтетическим моделям. По-видимому, не так много обсуждений этой функции, но запрос и обоснование имеют смысл для меня.
A ОПИСАНИЕ ЗАЯВКИ: Когда класс расширяет общие классы или реализует общий интерфейс, синтетический метод может быть сгенерирован для соединения между методом, принимающим конкретные параметры /return, и одним из суперкласса/интерфейса, который определен объектами из-за стирания. Метод моста перенаправляет вызов на фактический метод, в соответствии с Спецификацией языка Java. Однако в методе моста отсутствуют аннотации, определенные для исходного метода и его параметров.
ОБОСНОВАНИЕ: Проблема возникает при попытке получить аннотации такого метода во время выполнения. Поскольку невозможно достоверно узнать, какие классы заменяют общие параметры, мы не знаем, какой параметр следует отправить getMethod (...), чтобы получить правильный метод обратно. При отправке Object.class(в то время как общий параметр является другим классом) getMethod вернет метод bridge, который не будет иметь информацию об аннотациях исходного метода.
Это также описано в Руководстве по совместимости JDK 8:
Область: Инструменты /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
Ответ 2
@kapep: очень хороший ответ. Я хотел бы добавить дополнительную информацию об этой проблеме. К мостовому методу копируются не только аннотации. Имена параметров также скопированы. Если вы скомпилируете пример с параметром -parameters compiler, вы получите этот выход 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()