#java #reflection #annotations #runtime #java-annotations
#java #отражение #аннотации #время выполнения #java-аннотации
Вопрос:
У меня есть несколько классов A1, A2, A3, которые расширяют абстрактный класс myA. Эти классы имеют x полей класса B. Поля класса B аннотируются с помощью теста аннотации.(Тест доступен во время выполнения) Как я могу получить тест аннотации и его значение изнутри метода класса B.
public class A1 extends myA{
@Test("all")
private B b1;
@Test("none")
private B b2;
@Test("none")
private B b3;
//....
public void interact(){
b2.doSomethingBasedOnMyAnnotation();
}
}
public class A2 extends myA{
@Test("none")
private B b;
//....
}
public class B{
public void doSomethingBasedOnMyAnnotation(){
// How to reach the Annotation from here ?
}
}
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD)
public @interface Test{
String value() default "all";
}
Ответ №1:
Когда вы помещаете аннотацию в переменную, она становится статическим свойством этой переменной, а не объекта, к которому вы можете получить доступ через эту переменную. Рассмотрим:
public class A1 extends myA{
@Test("all")
private B b1=new B();
@Test("none")
private B b2=b1;
//....
public void interact(){
// this
b2.doSomethingBasedOnMyAnnotation();
// is exactly the same as
b1.doSomethingBasedOnMyAnnotation();
}
}
Неверно даже предполагать, что задействована аннотированная переменная. Как насчет
new B().doSomethingBasedOnMyAnnotation()
?
Поскольку поля разрешаются во время компиляции, в любом случае в вашей желаемой операции нет абстракции. Если вы знаете, что собираетесь вызывать, b2.doSomethingBasedOnMyAnnotation();
, вы уже знаете, какое поле вы используете, и нет проблем с предоставлением значения аннотации b2
в качестве параметра вызываемому методу, вместо того, чтобы ожидать, что получатель волшебным образом узнает. Например.
public class B{
public void doSomething(String arg){
}
}
public class A1 extends myA{
@Test("all")
private B b1;
@Test("none")
private B b2;
//....
public void interact(){
b1.doSomething(get("b1"));
b2.doSomething(get("b2"));
}
static String get(String fieldName) {
try {
return A1.class.getDeclaredField(fieldName)
.getAnnotation(Test.class).value();
} catch(NoSuchFieldException ex) {
throw new IllegalStateException(ex);
}
}
}
хотя мы могли бы с радостью работать без отражения:
public class A1 extends myA{
static final String TEST_B1="all";
@Test(TEST_B1)
private B b1;
static final String TEST_B2="none";
@Test(TEST_B2)
private B b2;
static final String TEST_B3="none";
@Test(TEST_B3)
private B b3;
//....
public void interact(){
b1.doSomething(TEST_B1);
b2.doSomething(TEST_B2);
}
}
Если вы хотите убедиться, что вызывающий объект не сможет случайно передать неправильный аргумент, вместо этого используйте инкапсуляцию:
public final class EncapsulatedB {
final String testValue;
B b;
EncapsulatedB(String value) {
this(value, null);
}
EncapsulatedB(String value, B initialB) {
testValue=value;
b=initialB;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
public void doSomething() {
b.doSomething(testValue);
}
}
public class A1 extends myA{
private final EncapsulatedB b1=new EncapsulatedB("all");
private final EncapsulatedB b2=new EncapsulatedB("none");
private final EncapsulatedB b3=new EncapsulatedB("none");
//....
public void interact(){
b1.doSomething();
b2.doSomething();
}
}