Interfaz de anotación de Java

  • PD: este blog implicará algún conocimiento de Google Guice, pero no se asuste, incluso si nunca conoce Guice, puede leer este blog normalmente.

1. Preámbulo

  • Al aprender las anotaciones de Java , se mencionó que todas las anotaciones heredarán java.lang.annotation.Annotationla interfaz y no pueden heredar otras clases o implementaciones.
  • Cuando realmente se usan anotaciones en profundidad, es muy necesario encontrar que el sistema aprende la interfaz de Anotación
  • Por ejemplo, la interfaz de anotación define sus propios equals()métodos hashCode()y toString()tiene su propio conjunto de reglas para la implementación de estos métodos.
  • Si no se reescriben los métodos relacionados de acuerdo con estas reglas, pueden surgir problemas con el uso de objetos de anotación.

2. Métodos importantes de la interfaz de anotación

  • Lea atentamente el código fuente de la interfaz de anotación, las anotaciones de clase son las siguientes, la información clave: todas las anotaciones heredarán la interfaz de anotación

    La interfaz común extendida por todos los tipos de anotación. Tenga en cuenta que una interfaz que amplía manualmente esta no define un tipo de anotación. También tenga en cuenta que esta interfaz en sí misma no define un tipo de anotación.

  • La interfaz de anotación define sus propios métodos, incluidos los métodos equals(), hashCode() y toString()

    public interface Annotation {
          
          
        boolean equals(Object obj);
        int hashCode();
        String toString();
        Class<? extends Annotation> annotationType();
    }
    
  • Los tres métodos anteriores también existen en la clase Object, pero los dos tienen restricciones diferentes en estos tres métodos.

