Перечисления Java и операторы Switch-случай по умолчанию?


для людей, предлагающих бросить исключение:
Выбрасывание исключения не дает мне ошибку времени компиляции, это дает мне ошибку времени выполнения. Я знаю, что могу бросить исключение, я лучше умру во время компиляции, чем во время выполнения.

во-первых, я использую eclipse 3.4.

у меня есть модель данных, которая имеет свойство mode, которое является перечислением.

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

в настоящее время я пишу вид этой модели и у меня есть код

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

Я получение ошибки " этот метод должен возвращать результат типа java.ОУ.Цвет " потому что у меня нет случая по умолчанию и нет возврата xxx в конце функции. я хочу ошибка компиляции в случае, когда кто-то добавляет еще один тип в enum (например, завершение вниз), поэтому я не хочу ставить по умолчанию, что отправляет AssertionError, как это компилировать с измененным режимом и не будет рассматриваться как ошибка выполнения скрипта.

мой вопрос такой:
Почему EclipseBuilder (и javac) не признают, что этот переключатель охватывает все возможности (или он охватывает их?) и перестаньте предупреждать меня о необходимости возврата типа. Есть ли способ сделать то, что я хочу, не добавляя методы в режим?

в противном случае, есть ли возможность предупредить/ошибка в операторах switch, которые не охватывают все возможные значения перечисления?

изменить: Роб: это компиляция . Я просто попытался скомпилировать его с javac, и я получаю " отсутствует оператор return " error targeting the last } метода. Eclispe просто помещает ошибку в верхней части метода.

10 53

10 ответов:

вы всегда можете использовать перечисление с шаблоном посетителя:

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

Это намного более подробно, но вы сразу же получите ошибку компиляции, если будет объявлено новое перечисление.

вы должны включить в Eclipse (window -> preferences) настройки "Enum type constant not covered in switch" с уровнем ошибок.

бросать исключение в конце метода, но не использовать по умолчанию.

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

  throw new IllegalArgumentException();
}

теперь, если кто-то добавит новое дело позже, Eclipse заставит его знать, что он пропустил дело. Поэтому никогда не используйте значение по умолчанию, если у вас нет действительно веских причин для этого.

Я не знаю, почему вы получаете эту ошибку, но вот предложение, почему бы вам не определить цвет в самом перечислении? Тогда вы не можете случайно забыть определить новый цвет.

например:

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

Я бы сказал, что это, вероятно, потому, что модель.Того, чтобы() может возвращает null.

создать случай по умолчанию, который вызывает исключение:

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

... и это в значительной степени описывает, почему Eclipse жалуется: хотя ваш коммутатор может охватывать все случаи перечисления сегодня, кто-то может добавить случай и не перекомпилировать завтра.

почему EclipseBuilder не признает, что этот переключатель охватывает все возможности (или он охватывает их?) и перестаньте предупреждать меня о необходимости возврата типа. Есть ли способ сделать то, что я хочу, не добавляя методы в режим?

Это не проблема в Eclipse, а скорее компилятор, javac. Все javac видит, что у вас нет возвращаемого значения в случае, когда ничего не соответствует (тот факт, что вы знаю, что вы сопоставление всех случаев не имеет значения). Вы должны вернуться что-то в случае по умолчанию (или создать исключение).

лично я бы просто бросить какое-то исключение.

ваша проблема заключается в том, что вы пытаетесь использовать оператор switch в качестве индикатора того, что ваше перечисление заблокировано.

дело в том, что оператор "switch" и компилятор java не могут распознать, что вы не хотите разрешать другие параметры в своем перечислении. Тот факт, что вы хотите только три варианта в своем перечислении, полностью отделен от вашего дизайна оператора switch, который, как отмечают другие, всегда должен иметь оператор по умолчанию. (В вашем случае он должен бросить исключение, потому что это необработанный сценарий.)

вы должны щедро посыпать свое перечисление комментариями, чтобы все знали, что его не трогать, и вы должны исправить свой оператор switch, чтобы выбросить ошибки для нераспознанных случаев. Таким образом, вы покрыли все основания.

EDIT

по поводу ошибки компилятора броска. Это не совсем имеет смысл. У вас есть перечисление с тремя вариантами и переключатель с тремя вариантами. Вы хотите выкинет ошибку компилятора, если кто-то добавляет значение в enum. За исключением того, что перечисления могут быть любого размера, поэтому нет смысла бросать ошибку компилятора, если кто-то ее изменяет. Кроме того, вы определяете размер своего перечисления на основе оператора switch, который может быть расположен в совершенно другом классе.

внутренние работы перечисления и коммутатора полностью разделены и должны оставаться несвязанными.

хорошим способом для этого было бы добавить случай по умолчанию для возврата некоторого значения ошибки или исключения броска и использовать автоматические тесты с 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 определен для всех перечислений.

Так как я не могу просто комментировать...

  1. всегда, всегда, всегда есть по умолчанию. Вы будете удивлены, как "часто" он будет поражен (меньше в Java, чем C, но все же).

  2. сказав это, что делать, если я только хочу обрабатывать только вкл/выкл в моем случае. Ваша семантическая обработка javac будет отмечать это как проблему.

В настоящее время (этот ответ написан через несколько лет после исходного вопроса), eclipse позволяет следующую конфигурацию в окне -> Настройки -> Java -> компилятор -> ошибка/предупреждения -> потенциальные проблемы программирования:

неполные случаи переключения

сигнал даже если по умолчанию не существует