2009-12-07 17 views
8

Tengo el código de un juego simple, donde se debe implementar AgentInterface para crear un controlador de agente para uno de los personajes del juego. GameState es una clase que implementa GameStateInterface, y un objeto que implementa esta interfaz se puede pasar al agente, por lo que el agente puede leer y analizar los datos del estado del juego, y el agente debe devolver la acción apropiada (devuelta como un int) que el personaje debería tomar.¿Es posible bloquear/denegar una conversión de conversión en Java?

Este es el AgentInterface que los agentes deben implementar:

public interface AgentInterface { 
    // the return value specifies the direction of the joystick 
    public int action(GameStateInterface gs); 
} 

ejecutar el juego con un agente llamado MyAgent:

GameState gs = new GameState(); 
    AgentInterface agent = new MyAgent(); 
    while (true) { 
     // more code here 
     int bestAction = agent.action(gs) 
     // more code here 
    }   

embargo, hay cierta información en gamestate que el agente no debe ser poder acceder, ya que eso sería hacer trampa para el controlador. Pero, haciendo una conversión elenco de GameStateInterface a gamestate permitiría que el agente para acceder a la información que no está definido en el GameStateInterface, así:

public MyAgent implements AgentInterface { 
    public int action(GameStateInterface gs) { 
     int nLives = ((GameState) gs).nLivesRemaining; // IS IT POSSIBLE TO DENY/PREVENT THIS CAST?? 
     // Do more stuff here 
     return BestAction; 
    } 
} 

Mi pregunta sería, ¿es posible bloquear una conversión reparto? Sé que el polimorfismo es una de las características principales de Java y de los lenguajes de programación orientados a objetos, pero en casos como este me gustaría evitar las conversiones de conversión.

Sé que esto puede resolverse de muchas otras maneras, pero tenía curiosidad por saber si es posible hacer esto.

Gracias de antemano.

Respuesta

12

Por lo que yo sé, no es posible interceptar un encasillado y negarlo (por ejemplo, lanzando un ClassCastException).

Pero en lugar de intentar negar el tipo de letra, simplemente puede usar el Proxy pattern para controlar el acceso al objeto GameState real. Simplemente implemente una clase proxy, que solo implementa GameStateInterface y permita reenviar todas las llamadas al objeto GameState. Ahora, en lugar de pasar la referencia real del objeto GameState al método de acción, la pasa envuelta por una instancia de su clase proxy.

+0

Parece una buena solución. +1 –

+4

Advertencia: asegúrese de tener su seguridad de Java configurada para que el código del agente (es decir, no confiable) no pueda usar las API de Reflection. Hay formas en que alguien podría usar la reflexión para acceder al miembro privado de una clase proxy para obtener el objeto GameState. –

0

No, no hay manera de hacer esto.

mejores deseos,
Fabian

0

No sé si lo que estás describiendo es posible en Java. En otros idiomas, puede sobrecargar a los operadores de typecast y hacer que emitan una excepción o algo así, pero esto no es posible en Java. Tu mejor opción es probablemente hacerlo de una de las "muchas otras formas" de las que hablaste.

4

No es posible bloquear un yeso. Sin embargo, puedes definir el estado de tu juego de tal forma que solo pueda construirse desde un lugar específico. Una cosa que viene a la mente sería una clase interna privada que implementa la interfaz, o una fábrica de regresar una instancia de la clase interna privada

4

La respuesta es simplemente "no echó a gamestate en su código de agente".

Alternativamente, se puede declarar la materia gamestate como privado. O si necesita acceder desde algunas otras clases selectas, declararlo como protegido del paquete.

2

Si le preocupa que un agente modifique el estado del juego, cree una copia en frijol del estado y páselo al agente, en lugar del objeto GameState real.

Prohibir un cast no parece posible (es probable que sea una característica de lenguaje JVM no bloqueable), o nunca he oído hablar de él.

+0

Sí, de hecho, una copia de frijol inmutable también fue mi primera solución posible. Solo quería saber otras formas de ir. +1 –

4

En general, no puede evitar que un objeto sea lanzado en Java. El código que recibe una referencia a su GameState podrá llamar a cualquier método no protegido y no protegido en ese objeto. Incluso si pudieras evitar el lanzamiento, aún podría usar el reflejo.

Si el código Agent está bajo su control, simplemente mantenga las cosas simples y no emitidas. Si otros escriben clases Agent, puede crear una clase proxy que tome un objeto GameState y solo implemente los métodos de GameStateInterface.

class GameStateProxy implements GameStateInterface { 
    private GameStateInterface state; 

    public GameStateProxy(GameState state) { 
     this.state = state; 
    } 

    public int someMethodInGameStateInterface(int x) { 
     return state.someMethodInGameStateInterface(x); 
    } 

    // other methods ... 
} 

Posteriormente, se podría crear un proxy y pasar así:

GameStateInterface proxy = new GameStateProxy(gameState); 
int bestAction = agent.action(proxy); 

El código que recibe una GameStateProxy sólo tendría acceso a los métodos de GameStateInterface.

+3

