【问题标题】:Java: Interfaces containing inner enums; Extending functionality in implementation classesJava:包含内部枚举的接口;在实现类中扩展功能
【发布时间】:2010-07-13 22:44:50
【问题描述】:

我的界面如下:

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();
}

和一个实现类:

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;
    }
}

想法是让ThingImpl 中的MovingStateThing 接口扩展MovingState(从而将MovingState 的实际实现与接口分开)。

这不起作用 - ThingImpl 中的 MovingState 枚举隐藏了接口中的定义而不是扩展它,然后编译器抱怨 ThingImpl 不是抽象的并且不会覆盖抽象方法 setNewPosition( Thing.MovingState) 中的事物。

有没有一种实际的方法可以实现我想要实现的目标?还是 Java 根本就没有这个能力?

【问题讨论】:

    标签: java interface enums


    【解决方案1】:

    您真正想要做的是从“ThingImpl”类中删除枚举声明,并将其全部(包括其构造函数和 getter)移动到 Thing 接口中。

    使您的字段在枚举中成为最终字段,以记住它们不应该被触及。

    以这种方式,任何希望使用 Thing 接口的东西都必须使用接口上定义的枚举 - 你的问题是你实际上定义了两次,但它应该在接口上(如果可以仅用于此接口)或作为公共级别的枚举 Java 文件(使用公共枚举而不是公共类)。如果您的接口以外的其他东西可以合理地期望使用它,您会将其设为公共枚举 - 在我看来,Map.Entry 是一个糟糕的嵌套接口,因为其他类已用于映射外部的键/值对和因此它应该是它自己的接口,但我们必须忍受它:(

    想法是让 ThingImpl 中的 MovingState 从 Thing 接口扩展 MovingState(从而将 MovingState 的实际实现与接口分离)。

    我不认为这真的是你的想法 - 我认为你在 Thing 界面上指定的行为很好,你真的不想触摸 MovingState 枚举,因为它很好。然而,如果你认为某些东西需要一个不同的 MovingState 实现,你可以让它实现一个名为 MovingState 的接口,因此你可以重命名你的枚举 DefaultMovingState。这是你的选择。

    您的 MovingState 界面将简单地包含您现在在 MovingState 中公开的 getter。两种方法。

    【讨论】:

      【解决方案2】:

      您不能扩展 enum,因为它是 final

      您可能需要阅读Effective Java 2nd Edition,Item 34:Emulate extensible enums with interfaces。从本质上讲,这归结为enum 本身就是implements Something

      【讨论】:

      • 只有一半正确,无法扩展。每个枚举值都可以是一个匿名类,它扩展了您的枚举类。
      • 我无权访问这本书,但从示例代码中可以看出,该项目与我试图实现的目标不同。第 34 条似乎涵盖了扩展枚举以添加新常量,但我的目标只是添加成员函数。我的基类 (Thing) 中的枚举包含所有必需的常量。该功能是我希望隐藏在实现类 (ThingImpl) 中的。
      • @Matthew:我认为enum 不适合定义一些抽象类型; interface 更合适。也许你可以有一个interface OrthogonallyMovablemoveUp()moveDown() 等,并有一个使用enum 的特定实现。
      【解决方案3】:

      polygenelubricants 已为您解答。我想添加一些内容,因为这是我遇到的问题。

      假设您有一个包含许多实现的接口。接口中的一种方法将enum 作为参数。理想情况下,您希望每个实现都有特定的枚举值集。但是,由于您无法扩展枚举,因此您无法创建“基础”枚举并从中进行扩展。天真的方法是拥有一个“上帝”枚举来维护完整的枚举值集。但是有一个更好的方法。您可以使用所谓的标记界面。有效的 Java 第 2 版。也谈到了这一点。 标记接口将不包含任何方法声明,而只是将一个类标记为某种类型。

      因此您可以定义一个标记接口并让您的所有枚举实现该接口。这与使enum 可扩展有关,因为如果您定义了一个接口让您的enums 扩展,您就会自动将您的enums 标记为某种类型。

      通过这种方式,您可以保留所有特定的枚举值及其特定实现(关注点分离)。

      对于您的情况,您可以这样做:

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

      然后:

      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;
          }
      }
      

      【讨论】:

        【解决方案4】:

        您正在尝试的是一种反模式。不应在接口中定义常量。

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

        我会将常量移出您的界面,或者简单地将它们设为方法,并让您的实现通过实现这些方法来定义它们...

        【讨论】:

        • -1 - 你不正确,他没有做反模式。他所做的很愚蠢,但这不是常量接口模式,特别是因为他的接口定义了行为,并且不仅仅用于保存常量值。在接口上定义常量是很好的,如果它们要与接口的行为结合使用的话。
        • 是的,我想这是正确的,我应该投反对票。然而,这种方法是朝错误方向迈出的一步,我认为我关于将他的 MOVING_LEFT 类型常量转换为方法(可能放入抽象类)的建议仍然很好......
        • 他可以这样做,当然,但如果他真的想添加额外的方向,他的界面就会受到影响。向左斜向上移动呢?这是另一种方法,但只是一个额外的枚举常量。你甚至可以制作更深奥的枚举——横向移动,向上移动,然后向下移动。让每一个都作为一个单独的方法来实现,使界面气球。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-28
        • 2012-12-10
        • 1970-01-01
        • 2021-03-17
        • 2011-09-24
        相关资源
        最近更新 更多