Вы должны быть осторожны с предположениями о членах скомпилированного класса.
Есть даже сгенерированные компилятором члены, которые являются частью доступного API, такие как конструктор по умолчанию или values()
и valueOf(String)
методы enum
типов. Кроме того, скомпилированные конструкторы внутренних классов и типов enum могут иметь больше параметров, чем видно в исходном коде, и из-за стирания типа сигнатуры в скомпилированных методах могут отличаться от исходного кода.
Кроме того, могут быть разные члены syntheti c. От Java 1.1 до Java 10 вложенные классы могут обращаться к закрытым членам друг друга через вспомогательные методы syntheti c (которые устарели в Java 11). Кроме того, переопределение методов обобщенных c классов или использование ковариантных возвращаемых типов может привести к генерации синтетического c метода моста.
И это еще не все.
Следующая программа
import java.util.Arrays;
import java.util.stream.Stream;
public enum ShowSyntheticMembers {
;
public static void main(String[] args) {
Stream.of(ShowSyntheticMembers.class, Inner.class)
.flatMap(cl -> Stream.concat(Arrays.stream(cl.getDeclaredFields()),
Arrays.stream(cl.getDeclaredMethods())))
.forEach(System.out::println);
}
private boolean x;
class Inner {
protected String clone() {
assert x;
return "";
}
}
}
печатается при компиляции с JDK 11:
private boolean ShowSyntheticMembers.x
private static final ShowSyntheticMembers[] ShowSyntheticMembers.$VALUES
public static void ShowSyntheticMembers.main(java.lang.String[])
public static ShowSyntheticMembers[] ShowSyntheticMembers.values()
public static ShowSyntheticMembers ShowSyntheticMembers.valueOf(java.lang.String)
private static void ShowSyntheticMembers.lambda$main$1(java.io.PrintStream,java.lang.Object)
private static java.util.stream.Stream ShowSyntheticMembers.lambda$main$0(java.lang.Class)
static final boolean ShowSyntheticMembers$Inner.$assertionsDisabled
final ShowSyntheticMembers ShowSyntheticMembers$Inner.this$0
protected java.lang.String ShowSyntheticMembers$Inner.clone()
protected java.lang.Object ShowSyntheticMembers$Inner.clone() throws java.lang.CloneNotSupportedException
при компиляции и запуске с JDK 8 дает
private boolean ShowSyntheticMembers.x
private static final ShowSyntheticMembers[] ShowSyntheticMembers.$VALUES
public static void ShowSyntheticMembers.main(java.lang.String[])
public static ShowSyntheticMembers[] ShowSyntheticMembers.values()
public static ShowSyntheticMembers ShowSyntheticMembers.valueOf(java.lang.String)
static boolean ShowSyntheticMembers.access$000(ShowSyntheticMembers)
private static java.util.stream.Stream ShowSyntheticMembers.lambda$main$0(java.lang.Class)
static final boolean ShowSyntheticMembers$Inner.$assertionsDisabled
final ShowSyntheticMembers ShowSyntheticMembers$Inner.this$0
protected java.lang.String ShowSyntheticMembers$Inner.clone()
protected java.lang.Object ShowSyntheticMembers$Inner.clone() throws java.lang.CloneNotSupportedException
$VALUES
- артефакт сгенерированного компилятором values()
реализации. $assertionsDisabled
часть реализации оператора assert
. this$0
является неявной ссылкой внутреннего класса на его внешний this
. - Метод
clone()
с типом возврата Object
является методом моста. - Метод
access$000
помогает получить доступ к полю private
внешнего класса из внутренний класс, который необходим до JDK 11. - Интересно, что метод syntheti c
lambda$main$1
, который существует только в скомпилированной версии JDK 11, является частью ссылки на метод System.out::println
, но на самом деле здесь не нужно.
Это побочный эффект o Исправление некоторых проблем, связанных с типом пересечений, следовательно, очень специфично для компилятора c. Изменение .flatMap(…)
на .<Object>flatMap(…)
в исходном коде приведет к тому, что метод исчезнет даже с этой указанной c версией компилятора.
Итак, так как множество факторов определяют наличие Синтетические c члены, не видимые в исходном коде, не следует искать конкретный метод, используя только тип параметра в качестве критерия.
Если вы хотите получить доступ к public
членам, лучше использовать Handler.class.getMethods()
вместо Handler.class.getDeclaredMethods()
. Или используйте Handler.class.getMethod("handle", Bar.class)
для непосредственного получения намеченного метода.
Если вы не хотите жестко кодировать имя метода в виде строки, видимая аннотация во время выполнения может помочь определить правильный метод.