Java 带注释的反射 Class 不工作

Java Reflection with Annotation Class not working

我对 Java 非常有经验,但是使用反射和注释 classes 是个新手,我正在努力学习以获取乐趣。为了进行一些练习,我制作了一个 Identifiable class,旨在为它继承的任何 class 添加几个有用的方法。

这里是完整的 class:

abstract class Identifiable<T, K extends Comparable<K>> implements Comparable<Identifiable<T, K>> {

    @Retention(RetentionPolicy.RUNTIME)
    public @interface Identifier { }

    private static Method getMethodAnnotatedWith(final Class<?> type) {
        return Arrays.stream(type.getDeclaredMethods())
                .filter(m -> m.isAnnotationPresent(Identifier.class))
                .findFirst()
                .orElse(null);
    }

    private K id;

    @SuppressWarnings("unchecked")
    public Identifiable(Class<T> clazz) {
        var m = getMethodAnnotatedWith(clazz);
        if (m == null) throw new IllegalArgumentException(
            clazz.toString() + " does not have a method annotated by @Identifier"
        );

        try {
            id = (K) m.invoke(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public int compareTo(@NotNull Identifiable<T, K> i) {
        return id.compareTo(i.id);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Identifiable<?, ?> that = (Identifiable<?, ?>) o;
        return id == that.id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}

下面是我尝试设计它的方式:

class Foo extends Identifiable<Foo, Integer> {
    private final int i;

    Foo(int i) {
        super(Foo.class);
        this.i = i;
    }

    @Identifier
    int getI() {
        return i;
    }
}

但是,由于某些原因,id 总是 0,所以我不确定这是我的 Identifier 注释 class 还是我的方式有问题'使用反射。我很确定它是后者,因为在调试时,我发现它能够访问带有注释的方法。任何帮助将不胜感激,谢谢!

构造时不要调用注解方法

如果标识符值是不可变的(final),只需将值传递给超级构造函数。

public Identifiable(K id) {
    this.id = id;
}
Foo(int i) {
    super(i);
    this.i = i;
}

如果标识符值是可变的,则需要更改逻辑以在需要该值时调用该方法,而不是在构造过程中缓存该值。

abstract class Identifiable<T, K extends Comparable<K>> implements Comparable<Identifiable<T, K>> {

    @Retention(RetentionPolicy.RUNTIME)
    public @interface Identifier {/**/}

    private Method idGetter;

    protected Identifiable(Class<T> type) {
        this.idGetter = Arrays.stream(type.getDeclaredMethods())
                .filter(m -> m.isAnnotationPresent(Identifier.class))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException(type.getName() + " does not have a method annotated by @Identifier"));
    }

    @SuppressWarnings("unchecked")
    private final K getIdentifiableKey() {
        try {
            return (K) this.idGetter.invoke(this);
        } catch (IllegalAccessException e) {
            throw new IllegalAccessError(e.getMessage());
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int compareTo(Identifiable<T, K> that) {
        return this.getIdentifiableKey().compareTo(that.getIdentifiableKey());
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        
        Identifiable<?, ?> that = (Identifiable<?, ?>) o;
        return this.getIdentifiableKey().equals(that.getIdentifiableKey()); // Call equals(), don't use ==
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.getIdentifiableKey());
    }
}

或者,使用功能接口并为其提供方法参考。

abstract class Identifiable<T extends Identifiable<T, K>, K extends Comparable<K>> implements Comparable<Identifiable<T, K>> {

    private Function<T, K> idGetter;

    protected Identifiable(Function<T, K> idGetter) {
        this.idGetter = Objects.requireNonNull(idGetter);
    }

    @Override
    @SuppressWarnings("unchecked")
    public int compareTo(Identifiable<T, K> that) {
        return this.idGetter.apply((T) this).compareTo(that.idGetter.apply((T) that));
    }

    @Override
    @SuppressWarnings("unchecked")
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        
        Identifiable<T, K> that = (Identifiable<T, K>) o;
        return this.idGetter.apply((T) this).equals(that.idGetter.apply((T) that));
    }

    @Override
    @SuppressWarnings("unchecked")
    public int hashCode() {
        return Objects.hash(this.idGetter.apply((T) this));
    }
}
class Foo extends Identifiable<Foo, Integer> {
    private final int i;

    Foo(int i) {
        super(Foo::getI);
        this.i = i;
    }

    int getI() {
        return i;
    }
}