2.1 método toString()

  • El método toString() de la clase Object devuelve una cadena que puede representar un objeto. La implementación predeterminada es:class_name@16进制_hashcode

    public String toString() {
          
          
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    

  • El método toString() de la interfaz Annotation devuelve un carácter que representa la anotación

  • @annotation_class_name(memver1=value1,member2=value2, ...)formato de uso común

  • Use Guice para personalizar anotaciones vinculantes para definir múltiples elementos@MultiMember

    @Retention(RUNTIME)
    @Target({
          
          ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
    @Qualifier
    public @interface MultiMember {
          
          
        String name();
    
        int version();
    }
    
  • El código real del método toString() de @MultiMember es el siguiente:

    public String toString() {
          
          
        return "@" + MultiMember.class.getName() + "(" + Annotations.memberValueString("name", name) + ", "
                + Annotations.memberValueString("version", version) + ")";
    }
    
  • Usando @MultiMember en @MultiMember(name="lucy", version=9)el camino, Guice imprimirá la información de la anotación cuando el programa se ejecute mal:@org.sunrise.binding.MultiMemberBinding(name=lucy, version=9)

2.2 método igual ()

  • Object estipula que el método equals() se usa para juzgar si dos objetos son equivalentes y requiere que su implementación tenga cinco características: reflexividad, simetría, transitividad, consistencia y no vacío.

  • Se implementa el método equals() de Object, utilizando el estándar más alto de igualdad de objetos: el mismo objeto

    public boolean equals(Object obj) {
          
          
        return (this == obj);
    }
    
    

  • El método equals() de la interfaz de anotación se utiliza para determinar si los objetos de dos tipos de anotación son iguales.
  • El método equals() devuelve verdadero: el objeto especificado (obj) y el objeto actual (this) pertenecen al mismo tipo de anotación, y todos los elementos (variables miembro) en los dos objetos son correspondientemente iguales,
  • ¿Cómo comparar elementos para la igualdad?
    • Los tipos de datos básicos, excepto float y double, se utilizan x == ypara juzgar
    • Los tipos flotante y doble deben juzgarse por el tipo de empaque, por ejemplo, el tipo doble debe Double.valueOf(x).equals(Double.valueOf(y))juzgarse usando
    • Cadena, clase, enumeración, tipo de anotación, uso x.equals(y)para juzgar
    • Tipo de matriz, uso Arrays.equals(x, y)para juzgar

2.3 método hashCode()

  • El método hashCode() de la clase Object devuelve el código hash del objeto

  • La clase Object requiere que el método hashCode() sea idempotente y cumpla con las siguientes dos convenciones:

    • Dos objetos son iguales, el código hash es igual
    • El código hash es igual y los dos objetos no son necesariamente iguales
  • El método hashCode() de la clase Object es un método nativo que convierte la dirección de memoria del objeto en un número entero para garantizar que los diferentes objetos tengan diferentes códigos hash.

    public native int hashCode();
    

  • El método hashCode() de la interfaz Annotation devuelve el código hash de la anotación

  • El código hash de la anotación es la suma de los códigos hash de cada elemento de la anotación, a sabersum(member_hashcode)

  • El código hash de cada elemento:(127 * "member_name".hashCode()) ^ member_value_hashcode

    • nombre_miembro es el nombre del elemento, que es de tipo Cadena
    • El código hash de member_value depende del tipo de elemento:
      • Tipo de datos básico, use su clase contenedora para calcular el código hash,WrapperType.valueOf(v).hashCode()
      • Los tipos de cadena, clase, enumeración y anotación, cuyo valor es v, usan v.hashCode().el código hash para calcular
      • Tipo de matriz, llame a su valor Arrays.hashCode()para calcular el código hash
  • El código real del método hashCode() de @MultiMember es el siguiente:

    public int hashCode() {
          
          
        return ((127 * "name".hashCode()) ^ name.hashCode())
                + ((127 * "version".hashCode()) ^ Integer.valueOf(version).hashCode());
    }
    
  • Aviso:

    • Al calcular la suma de los códigos hash de varios elementos, asegúrese de agregar la expresión de cálculo del código hash de cada elemento ()y luego sume.
    • De lo contrario, el código hash calculado no cumple con los requisitos de Anotación, lo que provocará que los objetos del tipo de anotación sean iguales y el juicio falle.

2.4 método annotationType()

  • En la clase Object, no hay método annotationType()
  • El método annotationType() de la interfaz Annotation se utiliza para devolver el tipo de anotación
  • La anotación es en realidad una interfaz y la clase se puede definir para implementar la anotación. El método annotationType() de estas clases de implementación generalmente devuelve el tipo de la anotación implementada
  • Por ejemplo, MultiMemberImpl implementa @MultiMember y el tipo de anotación devuelto es MultiMember
    @Override
    public Class<? extends Annotation> annotationType() {
          
          
        return MultiMember.class;
    }
    

3. implementa la anotación

  • En muchos escenarios, simplemente definimos anotaciones y luego las usamos directamente en algunos lugares.
  • Para aprender a personalizar las anotaciones , puede comprender la definición de anotaciones según la definición de interfaces. Por ejemplo, el estudio comparativo de la declaración del elemento de la anotación y la declaración del método en la interfaz
  • Además, no tenemos un conocimiento profundo de las anotaciones como interfaz.
  • Esta sección presentará cómo implementar anotaciones. Por un lado, puede aprender cómo implementar anotaciones de acuerdo con los requisitos en Anotación. Por otro lado, le ayudará a comprender el hecho de que las anotaciones son una interfaz.

3.1 Implementación de anotaciones imperfectas

3.1.1 IDE introduce automáticamente métodos que necesitan ser reescritos

  • Cree MultiMemberImpluna clase como la clase de implementación de @MultiMember

  • En este momento, el IDE le indicará que hay un método que debe reescribirse

  • Introduzca estos métodos que deben reescribirse a través del IDE, y el código formado es el siguiente:

    public class MultiMemberImpl implements MultiMember{
          
          
        @Override
        public String name() {
          
          
            return null;
        }
    
        @Override
        public int version() {
          
          
            return 0;
        }
    
        @Override
        public Class<? extends Annotation> annotationType() {
          
          
            return null;
        }
    }
    
  • Al observar el código de bytes de @MultiMember, podemos ver que el compilador genera automáticamente métodos de captación abstractos para los dos elementos de @MultiMember

  • Por lo tanto, el IDE agrega automáticamente name()métodos version()para que MultiMemberImpl implemente la reescritura de métodos.

  • Al mismo tiempo, según la relación de herencia, MultiMemberImpl también necesita reescribir los métodos en la interfaz de anotación (cuatro en total), pero el IDE solo introduce uno de los métodos para annotationType()reescribir

3.1.2 ¿Por qué faltan otros métodos?

  • Los tres métodos restantes en Anotación, equals(), hashCode() y toString(), son exactamente iguales a los métodos de la clase Object.
  • Object es la clase base de todas las clases de Java, y MultiMemberImpl heredará automáticamente estos tres métodos de Object
  • Los tres métodos heredados simplemente se convierten en los métodos de reescritura de los tres métodos en Anotación. El método se ha reescrito y el IDE no importará automáticamente estos métodos

3.1.3 Mejorar la lógica del método de reescritura

  • Basado en el código anterior, mejore la lógica del método de reescritura

    public class MultiMemberImpl implements MultiMember {
          
          
        private final String name;
        private final int version;
    
        public MultiMemberImpl(String name, int version) {
          
          
            this.name = name;
            this.version = version;
        }
    
        @Override
        public String name() {
          
          
            return this.name;
        }
    
        @Override
        public int version() {
          
          
            return this.version;
        }
    
        @Override
        public Class<? extends Annotation> annotationType() {
          
          
            return MultiMember.class;
        }
    }
    
  • Escriba el método main() para usar MultiMemberImpl y verifique que MultiMemberImpl herede los tres métodos de Object

    public static void main(String[] args) {
          
          
        MultiMemberImpl a = new MultiMemberImpl("jdk", 9);
        MultiMemberImpl b = a;
        MultiMemberImpl c = new MultiMemberImpl("jdk", 9);
        System.out.printf("a.toString(): %s\n", a);
        System.out.printf("a.equals(b): %b, a.equals(c): %b\n", a.equals(b), a.equals(c));
        System.out.printf("a.hash_code: %d, c.hash_code: %d\n", a.hashCode(), c.hashCode());
    }
    
  • Los resultados de la ejecución son los siguientes, se puede encontrar que equals(), hashCode() y toString() de MultiMemberImpl heredan Object

3.1.4 Problemas existentes

  • Tal MultiMemberImpl es completamente suficiente en el escenario de prueba
  • En escenarios de aplicaciones reales, debido a que los métodos equals(), hashCode() y toString() no se reescriben de acuerdo con las normas de Annotation, la ejecución normal del programa se verá afectada.
  • Por ejemplo, simule la implementación de @Named en Guice y personalice un @Binding.Si el método anterior no se reescribe correctamente, @Binding(name="database") no podrá coincidir con el enlace Bindings.bind("database")definido .
  • Los lectores interesados ​​pueden leer "Google Guice 3: Bindings"

3.2 Implementación correcta de las anotaciones

  • Para cumplir con el escenario de la aplicación real, es necesario reescribir los métodos equals(), hashCode() y toString() de acuerdo con las regulaciones en Anotación

    @Override
    public int hashCode() {
          
          
        return ((127 * "name".hashCode()) ^ name.hashCode())
                + ((127 * "version".hashCode()) ^ Integer.valueOf(version).hashCode());
    }
    
    @Override
    public boolean equals(Object obj) {
          
          
        if (!(obj instanceof MultiMember)) {
          
          
            return false;
        }
        MultiMember other = (MultiMember) obj;
        return this.name.equals(other.name()) && this.version == other.version();
    }
    
    @Override
    public String toString() {
          
          
        return "@" + MultiMember.class.getName() + "(" + Annotations.memberValueString("name", name) + ", "
                + Annotations.memberValueString("version", version) + ")";
    }
    
  • Vuelva a ejecutar el método main() y el resultado de la ejecución cambiará

Supongo que te gusta

Origin blog.csdn.net/u014454538/article/details/129116171
Recomendado
Clasificación