Hace un tiempo escribí esta respuesta a una pregunta sobre cómo evitar tener un getter y setter para cada variable mutable. En ese momento, solo tenía un presentimiento difícil de verbalizar de que esta es una mala idea, pero OP estaba preguntando explícitamente cómo hacerlo. Busqué aquí por qué esto podría ser un problema, y encontré esta pregunta , cuyas respuestas parecen dar por sentado que usar la reflexión a menos que sea absolutamente necesario es una mala práctica.
Entonces, ¿puede alguien verbalizar por qué es un hecho que se debe evitar la reflexión, específicamente en el caso del getter y setter genérico?
java
reflection
HAEM
fuente
fuente
Map
'sget
yput
, lo que es una manera bastante torpe de hacer las cosas.Respuestas:
Desventajas de la reflexión en general.
La reflexión es más difícil de entender que el código de línea recta.
En mi experiencia, la reflexión es una característica de "nivel experto" en Java. Yo diría que la mayoría de los programadores nunca usan la reflexión activamente (es decir, el consumo de bibliotecas que usan la reflexión no cuenta). Eso hace que el uso del código sea más difícil de entender para estos programadores.
El código de reflexión es inaccesible para el análisis estático
Supongamos que tengo un captador
getFoo
en mi clase y quiero cambiarle el nombregetBar
. Si no utilizo ningún reflejo, simplemente puedo buscar la base del códigogetFoo
y encontraré todos los lugares que usan el captador para poder actualizarlo, e incluso si me pierdo uno, el compilador se quejará.Pero si el lugar que usa el captador es algo así
callGetter("Foo")
y locallGetter
hacegetClass().getMethod("get"+name).invoke(this)
, entonces el método anterior no lo encontrará y el compilador no se quejará. Solo cuando el código se ejecute realmente obtendrá unNoSuchMethodException
. E imagine el dolor en el que está sufriendo si se traga esa excepción (que se rastrea)callGetter
porque "solo se usa con cadenas codificadas, en realidad no puede suceder". (¿Nadie haría eso, alguien podría discutir? Excepto que el OP hizo exactamente eso en su respuesta SO. Si se cambia el nombre del campo, los usuarios del setter genérico nunca lo notarían, excepto por el error extremadamente oscuro del setter que no hace nada en silencio. Los usuarios del getter podrían, si tienen suerte, notar la salida de la consola de la excepción ignorada).El compilador no verifica el código de reflexión
Esto es básicamente un gran subpunto de lo anterior. El código de reflexión se trata
Object
. Los tipos se verifican en tiempo de ejecución. Los errores se descubren mediante pruebas unitarias, pero solo si tiene cobertura. ("Es solo un captador, no necesito probarlo"). Básicamente, pierdes la ventaja de usar Java sobre Python que obtuviste en primer lugar.El código de reflexión no está disponible para la optimización
Tal vez no en teoría, pero en la práctica, no encontrará una JVM que alinee o cree una caché en línea
Method.invoke
. Las llamadas a métodos normales están disponibles para tales optimizaciones. Eso los hace mucho más rápidos.El código de reflexión es lento en general
La búsqueda dinámica de métodos y la verificación de tipos necesarios para el código de reflexión es más lenta que las llamadas a métodos normales. Si convierte ese captador de una línea barato en una bestia reflectante, podría (no he medido esto) estar observando varios órdenes de magnitud de desaceleración.
Desventaja de getter / setter genérico específicamente
Esa es solo una mala idea, porque su clase ahora ya no tiene encapsulación. Cada campo que tiene es accesible. También podrías hacerlos públicos.
fuente
Porque los getter / setters de paso son una abominación que no proporciona ningún valor. No proporcionan ninguna encapsulación ya que los usuarios pueden obtener los valores reales a través de las propiedades. Son YAGNI porque rara vez cambiará la implementación de su almacenamiento de campo.
Y porque esto es mucho menos eficiente. Al menos en C #, un captador / definidor se alineará directamente a una sola instrucción CIL. En su respuesta, está reemplazando eso con 3 llamadas a funciones, que a su vez necesitan cargar metadatos para reflexionar. En general, he usado 10x como regla general para los costos de reflexión, pero cuando busqué datos, una de las primeras respuestas incluyó esta respuesta que se acercó a 1000x.
(Y hace que su código sea más difícil de depurar, y perjudica la usabilidad porque hay más formas de mal uso, y perjudica la capacidad de mantenimiento porque ahora está utilizando cadenas mágicas en lugar de identificadores adecuados ...)
fuente
getX()
ysetX()
métodos que se pueden encontrar a través de la reflexión. No necesariamente se supone que los frijoles encapsulan sus valores, podrían ser DTO. Entonces, en Java, los captadores a menudo son un dolor necesario. Las propiedades de C # son mucho, mucho mejores en todos los aspectos.Además de los argumentos ya presentados.
No proporciona la interfaz esperada.
Los usuarios esperan llamar a foo.getBar () y foo.setBar (valor), no a foo.get ("bar") y foo.set ("bar", valor)
Para proporcionar la interfaz que los usuarios esperan que necesite, escriba un getter / setter separado para cada propiedad. Una vez que haya hecho eso, no tiene sentido usar la reflexión.
fuente
Por supuesto, no es una mala idea, es solo que en un mundo java normal es una mala idea (cuando solo quieres un getter o setter). Por ejemplo, algunos frameworks (spring mvc) o librares ya lo usan (jstl) de esa manera, algunos analizadores json o xml lo están usando, si quieres usar un DSL lo vas a usar. Entonces depende de su escenario de caso de uso.
Nada es correcto o incorrecto como un hecho.
fuente