Перечисления Java и операторы Switch - случай по умолчанию? - PullRequest
56 голосов
/ 13 мая 2009

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

Во-первых, я использую затмение 3.4.

У меня есть модель данных, у которой есть свойство mode - 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?

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

Edit: Роб: Это ошибка компиляции . Я только что попытался скомпилировать его с помощью javac, и у меня появляется ошибка «пропущенный оператор возврата», нацеленная на последний метод}. Eclispe просто помещает ошибку в начало метода.

Ответы [ 10 ]

70 голосов
/ 13 мая 2009

Вы всегда можете использовать шаблон 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() );

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

57 голосов
/ 13 мая 2009

Необходимо включить в Eclipse (окно -> настройки) настройки «Константа типа перечисления, не включенная в переключатель» с уровнем ошибки.

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

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

  throw new IllegalArgumentException();
}

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

10 голосов
/ 13 мая 2009

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

Например:

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;
        }
    }   
}
6 голосов
/ 13 мая 2009

Я бы сказал, что это возможно потому, что model.GetMode () может вернуть ноль.

2 голосов
/ 15 мая 2009

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

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

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

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

EDIT

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

Внутренняя работа Enum и Switch полностью разделена и должна оставаться не связанной.

2 голосов
/ 13 мая 2009

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

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

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

2 голосов
/ 13 мая 2009

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

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

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

0 голосов
/ 28 декабря 2016

В настоящее время (этот ответ написан через несколько лет после исходного вопроса), eclipse позволяет выполнить следующую конфигурацию в Window -> Preferences -> Java -> Compiler -> Error / warnings -> Потенциальные проблемы программирования:

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

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

0 голосов
/ 13 мая 2009

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...