web-dev-qa-db-ja.com

Java Enums and Switch Statements-default case?

例外をスローすることを提案する人々のために:
例外をスローしても、コンパイル時エラーは発生せず、ランタイムエラーが発生します。私は例外を投げることができることを知っています、私はむしろランタイム中よりもコンパイル中に死にたいです。

まず、Eclipse 3.4を使用しています。

Enumであるモードプロパティを持つデータモデルがあります。

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

私は現在このモデルのビューを書いており、コードを持っています

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

関数の最後にデフォルトのケースとreturn xxxがないため、「このメソッドはJava.awt.Color型の結果を返す必要があります」というエラーが表示されます。 Iwant誰かが列挙型に別の型を追加した場合(シャットダウンなど)のコンパイルエラーなので、 AssertionErrorをスローするデフォルトのケース。これは修正されたモードでコンパイルされ、実行時までエラーとして認識されないためです。

私の質問はこれです:
EclipseBuilder(およびjavac)は、このスイッチがすべての可能性をカバーしている(またはカバーしている)ことを認識せず、戻り値の型が必要であるという警告を停止します。 Modeにメソッドを追加せずに、必要なことを実行できる方法はありますか?

それに失敗すると、Enumの可能な値のすべてをカバーしていないswitchステートメントで警告/エラーするオプションがありますか?

編集:ロブ:コンパイルエラーです。 javacでコンパイルしようとしましたが、メソッドの最後の}を対象とする「missing return statement」エラーが表示されます。 Eclispeは、エラーをメソッドの先頭に配置するだけです。

56
KitsuneYMG

常にVisitorパターンでEnumを使用できます。

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

これはもっと冗長ですが、新しい列挙型が宣言された場合、すぐにコンパイルエラーが発生します。

70
mtpettyp

Eclipseで有効にする必要があります(ウィンドウ->設定)エラーレベルで「スイッチでカバーされていない列挙型定数」設定。

メソッドの最後に例外をスローしますが、デフォルトのケースを使用しないでください。

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

  throw new IllegalArgumentException();
}

誰かが後で新しいケースを追加した場合、Eclipseは彼がケースを紛失していることを彼に知らせます。したがって、本当に正当な理由がない限り、デフォルトを使用しないでください。

57
egaga

なぜこのエラーが発生するのかわかりませんが、提案は次のとおりです、なぜ列挙型自体で色を定義しないのですか?その後、誤って新しい色を定義することを忘れることはできません。

例えば:

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

ちなみに、ここでの比較のために、コンパイラエラーを含む元のケースを示します。

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

おそらく、model.GetMode()couldがnullを返すためだと思います。

6
Paul Sonier

あなたの問題は、あなたの列挙型がロックダウンされていることを示すインジケータとしてswitchステートメントを使用しようとしていることです。

実際には、 'switch'ステートメントとJavaコンパイラは、enumで他のオプションを許可したくないことを認識できません。enumで3つのオプションのみが必要なという事実は完全にswitch文の設計とは別に、他の人が指摘しているように、常にデフォルトの文を使用する必要があります(未処理のシナリオであるため、例外をスローする必要があります)。

誰もが触れないように、enumにコメントを自由に振りかける必要があります。また、認識できない場合にエラーをスローするようにswitchステートメントを修正する必要があります。そのようにして、すべてのベースをカバーしました。

[〜#〜] edit [〜#〜]

コンパイラエラーをスローする問題について。それは厳密には意味がありません。 3つのオプションを持つ列挙型と、3つのオプションを持つスイッチがあります。誰かが列挙に値を追加した場合、コンパイラエラーをスローする必要があります。列挙型のサイズは任意であるため、誰かが変更した場合にコンパイラエラーをスローしても意味がありません。さらに、まったく異なるクラスにあるswitchステートメントに基づいて列挙のサイズを定義しています。

EnumとSwitchの内部動作は完全に独立しているため、切り離されたままにしておく必要があります。

2
DevinB

このための良い方法は、デフォルトのケースを追加してエラー値を返すか、例外をスローし、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がすべての列挙に対して定義されていることに注意します。

2
Touko

例外をスローするデフォルトのケースを作成します。

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

...それは、Eclipseが文句を言う理由をほぼ説明しています。今日のスイッチはすべての列挙型のケースをカバーするかもしれませんが、誰かがケースを追加し、明日は再コンパイルできません。

2
kdgregory

なぜEclipseBuilderは、このスイッチがすべての可能性をカバーしている(またはカバーしているのか?)ことを認識せず、戻り値の型が必要であるという警告を停止します。 Modeにメソッドを追加せずに、必要なことを実行できる方法はありますか?

Eclipseの問題ではなく、コンパイラjavacです。 javacが見るのは、nothingが一致する場合に戻り値がないことです(yoがすべてに一致していることを知っているという事実ケースは無関係です)。デフォルトの場合はsomethingを返す必要があります(または例外をスローします)。

個人的には、ある種の例外を投げるだけです。

2
mipadi

私はコメントできないので...

  1. 常に、常に、常にデフォルトのケースがあります。それがどれほど「頻繁に」ヒットするかに驚かれることでしょう(Javaですが、それでも)。

  2. そうは言っても、私の場合にのみオン/オフのみを処理したい場合はどうでしょう。 javacによるセマンティック処理は、それを問題としてフラグを立てます。

0
Paul

最近(この回答は元の質問の数年後に書かれています)、Eclipseではウィンドウ->設定-> Java->コンパイラ->エラー/警告->潜在的なプログラミングの問題:

不完全なスイッチケース

デフォルトのケースが存在する場合でも信号を送る

0
pasaba por aqui