, pero recuerde configurar un SecurityManager para inhibir Reflection. No es tan difícil obtener acceso al 'estado' privado ... –

1

Lo que hacemos es dar un jar con "Stubs" que puede compilar en contra, pero no contiene ninguna implementación. Cuando se ejecuta el producto real, reemplazamos los talones con un frasco real.

Pero luego en nuestro caso, controlamos dónde se ejecuta.

En nuestro caso, también hacemos exactamente lo que nos pide. Cualquier clase tiene que solicitar acceso a otras clases (en tiempo de ejecución). Sin embargo, creo que es una implementación personalizada y no estoy seguro de que se ejecute en cualquier JVM.

Puede intentar buscar/solicitar/cualquiera que sea el código fuente de las cosas en las que estoy trabajando. Hay una implementación de referencia disponible si dice que está interesado en desarrollar cajas de cable para poder obtenerlas. Se llama implementación de la pila de referencia "tru2way" u "OCAP" y creo que el proyecto está disponible en algún lugar de Java. Podría tomar un poco de google, y estoy bastante seguro de que encontrará que todo está hecho en un cargador de clases especial o SecurityManager.

EDIT: Creo que puedo estar equivocado. Lo que hacemos es crear "permisos" con el administrador de seguridad en función del nombre de la clase a la que se accede. Cuando un hilo intenta llamar a un método en la clase, primero probamos sus permisos (escribimos el código dentro de la clase "protegida") y si el hilo actual no tiene el permiso identificado por el nombre de la clase, arroja un excepción.

Tiene el mismo efecto que usted, pero es más lento y prolijo. Pero luego tenemos que evitar que los niños vean pr0n.

Edición 2: (Lo siento !!)

En cuanto a las descripciones de permiso como esta me hace creer que debe ser al menos parcialmente posible:

Esto concede el permiso para consultar código de una clase por su público, acceso protegido, predeterminado (paquete), y campos y/o métodos privados. Aunque el código tendría acceso a los nombres de métodos y campos privados y protegidos, no tendría acceso a los datos de campo privados/protegidos y no podría invocar ningún método privado.Sin embargo, el código malicioso puede usar esta información para apuntar mejor un ataque. Además, puede invocar cualquier método público y/o acceder a campos públicos en la clase. Esto podría ser peligroso si el código normalmente no podría invocar esos métodos y/o acceder a los campos porque no puede convertir el objeto a la clase/interfaz con esos métodos y campos.

De lo contrario, ¿cómo se podría evitar que los applets instanciaran y accedieran a clases de JVM arbitrarias? Es posible que las rutas "peligrosas" estén todas bloqueadas de la misma manera que bloqueamos nuestras cosas, al leer los permisos de comprobación cada vez que se las llama, pero esa cita anterior hace que parezca que hay más disponibles y la mayoría de las clases están completamente bloqueadas por defecto.

Esto me ha interesado por un tiempo, pero nunca lo investigué.

1

Solo se puede convertir a un tipo accesible. Al hacer que GameState sea privado, esté protegido o protegido, puede restringir quién puede transmitirlo.

Si está ejecutando código no confiable, asegúrese de instalar un controlador de seguridad, como la reflexión puede utilizarse para eludir modificadores de acceso en su absensce (compárese con Field.setAccessible)

2

que estaba ejecutando un único objeto asegurado leer. Si crea una interfaz de solo lectura (sin setters), puede encasillar y acceder a los métodos de objeto puro. Por ejemplo, la interfaz solo tiene un get y el hijo de esta interfaz tiene el conjunto. Si lanzas el objeto a la interfaz, solo tienes el get. Pero todavía se puede encasillado este objeto y acceder a todo :(

Para evitar esto, se puede crear un compuesto que va a ser poseídas por el creador de la clase Aquí está un ejemplo:.

public class ItemReadOnly { 

private String m_name; 

private ItemReadOnly(String name){ 
    m_name = name; 
} 

public String getName(){ 
    return m_name; 
} 

private void setName(String name){ 
    m_name = name; 
} 

public static Item createItem(String name){ 
    return new Item(new ItemReadOnly(name)); 
} 

private static class Item { 

    private ItemReadOnly m_readOnlyInstance; 

    public Item(ItemReadOnly readOnlyInstance){ 
     m_readOnlyInstance = readOnlyInstance; 
    } 

    public void setName(String name){ 
     m_readOnlyInstance.setName(name); 
    } 

    public String getName(){ 
     return m_readOnlyInstance.getName(); 
    } 

    public ItemReadOnly getReadOnlyInstance(){ 
     return m_readOnlyInstance; 
    } 
} 
} 

Este Así, se escribe:

Item item = ItemReadOnly.createItem(name); 

Así que tienen el acceso de objeto Item (clase interna puede tener acceso a métodos privados :)) Entonces, si usted quiere dar acceso de sólo lectura a este artículo:

ItemReadOnly readOnly = item.getReadOnlyInstance(); 

¡Ahora, absolutamente no es posible encasillar porque no son del mismo tipo en absoluto!

Espero que esto pueda ayudar a alguien! (me gustaría si mencionas la fuente: P)