Este artículo tiene como objetivo dar una introducción integral al mecanismo de reflexión de Java. Espero que a través de este artículo tenga una comprensión integral del contenido relevante de la reflexión de Java.
Antes de leer este artículo, puede consultar " Reinstanding Java Generics " .
Prefacio
El mecanismo de reflexión de Java es una función muy poderosa. Los reflejos se pueden ver en muchos proyectos a gran escala como Spring y MyBatis. A través del mecanismo de reflexión, podemos obtener información del tipo de objeto durante la operación. Usando esta función, podemos implementar patrones de diseño como el modo de fábrica y el modo proxy, y también podemos resolver problemas angustiantes como Java Generic Erase. En este artículo, aplicaremos el mecanismo de reflexión de Java desde la perspectiva de las aplicaciones prácticas.
Base de reflexión
PD: Este artículo requiere que los lectores tengan un cierto grado de comprensión de la API del mecanismo de reflexión. Si no ha estado expuesto a él antes, se recomienda ver primero el inicio rápido del documento oficial.
Antes de aplicar el mecanismo de reflexión, primero veamos cómo obtener la Class de reflexión correspondiente a un objeto. En Java, tenemos tres formas de obtener la clase de reflexión de un objeto.
Por el método GetClass
En Java, cada Object tiene un método getClass . A través del método GetClass, podemos obtener la clase de reflexión correspondiente de este objeto:
Cadena s = "ziwenxie"; class <?> C = s.getClass ();
También podemos llamar al forName del método estático de Class :
Clase <?> C = class.forname ("java.lang.string"); O podemos usar .class directamente:
Clase <?> C = string.class;
Al comienzo del artículo, mencionamos que uno de los principales beneficios de la reflexión es que nos permite obtener información del tipo de objeto durante la operación. Echemos un vistazo en detalle con un ejemplo.
Primero, creamos una nueva interfaz A bajo typeinfo.interfacea :
paquete typeinfo.interfacea; interfaz pública a {void f (); } Luego creamos una nueva interfaz C en el paquete typeinfo.packageaccess . La interfaz C hereda de la interfaz A , y también creamos varios otros métodos para las pruebas. Tenga en cuenta que los permisos de los siguientes métodos son diferentes.
paquete typeinfo.packageAccess; import typeinfo.interfacea.a; clase C implementa un {public void f () {System.out.println ("public cf ()"); } public void g () {System.out.println ("public cg ()"); } protegido void v () {system.out.println ("protegido cv ()"); } void u () {system.out.println ("paquete cu ()"); } private void w () {system.out.println ("private cw ()"); }} clase pública Hiddenc {public static a makea () {return new c (); }} En callHiddenMethod() , utilizamos varias API nuevas, donde getDeclaredMethod() se usa para obtener un método que la clase de clase se refiere al objeto de acuerdo con el nombre del método, y luego podemos activar los métodos relacionados del objeto llamando invoke() :
paquete typeInfo; import typeinfo.interfacea.a; import typeinfo.packageaccess.hiddenc; import java.lang.reflect.method; public class Hiddenimplementation {public static void main (String [] args) arroja excepción {a a = hiddenc.makea (); af (); System.out.println (a.getClass (). GetName ()); // ¡Ups! La reflexión todavía nos permite llamar a G (): CallHiddenMethod (A, "G"); // ¡e incluso métodos que son menos accesibles! callhiddenmethod (a, "u"); callhiddenmethod (a, "v"); callhiddenmethod (a, "w"); } static void callHiddenMethod (objeto A, string MethodName) lanza la excepción {método g = a.getClass (). getDeclaredMethod (MethodName); G.SetAccessible (verdadero); g.invoke (a); }} A partir de los resultados de la salida, podemos ver que si es public , default , protect o pricate , podemos llamarlo libremente a través de la clase de reflexión. Por supuesto, solo debemos mostrar el poderoso poder de la reflexión, y esta técnica no se recomienda en el desarrollo real.
public cf () typeinfo.packageaccess.cpublic cg () paquete cu () protegido cv () private cw () private cw ()
Tenemos el siguiente escenario comercial. Tenemos una List<Class<? extends Pet>> . Necesitamos contar cuántas Pet específicas hay en esta clase de colección. Debido a Java Generic Erase, definitivamente no es posible prestar atención a la práctica similar a List<? extends Pet> , porque después del compilador realiza verificación de tipo estático, el JVM tratará todos los objetos de la colección como Pet durante la ejecución, pero no sabrá si Pet representa Cat o Dog , por lo que la información de tipo del objeto realmente se pierde durante la ejecución. PD: Acerca de la borrado genérico: tengo una explicación detallada en el artículo anterior. Los amigos interesados pueden echar un vistazo.
Para implementar nuestro ejemplo anterior, primero definimos varias clases:
PET de clase pública extiende individual {PET public (nombre de cadena) {super (nombre); } public Pet () {super (); }} public class Cat extiende PET {public Cat (nombre de cadena) {super (nombre); } public Cat () {super (); }} Public Class Dog extiende PET {Public Dog (nombre de cadena) {super (nombre); }} clase pública Egipcio Egipto extiende Cat {public egipctianmau (nombre de cadena) {super (nombre); } public egyptianmau () {super (); }} Mutt de clase pública extiende el perro {Mutt public (nombre de cadena) {super (nombre); } public Mutt () {super (); }} Pet anterior hereda del Individual . La implementación de Individual es un poco más complicada. Implementamos Comparable y redefinimos las reglas de comparación de clases. Si no lo entendemos muy bien, no importa. Lo hemos abstraído, por lo que no importa si no entendemos el principio de implementación.
implementaciones individuales de clase pública comparables <individual> {contador largo estático privado = 0; ID de larga final privada = contador ++; nombre de cadena privada; // El nombre es opcional público público (nombre de cadena) {this.name = name; } público individual () {} public string toString () {return getClass (). getSimplename () + (name == null? "": "" + nombre); } public long id () {return id; } public boolean iguales (objeto o) {return o instancia de individuo && id == ((individual) o) .id; } public int hashcode () {int resultado = 17; if (name! = null) {resultado = 37 * resultado + name.hashcode (); } resultado = 37 * resultado + (int) id; resultado de retorno; } public int compareto (arg individual) {// Compare por nombre de clase First: String First = GetClass (). GetSImplename (); Cadena argFirst = arg.getClass (). GetSimplename (); int FirstCompare = First.compareto (ArgFirst); if (FirstCompare! = 0) {return FirstCompare; } if (name! = null && arg.name! = null) {int SecondAyCompare = name.compareto (arg.name); if (SECENDCOMPARE! = 0) {return SecondAyComPare; }} return (arg.id <id? -1: (arg.id == id? 0: 1)); }} A continuación se muestra una clase abstracta PetCreator . En el futuro, podemos obtener directamente la colección de clases Pet relacionadas llamando arrayList() . Aquí usamos el método newInstance() que no mencionamos anteriormente. Devolverá una instancia de la clase a la que la clase de clase realmente se refiere. ¿Qué quiere decir esto? Por ejemplo, declarar new Dog().getClass().newInstance() y Direct new Dog() son equivalentes.
public abstract class petCreator {private random rand = new Random (47); // La lista de los diferentes GetTypes of PET para crear: Lista de resúmenes públicos <class <? extiende PET >> getTypes (); public Pet randompet () {// Crear una mascota aleatoria int n = rand.nextint (getTypeS (). size ()); intente {return getTypes (). get (n) .newinstance (); } catch (InstanciationException e) {Throw New RuntimeException (e); } Catch (ilegalAccessException e) {Throw New RuntimeException (e); }} public Pet [] createArray (int size) {Pet [] result = new Pet [size]; for (int i = 0; i <size; i ++) {resultado [i] = randompet (); } resultado de retorno; } Public ArrayList <Pet> ArrayList (int size) {ArrayList <et> result = new ArrayList <Set> (); Colección.addall (resultado, createarray (tamaño)); resultado de retorno; }} A continuación, implementemos la clase abstracta anterior y expliquemos el siguiente código. En el siguiente código, declaramos dos clases de recolección, allTypes y types , entre los cuales allTypes contiene todas las clases declaradas anteriormente, pero nuestros tipos específicos son en realidad solo dos tipos, a saber, Mutt y EgypianMau , por lo que la mascota que realmente necesitamos obtener new son solo los tipos contenidos en types . En el futuro, podemos obtener los tipos contenidos en types llamando getTypes() .
Public Class LiteralPetCreator extiende PetCreator {@supresswarnings ("sin verificar") Lista final estática pública <class <? extiende PET >> AllTypes = Collections.unmodifiablelist (Arrays.aslist (Pet.Class, Dog.Class, Cat.Class, Mutt.Class, Egyptianmau.class)); Lista final estática privada <clase <? extiende PET >> tipos = AllTypes.Sublist (AllTypes.IndexOf (Mutt.Class), AllTypes.Size ()); Lista pública <clase <? extiende PET >> getTypes () {tipos de retorno; }} La lógica general se ha completado, y finalmente implementamos TypeCounter utilizada para contar el número de clases Pet relevantes en el conjunto. Explique isAssignalbeFrom() , que puede determinar que una clase de reflexión es una subclase o subclase indirecta de una clase de reflexión. Como su nombre indica, getSuperclass() es obtener la clase principal de una clase de reflexión.
La clase pública typecounter extiende hashmap <class <?>, integer> {clase privada <?> basetype; public typecounter (clase <?> basetype) {this.basetype = basetype; } public void Count (object obj) {class <?> type = obj.getClass (); if (! basetype.isassignableFrom (type)) {throw new runtimeException (obj + "tipo incorrecto" + tipo + ", debe ser tipo o subtipo de" + basetype); } countClass (tipo); } private void countClass (class <?> type) {Integer Cantity = get (type); poner (tipo, cantidad == NULL? 1: Cantidad + 1); Clase <?> Superclass = type.getSuperClass (); if (superclass! = null && basetype.isassignableFrom (superclass)) {countClass (superclass); }} @Override public string toString () {stringBuilder result = new StringBuilder ("{"); for (map.entry <class <?>, integer> par: entryset ()) {result.append (par.getKey (). getSimplename ()); resultado.append ("="); resultado.append (par.getValue ()); resultado.append (","); } result.delete (result.length () - 2, resultado.length ()); resultado.append ("}"); return result.toString (); }}Resumir
Lo anterior es todo el contenido de este artículo sobre el código de ejemplo compartido del mecanismo de reflexión de Java, y espero que sea útil para todos. Los amigos interesados pueden continuar referiéndose a este sitio:
Código de implementación de recibo de programación e impresión de Java
Explicación detallada de la implementación de referencias y proxy dinámico en Java
Programación de Java para implementar el intercambio de código simple de Lunar Eclipse
Si hay alguna deficiencia, deje un mensaje para señalarlo. ¡Gracias amigos por su apoyo para este sitio!