examples - try with resources java
¿Anotaciones en variables declaradas en try-with-resources? (2)
El único tipo de anotaciones que puede aplicar a una variable local (incluida una asignación de variable dentro de un bloque try-with-resources de prueba) son las que tienen @Target({ElementType.LOCAL_VARIABLE})
. Y esos no son accesibles en tiempo de ejecución (a través de la reflexión), por lo que solo puede ser accedido por el compilador.
Ejemplos cuando podrían ser útiles:
-
@SuppressWarnings("unchecked")
- si tiene una tarea sin marcar dentro de untry(...)
- Uso de anotaciones JSR 305 (Anotaciones para detección de defectos de software), como
@Nullable
,@Nonnull
Solo me pregunto qué anotaciones podrían usarse con las variables declaradas en las declaraciones de prueba con recursos , que se permiten según su gramática. La sección 14.20.3 de la especificación de lenguaje (Java 7) lee,
TryWithResourcesStatement:
try
ResourceSpecification Bloque Capturas opcion Finalmente opteEspecificación de recursos:
(
Recursos;
opt)
Recursos:
Recurso de recursos;
RecursosRecurso:
VariableModifiers opt Type VariableDeclaratorId=
Expression
Y VariableModifiers se expande como (sección 14.4 ),
VariableModificadores:
Modificador de variable
VariableModifiers VariableModifierVariableModifier: uno de
Anotaciónfinal
Ahí tienes: VariableModifier puede tener Anotación . Bueno, eso básicamente significa que podemos escribir algo como esto:
try( @SomeAnnotation SomeType obj = createSomeType() ) {
//some code
}
Entonces, mi pregunta es: ¿cómo y qué tipo de anotaciones podrían usarse para probar con recursos y para lograr qué tipo de comportamiento? ¿Alguna idea innovadora? ¿Alguien los ha usado?
No en Java 7, pero sospecho que etiquetó este java-7
solo porque esa es la versión que introdujo try-with-resources, y todavía está interesado en posibles usos más allá de Java 7 (creo que esta pregunta es muy interesante para Java> = 8).
Creo que no hay nada especial que vincule el intento con recursos y las anotaciones, no es un caso especial en la gramática; a este respecto, tales variables (declaradas dentro de una declaración de prueba con recursos) son exactamente iguales a otras variables locales, y la gramática también permite anotaciones:
- Java 7 ha introducido declaraciones de prueba con recursos, en las que puede declarar una variable que recibirá un tratamiento especial .
- La gramática ha permitido anotaciones en declaraciones de variables locales desde Java 5, cuando se introdujeron las anotaciones (pero tuvimos que esperar a Java 6 para obtener una API utilizable para el procesamiento de anotaciones)
- Pero incluso con Java 7 no fue posible para los procesadores de anotaciones acceder a las anotaciones en variables locales. La única anotación en la variable local que era "utilizable" fue
@SuppressWarnings
pero esa fue tratada especialmente por el compilador en sí, no había forma de engancharte a esto. - Java 8 introdujo un nuevo tipo de contexto de anotación además del "contexto de declaración", ahora hay "contexto de tipo" , y ahora un
Target
anotación puede serElementType.TYPE_USE
Entonces, la respuesta (con Java 8) es la misma que con cualquier anotación en las variables locales .
(Algunas curiosidades sobre las nuevas "anotaciones de tipo" de Java 8)
... y aquí es donde se vuelve interesante: anotar cualquier tipo de uso !
Las ubicaciones sintácticas donde pueden aparecer las anotaciones se dividen en contextos de declaración , donde las anotaciones se aplican a las declaraciones y los contextos de tipo , donde las anotaciones se aplican a los tipos utilizados en las declaraciones y declaraciones.
Dichas anotaciones no se retienen en tiempo de ejecución, pero se pueden usar en tiempo de compilación para una variedad de "cheques". Consulte el marco de verificación , que se basa en el trabajo realizado para JSR-308 (por el mismo autor si lo entiendo correctamente).
Muy rápido porque es divertido, ahora podemos hacer esto:
@NonNull Object @Nullable [] array; // Nullable array of non-null objects
@Nullable Object @NonNull [] array; // Non-null array of nullable objects
@Foo List<@Foo Integer> doSomething(@Foo Integer @Foo [] arrayOfIntegers, @Foo long x) {
arrayOfIntegers[0] = (@Foo int) x;
return Arrays.asList(arrayOfIntegers);
}
Ejemplos de tales "anotaciones de tipo" :
Checker Framework proporciona algunas anotaciones de tipo que podrían beneficiar a los desarrolladores de bibliotecas y aplicaciones, como:
@NonNull
: el compilador puede determinar los casos en los que una ruta de código puede recibir un valor nulo, sin tener que depurar una NullPointerException.
@ReadOnly
: el compilador marcará cualquier intento de cambiar el objeto. Esto es similar a Collections.unmodifiableList, pero más general y verificado en tiempo de compilación.
@Regex
: proporciona una verificación en tiempo de compilación de que una cadena que se pretende utilizar como una expresión regular es una expresión regular con el formato correcto.
@Tainted
y@Untainted
: tipos de datos de identidad que no se deben usar juntos, como la entrada de un usuario remoto que se usa en los comandos del sistema o información confidencial en las secuencias de registro.
@m
: las unidades de medida garantizan que los números utilizados para medir objetos se utilicen y comparen correctamente, o que hayan sufrido la conversión de la unidad adecuada.
Pero ninguno de ellos es especialmente útil en el contexto de una declaración try-with-resources (quiero decir, ni más ni menos que en cualquier otro lugar).
Volviendo a la pregunta: ¿existen usos para las anotaciones en las variables locales que serían particularmente interesantes cuando se declaren dentro de una declaración de prueba con recursos?
Creo que en este caso las aplicaciones se limitarían esencialmente a las verificaciones en tiempo de compilación , porque tal anotación estará en la variable local o en el tipo de uso, y ninguna de ellas está disponible en tiempo de ejecución (o no realmente):
- De acuerdo con las anotaciones JLS en las variables locales, no se conservan en la representación binaria.
- las anotaciones sobre los usos de tipo se that , pero aún no están disponibles en tiempo de ejecución por reflexión (¡usted tendría que analizar el archivo de clase usted mismo!)
Por lo tanto, puedo pensar en un uso "especial", pero ni siquiera estoy seguro de que sea muy útil, ya que probablemente haya otras formas de lograrlo: para algunos tipos particulares de recursos que declara en una prueba de recursos. Es posible que deba asegurarse de que el recurso se consume por completo antes de que se cierre (he visto algo así con una biblioteca de cliente HTTP y la parte de la API que lee los encabezados, no puedo recordar los detalles) .
/* Say getResponse() taps into a third-party library that has a quirk:
* a response object must be consumed entirely before being closed. */
try(@MustConsumeEntirely Stream<String> lines = getResponse()) {
lines.findFirst().ifPresent(System.out::println);
/* The stream is not entirely consumed (unless it is only 1 line but there is no way to tell).
* A smart checker could catch this and issue a warning. */
}
Esta anotación tendría un ElementType.LOCAL_VARIABLE
destino (por lo que no requeriría nuevos tipos de anotación de Java 8, pero sí requeriría que Java 8 sea procesable), y el verificador probablemente debería verificar que la variable se declare efectivamente dentro de una declaración try-with-resources ( el compilador no puede evitar usarlo en ninguna variable local) y luego analizar el árbol de origen para determinar si los recursos se consumen según sea necesario.
Probablemente sería imposible implementar un verificador de este tipo de manera correcta al 100%, pero en el papel parece posible verificar algunos patrones mal conocidos, y tendría más sentido cuando la variable de destino se declara en un intento con declaración de recursos.
Otra idea (aún en uso de variables y no de tipo), también es de muy poca utilidad: @MustNotEscape
, si desea controlar que la variable no se pase a otro método porque (por razones similares a las anteriores) desea la capacidad de controlar todo eso le sucede al objeto que está detrás (por ejemplo, como en la idea anterior), y eso sería más difícil de lograr si la variable se pasa alrededor.
Para ilustrar que tal cosa es vagamente posible, aquí hay un ejemplo de un marco que espera que sigas su "DSL incrustado" dentro de un bloque determinado, y fails si fails lo haces. Uno podría imaginar una anotación para ayudar a verificar el cumplimiento de restricciones similares impuestas por un marco hipotético en un recurso dentro de un bloque de prueba con recursos.
Sin embargo, no digo que este sea un buen diseño ... (En el caso de ModelMapper, el DSL era solo un truco inteligente que se les ocurrió antes de Java 8, y ahora tienen soluciones mejores y más seguras con lambdas)