2010-07-13 20 views
9

que tiene una interfaz similar a la siguiente:Java: Interfaces que contienen enumeraciones internas; funcionalidad se extiende en clases de implementación

package example; 
import java.awt.Point; 

public interface Thing { 
    public enum MovingState { 
     MOVING_LEFT, 
     MOVING_UP, 
     MOVING_RIGHT, 
     MOVING_DOWN 
    } 

    public void setNewPosition(MovingState state); 
    public Point getPosition(); 
} 

y una clase de implementación:

package example; 
import java.awt.Point; 

public class ThingImpl implements Thing { 
    public enum MovingState { 
     MOVING_LEFT (-1, 0), 
     MOVING_UP (0, -1), 
     MOVING_RIGHT (1, 0), 
     MOVING_DOWN (0, 1); 

     private int x_move; 
     private int y_move; 

     MovingState(int x, int y) { 
      x_move = x; 
      y_move = y; 
     } 

     public int xMove() { 
      return x_move; 
     } 

     public int yMove() { 
      return y_move; 
     } 
    } 


    private Point position; 

    public void setNewPosition(MovingState state) { 
     position.translate(state.xMove(), state.yMove()); 
    } 

    public Point getPosition() { 
     return position; 
    } 
} 

La idea es tener MovingState en ThingImpl extienden MovingState desde la interfaz Thing (por lo tanto separando la implementación real de MovingState de la interfaz).

Esto no funciona, aunque - la enumeración MovingState en ThingImpl sombras la definición de la interfaz en lugar de extenderlo, entonces el compilador se queja de que ThingImpl no es abstracta y no reemplazar el método abstracto setNewPosition (Thing.MovingState) en Cosa.

¿Hay una manera real de hacer lo que estoy tratando de lograr? ¿O simplemente Java no tiene esta capacidad?

Respuesta

11

Lo que realmente quiere hacer es quitar la declaración de enumeración de su clase "ThingImpl" y se mueven todo (incluido su constructor y getters) en la interfaz Thing.

Haga que sus campos sean definitivos en la enumeración para recordar que no deben tocarse.

