Pregunta

Estoy tratando de utilizar un distribuidor de sucesos para permitir un modelo para notificar suscrito oyentes cuando los cambios.el evento operador recibe una clase de controlador y el nombre de un método para llamar durante el envío.el presentador se suscribe a los cambios en el modelo, y proporcionar un Controlador de aplicación que se llama en los cambios.

He aquí el código (lo siento, es un poco largo).

EventDispacther:

package utils;

public class EventDispatcher<T> {
    List<T> listeners;
    private String methodName;

    public EventDispatcher(String methodName) {
        listeners = new ArrayList<T>();
        this.methodName = methodName;
    }

    public void add(T listener) {
        listeners.add(listener);
    }

    public void dispatch() {
        for (T listener : listeners) {
            try {
                Method method = listener.getClass().getMethod(methodName);
                method.invoke(listener);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
    }
}

Modelo:

package model;

public class Model {
    private EventDispatcher<ModelChangedHandler> dispatcher;

    public Model() {
        dispatcher = new EventDispatcher<ModelChangedHandler>("modelChanged");
    }

    public void whenModelChange(ModelChangedHandler handler) {
        dispatcher.add(handler);
    }

    public void change() {
        dispatcher.dispatch();
    }
}

ModelChangedHandler:

package model;

public interface ModelChangedHandler {
    void modelChanged();
}

Presentador:

package presenter;

public class Presenter {

    private final Model model;

    public Presenter(Model model) {
        this.model = model;
        this.model.whenModelChange(new ModelChangedHandler() {

            @Override
            public void modelChanged() {
                System.out.println("model changed");
            }
        });
    }
}

Principal:

package main;

public class Main {
    public static void main(String[] args) {
        Model model = new Model();
        Presenter presenter = new Presenter(model);
        model.change();
    }
}

Ahora, espero conseguir el "modelo de cambio" mensaje.Sin embargo, me estoy poniendo un java.lang.IllegalAccessException:Clase utils.EventDispatcher no puede acceder a un miembro de la clase presentador.Presentador de$1 con modificadores de "público".

Entiendo que la clase la culpa es de la clase anónima que he creado en el interior de la presentadora, sin embargo no sé cómo hacerlo más "público", de lo que actualmente es.Si puedo reemplazar con el nombre de una clase anidada que parecen funcionar.También funciona si el Presentador y el EventDispatcher están en el mismo paquete, pero no puedo permitir que (varios ponentes de diferentes paquetes deben utilizar el EventDispatcher)

alguna idea?

¿Fue útil?

Solución

Este es un error en el JVM (Error 4819108)

La solución es llamar method.setAccessible(true) antes de la llamada a method.invoke(listener)

Otros consejos

Supongo que una clase anónima siempre es private, pero no encontré una declaración clara sobre esto en la especificación del idioma Java (miré en §15.9.5)

En Java, si un tipo no es accesible, tampoco lo son sus miembros.

Si te gusta la magia negra, puedes deshabilitar la verificación de acceso usando method.setAccessible(true). Alternativamente, puede requerir que los manejadores de eventos se denominen clases, o el método en cuestión que se declare en tipos accesibles.

El problema aquí es que en el código que utiliza la reflexión, que son reflejo de la clase en lugar de la interfaz.

En virtud de la no-reflexión de las circunstancias, el listener no se considera de tipo presenter.Presenter$1.Usted estaría usando a través de un ModelChangedHandler de referencia. ModelChangedHandler es un tipo público y tiene un método público, y que polimórficos acceso será permitido.

Pero debido a que usted está usando getClass(), se está sacando el real de la clase de implementación.Normalmente, esta clase no es accesible a todos.Locales y clases anónimas no son de nivel superior y que no sea miembro de las clases.Por lo tanto, el "acceso" no está definido para ellos.

De hecho, el verdadero error aquí es el hecho de que el mecanismo de reflexión vistas de la "no modificadores de acceso" como "predeterminada de acceso", que es "paquete privado".Lo que permite esta operación cuando los tipos están en el mismo paquete.De la OMI, se debería haber informado IllegalAccessException incluso cuando están en el mismo paquete, ya que no hay acceso a la clase dada, desde donde se llama, y la restricción de acceso debe ser explícitamente levantado con method.setAccessible(true).

Entonces, ¿cuál sería la manera más correcta de hacerlo?Usted debe tener acceso a él mediante el interfaz Class objeto.

package util;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class EventDispatcher<T> {
    List<T> listeners;
    Method method;

    public EventDispatcher(Class<? extends T> cls, String methodName) throws NoSuchMethodException, SecurityException {
        listeners = new ArrayList<T>();
        this.method = cls.getMethod(methodName);
    }

    public void add(T listener) {
        listeners.add(listener);
    }

    public void dispatch() {
        for (T listener : listeners) {
            try {
                method.invoke(listener);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
    }
}

En esta versión, se pasa al constructor de una Clase de objetos de la interfaz necesaria, así como el nombre del método.Vamos a crear el Method objeto en el constructor.Es un reflejo de el método en el interfaz sí.No la clase.

En dispatch, cuando invocamos el método, se aplica el la interfaz método del oyente.Esta es la reflexión combinado con el polimorfismo.

package model;

import util.EventDispatcher;

public class Model {
    private EventDispatcher<ModelChangedHandler> dispatcher;

    public Model() throws NoSuchMethodException, SecurityException {
        dispatcher = new EventDispatcher<ModelChangedHandler>(ModelChangedHandler.class, "modelChanged");
    }

    public void whenModelChange(ModelChangedHandler handler) {
        dispatcher.add(handler);
    }

    public void change() {
        dispatcher.dispatch();
    }
}

Así que aquí en el Model, hacemos uso de la interfaz de la clase literal - que sabemos porque es aquí que se trata de decidir qué interfaz de uso.

package main;

import model.Model;
import presenter.Presenter;

public class Main {
    public static void main(String[] args) {
        Model model;
        try {
            model = new Model();
            Presenter presenter = new Presenter(model);
            model.change();

        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
    }
}

El único cambio aquí es el try-catch.

Esta vez - no hay problemas de acceso.Se invoca el método polymorphically, y es perfectamente accesible!

Esta es una muy mala idea para usar la reflexión en ese caso. Simplemente deje que su despachador llame al método requerido. Si necesita varios despachadores para llamar a diferentes métodos, simplemente subclaselos.

Java le faltan cierres, ¡pero la ayuda está en camino!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top