Java枚举和切换语句 - 默认情况?

时间:2009-05-13 18:23:52

标签: java enums

对于建议抛出异常的人:
抛出异常不会给我一个编译时错误,它给我一个运行时错误。我知道我可以抛出异常,我宁愿在编译期间死也不会在运行时死。

首先,我使用的是eclipse 3.4。

我的数据模型的模式属性是Enum。

enum Mode {on(...), off(...), standby(...); ...}

我目前正在撰写此模型的视图,我有代码

...
switch(model.getMode()) {
case on:
   return getOnColor();
case off:
   return getOffColor();
case standby:
   return getStandbyColor();
}
...

我收到错误“此方法必须返回java.awt.Color类型的结果”,因为我没有默认情况,并且在函数末尾没有返回xxx。 想要编译错误,如果有人在枚举中添加了另一种类型(例如关闭),所以我不想放一个抛出AssertionError的默认情况,因为这会编译使用修改后的模式,直到运行时才被视为错误。

我的问题是:
为什么EclipseBuilder(和javac)没有意识到这个开关涵盖了所有可能性(或者它是否涵盖了它们?)并且停止警告我需要返回类型。有没有办法,我可以做我想要的,而无需向Mode添加方法?

如果失败了,是否有一个选项可以在不包含所有Enum可能值的switch语句上发出警告/错误?

编辑: Rob:这是一个编译错误。我只是尝试用javac编译它,我得到一个“缺少返回语句”错误,目标是方法的最后一个}。 Eclispe只是将错误放在方法的顶部。

10 个答案:

答案 0 :(得分:70)

您可以随时使用Enum with Visitor模式:

enum Mode {
  on {
      public <E> E accept( ModeVisitor<E> visitor ) {
         return visitor.visitOn();
      }
  },
  off {
      public <E> E accept( ModeVisitor<E> visitor ) {
         return visitor.visitOff();
      }
  },
  standby {
      public <E> E accept( ModeVisitor<E> visitor ) {
         return visitor.visitStandby();
      }
  }

  public abstract <E> E accept( ModeVisitor<E> visitor );

  public interface ModeVisitor<E> {
      E visitOn();
      E visitOff();
      E visitStandby();
  }
}

然后你将实现如下内容:

public final class ModeColorVisitor implements ModeVisitor<Color> {
    public Color visitOn() {
       return getOnColor();
    }

    public Color visitOff() {
       return getOffColor();
    }

    public Color visitStandby() {
       return getStandbyColor();
    }

}

您可以按如下方式使用它:

return model.getMode().accept( new ModeColorVisitor() );

这更详细但如果声明了新的枚举,你会立即得到编译错误。

答案 1 :(得分:57)

您必须在Eclipse(窗口 - &gt;首选项)设置中启用“错误级别未包含在交换机中的枚举类型常量”。

在方法结束时抛出异常,但不要使用默认情况。

public String method(Foo foo)
  switch(foo) {
  case x: return "x";
  case y: return "y";
  }

  throw new IllegalArgumentException();
}

现在如果有人稍后添加新案例,Eclipse会让他知道他错过了一个案例。所以除非你有充分的理由这样做,否则不要使用默认值。

答案 2 :(得分:10)

我不知道为什么会出现这个错误,但这里有一个建议,为什么不定义枚举本身的颜色?然后你不会不小心忘记定义一种新颜色。

例如:

import java.awt.Color;

public class Test {

    enum Mode 
    {
        on (Color.BLACK), 
        off (Color.RED),
        standby (Color.GREEN);

        private final Color color; 
        Mode (Color aColor) { color = aColor; }
        Color getColor() { return color; }
    }

    class Model
    {
        private Mode mode;
        public Mode getMode () { return mode; }
    }

    private Model model;

    public Color getColor()
    {
        return model.getMode().getColor();
    }   
}

btw,这里的比较是原始情况,编译错误。

import java.awt.Color;
public class Test {

    enum Mode {on, off, standby;}

    class Model
    {
        private Mode mode;
        public Mode getMode () { return mode; }
    }

    private Model model;

    public Color getColor()
    {
        switch(model.getMode()) {
        case on:
           return Color.BLACK;
        case off:
           return Color.RED;
        case standby:
           return Color.GREEN;
        }
    }   
}

答案 3 :(得分:6)

我想这可能是因为model.GetMode()可以返回null。

答案 4 :(得分:2)

创建一个抛出异常的默认情况:

throw new RuntimeExeption("this code should never be hit unless someone updated the enum") 

...而且这几乎描述了为什么Eclipse会抱怨:虽然你的交换机今天可以覆盖所有枚举案例,但有人可以添加案例而不是明天重新编译。

答案 5 :(得分:2)

  

为什么EclipseBuilder没有意识到这个开关涵盖了所有可能性(或者是否涵盖了它们?)并且停止警告我需要返回类型。有没有办法,我可以做我想要的,而无需向Mode添加方法?

这不是Eclipse中的问题,而是编译器javac。所有javac看到的是,在 nothing 匹配的情况下,您没有返回值(知道您匹配所有案例的事实是无关紧要的)。您必须在默认情况下返回某些(或抛出异常)。

就个人而言,我只是提出某种例外。

答案 6 :(得分:2)

您的问题是您正在尝试使用switch语句作为您的枚举被锁定的指示符。

事实是'switch'语句和java编译器无法识别您不想在枚举中允许其他选项。您只想在枚举中使用三个选项的事实与您对switch语句的设计完全分开,其他人应该注意到它应该总是有一个默认语句。 (在你的情况下,它应抛出异常,因为它是一个未处理的场景。)

你应该自由地在你的枚举上添加注释,以便每个人都知道不要触摸它,你应该修复你的switch语句,为未识别的案例抛出错误。 这样你就覆盖了所有的基础。

修改

关于抛出编译错误的问题。这没有严格意义。你有一个包含三个选项的枚举,以及一​​个带有三个选项的开关。如果有人向枚举添加值,您希望它抛出编译器错误。除了枚举可以是任何大小,因此如果有人更改它,抛出编译器错误是没有意义的。此外,您基于switch语句定义枚举的大小,该语句可以位于完全不同的类中。

Enum和Switch的内部工作方式是完全独立的,应该保持解耦。

答案 7 :(得分:2)

一个很好的方法是添加默认大小写以返回一些错误值或抛出异常并使用jUnit自动化测试,例如:

@Test
public void testEnum() {
  for(Mode m : Mode.values() {
    m.foobar(); // The switch is separated to a method
    // If you want to check the return value, do it (or if there's an exception in the 
    // default part, that's enough)
  }
}

当您进行自动化测试时,将负责为所有枚举定义foobar。

答案 8 :(得分:0)

因为我不能只评论......

  1. 始终,始终,始终拥有默认案例。你会惊讶地发现它会“经常”被击中(Java比C少,但仍然如此)。

  2. 话虽如此,如果我只想在我的情况下只处理开/关怎么办?您对javac的语义处理会将其标记为问题。

答案 9 :(得分:0)

如今(这个答案写在原问题之后几年),eclipse允许在Window处进行以下配置 - &gt;偏好 - &gt; Java - &gt;编译器 - &gt;错误/警告 - &gt;潜在的编程问题:

  

不完整的开关案例

     
    

即使存在默认情况也会发出信号