De esta manera, cualquier cosa que desee usar la interfaz Thing debe usar la enumeración definida en su interfaz; su problema es que efectivamente la está definiendo dos veces, pero debería estar en la interfaz (lo cual está bien si solo se usará para esta interfaz) o como un archivo Java de enum de nivel público (usando public enum en lugar de public class). Lo convertirías en una enumeración pública si pudiera esperarse razonablemente que utilizara algo más que tu interfaz: Map.Entry, en mi opinión, es una mala interfaz anidada porque otras clases tienen uso para una pareja clave/valor externa a un mapa y por lo que debe ser su propia interfaz, pero tenemos que vivir con ello :(

la idea es tener MovingState en ThingImpl se extienden desde la interfaz MovingState cosa (separando así la aplicación real de MovingState desde la interfaz).

No creo que esta sea realmente tu idea, creo que el comportamiento que has especificado en la interfaz de Thing está bien, realmente no quieres tocar el enunciado MovingState ya que es bien como es. Sin embargo, si crees que algo necesita una implementación diferente de MovingState, puedes hacer que implemente una interfaz llamada MovingState y así podrás cambiar el nombre de tu enum DefaultMovingState. Es tu elección.

Su interfaz MovingState simplemente tendría los getters que está exponiendo en MovingState en este momento. Dos métodos

-2

Lo que estás intentando es un antipatrón. Las constantes no deberían definirse en una interfaz.

http://en.wikipedia.org/wiki/Constant_interface

me movería las constantes de su interfaz, o simplemente hacerlos métodos, y dejar que sus implementaciones a definir mediante la aplicación de esos métodos ...

+2

-1 - no es correcto, no está haciendo el antipatrón. Lo que está haciendo es estúpido, pero no es el patrón de interfaz constante específicamente porque su interfaz * define el comportamiento *, y no sirve para mantener valores constantes. Definir constantes en una interfaz es correcto. IFF se debe usar junto con el comportamiento de la interfaz. – MetroidFan2002

+0

Sí, supongo que es correcto y merezco el voto abajo. sin embargo, el enfoque es un paso en la dirección incorrecta y creo que mi consejo sobre cómo convertir sus constantes de tipo MOVING_LEFT en métodos (posiblemente en una clase abstracta) sigue siendo bueno ... – hvgotcodes

+0

Podría hacerlo, claro, pero luego su interfaz sufrirá si realmente quiere agregar direcciones adicionales. ¿Qué hay de mover diagonalmente hacia arriba a la izquierda? Ese es otro método, pero solo una constante enumerada adicional. Incluso podría hacer enumeraciones más esotéricas: moverse de lado, arriba y abajo. Tener cada uno implementado como un método separado hace que la interfaz se inflame. – MetroidFan2002

5

Usted no puede extender un enum porque es final.

Le recomendamos leer Effective Java 2nd Edition, Item 34: Emular enum extensibles con interfaces. Esencialmente, esto se reduce al enum sí mismo que implements Something.

+0

Sólo la mitad de verdad con el no se puede extender. Cada uno de los valores enum puede ser una clase anónima que extiende su clase enum. – josefx

+0

No tengo acceso a este libro, pero por lo que puedo decir del código de muestra, este artículo no cubre lo mismo que estoy tratando de lograr. El elemento 34 parece abarcar la extensión de enumeraciones para agregar nuevas constantes, pero mi objetivo es solo agregar funciones de miembro. La enumeración en mi clase base (Cosa) contiene todas las constantes requeridas. La funcionalidad es lo que esperaba ocultar en la clase de implementación (ThingImpl). – MatthewD

+0

@Matthew: No creo que un 'enum' sea apropiado para definir algún tipo abstracto; una 'interfaz' es más apropiada. Quizás puedas tener una 'interfaz OrtogonallyMovable', con' moveUp() ',' moveDown() ', etc., y tener una implementación particular que use' enum'. – polygenelubricants

3

polygenelubricants ha dado su respuesta. Me gustaría agregarle algo, porque fue un problema con el que me encontré.

Digamos que tiene una interfaz que tiene muchas implementaciones. Un método en la interfaz toma como argumento enum. Idealmente, le gustaría tener conjuntos específicos de valores enumerados por implementación. Sin embargo, como no puede extender las enumeraciones, no puede crear una enumeración "base" y extenderse desde allí. El enfoque ingenuo sería tener una enumeración "dios" que mantenga el conjunto completo de valores enumerados. Pero hay una mejor manera. Puede usar lo que se conoce como una interfaz de marcador . Effective Java 2nd Ed. habla de esto también. Una interfaz de marcador no contendrá ninguna declaración de método, pero simplemente marca una clase como un tipo determinado.

Para que pueda definir una interfaz de marcador y tener todas sus enums implementar esa interfaz. Esto se relaciona con la ampliación del enum, ya que si define una interfaz que extiende su enum s, ha marcado automáticamente su enum como un tipo determinado.

De esta manera puede mantener todos los valores enumerados específicos junto con sus implementaciones específicas (separación de la preocupación).

Para su caso, se puede hacer algo como esto:

public interface MovingInterface {  
    int xMove(); 
    int yMove(); 
} 

y luego:

public enum MovingState implements MovingInterface { 
    MOVING_LEFT (-1, 0), 
    MOVING_UP (0, -1), 
    MOVING_RIGHT (1, 0), 
    MOVING_DOWN (0, 1); 

    private int x_move; 
    private int y_move; 

    MovingState(int x, int y) { 
     x_move = x; 
     y_move = y; 
    } 

    public int xMove() { 
     return x_move; 
    } 

    public int yMove() { 
     return y_move; 
    } 
} 
Cuestiones relacionadas