notacion - java annotation type
Uso de la anotación para garantizar que el valor devuelto por el método no se descarte (7)
Descargo de responsabilidad: En realidad, tengo la misma pregunta y todavía no tengo una solución completa. PERO:
Tengo una idea de cómo se podría hacer esto de una manera limpia, que quiero publicar aquí, mientras trato de hacerlo:
Uno puede usar AspectJ para invocar código después de que se haya llamado a un método específico. Por ejemplo
@AfterReturning (pointcut = "call (int Foo.m (int))", regresando = "x") Vacío público doSomething (int x) {...} puede ser usado. El valor de retorno x se pasa a su método.
Su método podría entonces observar el recuento de referencia del valor de retorno. Si el valor de retorno es Garbadge Collected se desechó y podría emitir una advertencia, consulte, por ejemplo, http://java.dzone.com/articles/letting-garbage-collector-do-c
Por supuesto, preferiría una anotación y un soporte de tiempo de compilación para esto, ya que lo anterior es quizás solo adecuado en un entorno de prueba y tal vez no en producción (debido al impacto en el rendimiento).
¿Algún comentario si esto pudiera funcionar?
String
en Java es inmutable. El siguiente fragmento de código es, en términos generales, "incorrecto".
String s = "hello world!";
s.toUpperCase(); // "wrong"!!
System.out.println(s); // still "hello world!"!!!
A pesar de estar "equivocado", el código se compila y se ejecuta, tal vez para la confusión de muchos principiantes, a quienes se les debe decir cuál es el error, o descubrirlo por sí mismos consultando la documentación.
Leer la documentación es una parte esencial de la comprensión de una API, pero me pregunto si esto puede complementarse con controles adicionales de tiempo de compilación. En particular, me pregunto si tal vez el marco de anotación de Java se pueda usar para imponer que el valor devuelto por ciertos métodos no se ignore. Los diseñadores de API / autores de bibliotecas usarían esta anotación en sus métodos para documentar qué valores de retorno no deben ignorarse.
Una vez que la API se complementa con esta anotación (o tal vez con otro mecanismo), cada vez que un usuario escribe un código como el anterior, no compilaría (ni lo haría con una advertencia severa).
Entonces, ¿se puede hacer esto y cómo harías para hacer algo como esto?
Apéndice: La Motivación.
Parece claro que, en el caso general, Java debería permitir que se ignoren los valores de retorno de los métodos. Los valores devueltos de métodos como List.add
( siempre true
), System.setProperty
(valor anterior), probablemente se pueden ignorar de forma segura la mayoría de las veces.
Sin embargo, también hay muchos métodos cuyos valores de retorno NO deben ignorarse. Hacerlo es casi siempre un error del programador o, de lo contrario, no es un uso adecuado de la API. Esto incluye cosas como:
- Métodos en tipos inmutables (por ejemplo,
String
,BigInteger
, etc.) que devuelven el resultado de las operaciones en lugar de mutar la instancia en la que se invoca. - Los métodos cuyo valor de retorno es una parte crítica de su comportamiento y no deben ignorarse, pero a veces la gente lo hace de todos modos (por ejemplo,
InputStream.read(byte[])
devuelve el número de bytes leídos, que NO debe asumirse como la longitud total de la matriz)
Actualmente podemos escribir códigos que ignoran estos valores de retorno y hacer que se compilen y ejecuten sin previo aviso. Los verificadores de análisis estáticos / buscadores de errores / ejecutores de estilo / etc casi seguramente pueden marcarlos como posibles olores de código, pero parecería apropiado / ideal si la API lo impone, quizás a través de anotaciones.
Es casi imposible que una clase se asegure de que siempre se use "correctamente", pero hay cosas que puede hacer para ayudar a guiar a los clientes a un uso adecuado (vea: Efectiva Java 2da edición, Artículo 58: Utilice excepciones marcadas para condiciones recuperables y Excepciones de tiempo de ejecución para errores de programación y el Artículo 62: Documentar todas las excepciones lanzadas por cada método ). Tener una anotación que obligaría a los clientes a no ignorar los valores de retorno de ciertos métodos, y hacer que el compilador los imponga en tiempo de compilación, ya sea en forma de errores o advertencias, parece estar en línea con esta idea.
Apéndice 2: Fragmento
Lo siguiente es un intento preliminar que ilustra sucintamente lo que quiero lograr:
@interface Undiscardable { }
//attachable to methods to indicate that its
//return value must not be discarded
public class UndiscardableTest {
public static @Undiscardable int f() {
return 42;
}
public static void main(String[] args) {
f(); // what do I have to do so this generates
// compilation warning/error?
System.out.println(f()); // this one would be fine!
}
}
El código anterior compila y funciona bien ( como se ve en ideone.com ). ¿Cómo puedo hacer que no sea así? ¿Cómo puedo asignar la semántica que quiero a @Undiscardable
?
En @Deprecated
: ¿le gustaría tener una anotación de tipo @Deprecated
que ayudaría al compilador / IDE a advertir / error cuando se llama al método sin asignar su resultado? No puedes lograr esto sin modificar el código fuente de Java y el compilador. El método particular debe ser anotado y el compilador debe ser consciente de ellos. Sin modificar la fuente y / o el compilador, puede crear al máximo la clase de un complemento / configuración IDE que reconozca los casos y genere un error / advertencia en consecuencia.
Actualización : podría escribir un marco / complemento a su alrededor que verifique el método llamado y los errores correspondientes. Solo le gustaría tener la anotación disponible durante el tiempo de ejecución. Puede hacerlo anotando la anotación usando @Retention
(RetentionPolicy.RUNTIME)
. Luego, puede usar el Method#getAnnotation()
para determinar si esta anotación está disponible. Aquí hay un ejemplo de cómo el marco podría hacer este trabajo:
package com.example;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class Test {
public static void main(String[] args) throws Exception {
if (Test.class.getMethod("f", new Class[0]).getAnnotation(Undiscardable.class) != null) {
System.err.println("You should not discard the return value of f()!");
} else {
f();
}
System.out.println(f());
}
public static @Undiscardable int f() {
return 42;
}
}
@Retention(RetentionPolicy.RUNTIME)
@interface Undiscardable {}
Aún así, para que el compilador haga el trabajo, tienes que trabajar un poco más.
En Android, puede usar @CheckResult
para mostrar una advertencia si no se usa el valor de retorno.
public class ImmutableObject { public final int value; public ImmutableObject(int value) { this.value = value; } @CheckResult public ImmutableObject addOne() { return new ImmutableObject(value + 1); } }
Esto emitirá una advertencia:
ImmutableObject obj = new ImmutableObj(); obj.addOne(); // Warning here ImmutableObject obj2 = obj.addOne(); // No warning
Si usa RxJava, también puede usar @CheckReturnValue
.
No es necesario definir una anotación. Podría definir una regla cuando se invoca un método:
- el método tiene un tipo de retorno nulo;
- el resultado del método se utiliza como argumento para otra invocación de método; o
- El resultado del método se asigna a una variable.
Podría implementar un Procesador que aplique esta regla o implementar un Estilo de verificación que aplique esta regla.
No estoy seguro de la viabilidad, especialmente de manera portátil, pero eche un vistazo a los números romanos, en nuestro Java ( código GitHub ) de Adrian Kuhn . Utilizó el procesamiento de anotaciones Y la API privada de Sun de javac
para agregar literales de números romanos a Java al visitar el código fuente para hacer algún reemplazo .
Tal vez podrías usar un enfoque similar para:
- Encuentra llamadas a tu método anotado en el código fuente.
- verifique si el resultado está asignado (no será fácil IMO)
- generar una advertencia del compilador si no
Y no te pierdas los siguientes recursos de la publicación de Adrian:
También te puede interesar
- Guía de hackers para el compilador de Java por David Erni
- Javac Hacker Resources , una colección de enlaces.
- ¡Cómo reescribir aseveraciones de tal manera que no se puedan desactivar!
Referencia
Preguntas relacionadas
También puede consultar jsr305 que define una anotación @CheckReturnValue . Es compatible con findbugs y genera una advertencia cuando alguien se olvida de manejar el valor de retorno.
Guavas Splitter lo utiliza: http://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/base/Splitter.java
Debo decir que me encantan las anotaciones que pueden guiar el análisis de código estático.
Usted tiene un problema y el problema es que las personas pueden olvidarse por error de utilizar los retornos de los métodos. Al usar las anotaciones, le está diciendo al escritor de la biblioteca que debe ser responsable de recordarles a las personas que llaman que no desechen los resultados de ciertos métodos.
Si bien parece una buena idea, no creo que lo sea. ¿Queremos saturar el código con avisos a los usuarios sobre su mala práctica? Hay muchos productos que analizan el código y le dicen cuándo está haciendo algo incorrecto (o indeseable) como Lint, Sonar e incluso JavaDocs en menor medida.
Si no está de acuerdo con lo que ha dicho el escritor de la biblioteca, ahora se espera que usemos @SuppressWarnings ("devolución descartada").
Si bien esto podría ser útil como ayuda para el aprendizaje, mi punto de vista es más que ver con la separación de preocupaciones que con la ayuda a los programadores novatos. El código (y las anotaciones) en la clase debe estar relacionado con la función de la clase y no establecer la política de cuándo y cómo usar sus métodos.