Prefacio
Recientemente, durante el proceso de aprendizaje, descubrí un problema. Las clases abstractas no pueden construir el objeto a través de nuevo sin implementar todos los métodos abstractos, pero los métodos abstractos pueden tener sus propios métodos de construcción. Esto me confundió. Dado que hay un método de construcción y no se puede crear a través de nuevos, ¿se pueden instanciar clases abstractas cuando no se han convertido en clases concretas?
En Java, las clases abstractas no se pueden instanciar directamente. Sin embargo, esta característica de las clases abstractas a menudo se convierte en un obstáculo problemático. Por ejemplo, si quiero usar un proxy dinámico para dar a una clase abstracta la capacidad de ejecutar métodos abstractos, habrá dos dificultades: 1. El proxy dinámico solo puede crear un objeto proxy que implementa la interfaz, pero no un objeto que hereda la clase abstracta. Para este JVM estándar, hay algunas implementaciones, como Javassist, que pueden usarse para lograr esto utilizando herramientas de bytecode (proxyFactory).
Si desea construir un objeto de clase abstracta en Android, solo puede tener new ClassName() {} o heredado. Sin embargo, ambos métodos no pueden ser operados directamente por sus objetos de clase, lo que conduce a algunos problemas que no pueden lograr las capacidades abstractas que necesitamos.
Aquí hay una descripción detallada de la escena mencionada en el primer párrafo:
Primero, hay un archivo de interfaz definido de la siguiente manera (los amigos familiarizados con Android pueden ver que esta es una interfaz de configuración de API proporcionada a la modernización para generar objetos proxy):
interfaz pública realApi {@get ("API1") Observable <String> API1 (); @Get ("API2") Observable <String> API2 (); @Get ("API3") Observable <String> API3 (); //...Other Métodos}A continuación, escriba una clase abstracta para implementar solo uno de los métodos de la interfaz (utilizado para simular datos de interfaz):
@MockApIpublic La clase abstracta MOCKAPI implementa RealApi {Observable <String> API3 () {return observable.just ("datos simulados"); }}Luego necesitamos tener una herramienta, como MockManager, que combina nuestro objeto RealApi existente y la clase Mockapi para construir un objeto mixto. Al ejecutar métodos ya definidos en Mockapi, los ejecutará directamente. Cuando Mockapi no define el método, llamará al método RealApi. El método de llamada es más o menos:
RealApi API = MockManager.Build (RealApi, Mockapi.Class);
A través de Javassist, es muy simple completar la función anterior. Cree un objeto proxyFactory, establezca su superclase en Mockapi, luego filtre el método abstracto y establezca el controlador de métodos para llamar al objeto RealApi con el mismo nombre y método de parámetros. La implementación del código no se dará aquí.
Pero en Android, el método de Javassist será arrojado
Causado por: java.lang.unsupportedoperationException: no puedo cargar este tipo de archivo de clase en java.lang.classloader.defineeclass (classloader.java:520) en java.lang.reflect.method.invoke (método nativo) AT javassist.util.proxy.factoryhelper.toclass2 (factoryhelper.java:182)
Excepciones similares. La razón es probablemente que la implementación y los estándares de las máquinas virtuales en Android son ligeramente diferentes, por lo que aquí dirigimos la dirección a otra dirección del procesador de anotación de generación de código dinámico.
Si usa el procesador de anotaciones para implementarlo, la idea es mucho más simple, pero el proceso sigue siendo un poco tortuoso:
Primero defina una anotación para marcar la clase abstracta que debe construirse
@Target (ElementType.Type)@documented@retención (retenciónPolicy.source) public @Interface Mockapi {} El procesador obtiene el objeto de elemento de la clase basado en la anotación, que es un objeto similar a la clase. Debido a que la clase aún no existe en la etapa de precompilación, es imposible obtener el objeto de clase requerido por el tiempo de ejecución usando Class.forName . Sin embargo, el elemento proporciona métodos similares a la reflexión de clase, y también hay diferencias como el sello y ejecución. ¿Cuáles son los métodos abstractos de la clase abstracta anotados utilizando el objeto Elemento para analizar la clase abstracta anotada, generar una clase de implementación (no abstracto) que hereda la clase e implementar todos los métodos abstractos de la clase. Debido a que estos métodos abstractos no se utilizarán realmente, solo necesitan poder compilar y pasar. La forma en que elegí es que cada cuerpo de método lanza una excepción, lo que provoca que el método es un método abstracto y no se puede llamar directamente. El método de generación del código puede usar algunas herramientas para simplificar el trabajo, como el autoprocesador y Javapoet, que implementa específicamente el código del proyecto al final del texto de referencia. El código generado es más o menos así:
// El nombre de clase generado se nombra con el sufijo del nombre de clase original + "$ impl" para evitar conflictos con otros nombres de clase. Esta restricción también se usa para reflejar la clase de clase pública Final Clase Mockapi $ Implice extiende MockAPI {@Override public Ovsemable <String> api1 () {Throw New IlegalStateException ("API1 () es un método abstracto!"); } @Override public Observable <String> api2 () {throw new IlegalStateException ("api2 () es un método abstracto!"); }}Refleja la clase de implementación basada en el nombre de clase de la clase abstracta y luego construye un objeto de implementación basado en la reflexión llamando a su método de constructor.
// Obtenga el objeto que genera la construcción de código estática privada <t> t getimplObject (clase <t> cls) {try {return (t) class.forname (cls.getName () + "$ impl"). NewInStance (); } capt (excepción e) {return null; }}Construya un proxy dinámico, pasárselo al objeto real de RealApi y el objeto de implementación de la clase abstracta construida en el paso anterior, y determine qué objeto está proxyando su comportamiento de método en función de la definición en la clase abstracta: si hay una definición en la clase abstracta, es decir, el método no es un método abstracto, el objeto de implementación de la clase abstracta se ejecutará; De lo contrario, será ejecutado por el objeto real de la interfaz.
Public Static <Origin, Mock extiende Origin> Origin Build (Final Origin Origin, Final Clase <Mock> MockClass) {// Si la clase Mock está marcada como cerrada, devolverá directamente el objeto de la interfaz real if (! Isenable (MockClass)) {Origin de retorno; } Final Mock MockObject = getImplObject (MockClass); Clase <?> OriginClass = origen.getClass (). GetInterfaces () [0]; return (Origin) Proxy.newProxyInstance(originClass.getClassLoader(), new Class[]{originClass}, new InvocationHandler() { @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { // Get the method of the same name in the defined abstract class and determine whether the Method mockMethod = null; try { mockMethod = MockClass.getDeClaredMethod (método.getName (), método.getParametertypes ()); mockmethod.invoke (mockObject, objetos);Después de completar el trabajo anterior, puede usar el método de compilación para construir un objeto proxy que mezcle interfaces reales y métodos de clase abstracta como se menciona al principio. Aunque la clase de llamadas está esencialmente codificada, es generada automáticamente por el procesador de anotaciones sin mantenimiento manual. En términos de uso, es básicamente lo mismo que usar la implementación de Javassist.
Utilicé el método al que pertenezco en este artículo para implementar una herramienta que simula solicitudes de modernización (hay un enlace al final del artículo), pero en esencia, se puede utilizar para implementar muchas necesidades que requieren la construcción de clases abstractas, y aún deben explorarse más escenarios de uso.
La implementación del código fuente mencionada en el artículo se puede encontrar en el proyecto Retrofit-Mock-Result o la descarga local;
Resumir
Lo anterior es todo el contenido de este artículo. Espero que el contenido de este artículo tenga cierto valor de referencia para el estudio o el trabajo de todos. Si tiene alguna pregunta, puede dejar un mensaje para comunicarse. Gracias por su apoyo a Wulin.com.