2015-03-12 3 views
2

Если я использую enum для определения типа задачи.Убедитесь, что используется каждое значение перечисления

public enum TaskType { 
    TYPE_ONE("Type1"),TYPE_TWO("Type2"),TYPE_THREE("Type3"); 

    private final String type; 

    private StageType(String type) { 
     this.type = type; 
    } 

    @Override 
    public String toString() { 
     return type; 
    } 
} 

как я могу уверить в один момент в моей заявке

if(taskType == TaskType.TYPE_ONE) { 
    typeOneProcessing(); 
} else if(taskType == TaskType.TYPE_TWO) { 
    typeTwoProcessing(); 
} else if(taskType == TaskType.TYPE_THREE) { 
    typeThreeProcessing(); 
} 

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

+0

проверить ссылки в eclipse. –

+0

Можно ли предположить, что каждый 'typeXProcessing' будет везде одинаковым? Если это возможно, было бы лучше поместить его в каждый тип и вызвать нечто вроде 'taskType.process()'. – Pshemo

ответ

1

Я думаю, вы говорите, что хотите, чтобы компилятор сказал вам, что все значения перечисления рассматриваются.

К сожалению, Java не поддерживает это.

Вы могли бы подумать, что вы могли бы написать что-то вроде этого:

public int method(TaskType t) { 
    switch (t) { 
    case TYPE_ONE: return 1; 
    case TYPE_TWO: return 2; 
    case TYPE_THREE: return 3; 
    } 
    // not reachable ... no return required 
} 

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

К сожалению, это не работает! Вышеупомянутая ошибка компиляции. Согласно правилам доступности JLS, оператор switch должен иметь значение default:, чтобы этот метод был действительным. (Или вы можете добавить return в конце ...)

Существует веская причина для этой странности. В бинарных правилах совместимости JLS говорится, что добавление нового значения в enum - это двоичное совместимое изменение. Это означает, что любой код с оператором switch, который включает enum, должен оставаться действительным (исполняемым) кодом после добавления значений перечисления. Если для начала было выполнено method, оно не может стать недействительным (потому что есть путь возврата без инструкции return) после двоичного совместимого изменения.


На самом деле, это, как я хотел бы написать код, указанный выше:

public int method(TaskType t) { 
    switch (t) { 
    case TYPE_ONE: return 1; 
    case TYPE_TWO: return 2; 
    case TYPE_THREE: return 3; 
    default: 
     throw new AssertionError("TaskType " + t + " not implemented"); 
    } 
    // not reachable ... no return required 
} 

Это не прикидываться время компиляции безопасно, но это так и не быстрый, и это Безразлично» t связано с плохим дизайном OO.

+0

Вот почему они должны сделать логику, специфичную для перечисления, частью самого перечисления в соответствии с решением OldCurmudgeon, поэтому вам не нужно вступать в ветви на основе 'TaskType'. – EpicPandaForce

+1

@EpicPandaForce - это решение не масштабируется. И, кроме того, это плохой дизайн ... он нарушает принцип разделения проблем. –

+0

Лучше, чем изменять код, если вам нужно перебирать перечисления каждый раз, когда вы добавляете новое перечисление. Вы даже можете сделать отдельный класс и вернуть его из своего перечисления, которое «обрабатывало» перечисление, на которое вызван метод. Он может работать. – EpicPandaForce

5

Существуют инструменты findbugs для этого, но вы можете рассмотреть возможность полного удаления if-then-else и поместить обработку внутри enum. Здесь добавление нового TYPE_FOUR заставит вас написать метод doProcessing().

public interface DoesProcessing { 

    public void doProcessing(); 
} 

public enum TaskType implements DoesProcessing { 

    TYPE_ONE("Type1") { 
       @Override 
       public void doProcessing() { 

       } 
      }, 
    TYPE_TWO("Type2") { 
       @Override 
       public void doProcessing() { 

       } 
      }, 
    TYPE_THREE("Type3") { 
       @Override 
       public void doProcessing() { 

       } 
      }, 
    TYPE_FOUR("Type4") { 
     // error: <anonymous com.oldcurmudgeon.test.Test$TaskType$4> is not abstract and does not override abstract method doProcessing() in DoesProcessing 
      }; 

    private final String type; 

    private TaskType(String type) { 
     this.type = type; 
    } 

    @Override 
    public String toString() { 
     return type; 
    } 
} 

public void test() { 
    DoesProcessing type = TaskType.TYPE_TWO; 
    type.doProcessing(); 
} 

Если вы предпочли бы abstract метод, то это работает:

public enum TaskType { 

    TYPE_ONE("Type1") { 
       @Override 
       public void doProcessing() { 

       } 
      }, 
    TYPE_TWO("Type2") { 
       @Override 
       public void doProcessing() { 

       } 
      }, 
    TYPE_THREE("Type3") { 
       @Override 
       public void doProcessing() { 

       } 
      }; 

    private final String type; 

    private TaskType(String type) { 
     this.type = type; 
    } 

    // Force them all to implement doProcessing. 
    public abstract void doProcessing(); 

    @Override 
    public String toString() { 
     return type; 
    } 
} 
+0

Почему вы не просто добавляете абстрактный метод в перечисление вместо этого интерфейса? –

+0

@AlexisC. - Могло бы это сделать, но реализация интерфейса более гибкая, и другие перечисления могут ее реализовать. – OldCurmudgeon

+1

Это действительно хорошее решение. Это то, что я закончил делать, когда у меня была такая же проблема, как и у вопросника (вернемся к вопросу http://stackoverflow.com/questions/28234960/java-generics-and-enum-loss-of-template-parameters). Он * заставляет * вы реализовать метод для каждого перечисления, а затем вы можете просто перебирать их с помощью 'values ​​()'. – EpicPandaForce

2

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

public interface Task { 
    void process(); 
} 

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

0
HashMap<String, Integer> hm=new HashMap<String, Integer>(); 

... 

if(taskType == TaskType.TYPE_ONE) { 
    typeOneProcessing(); 
    hm.put(TaskType.TYPE_ONE, 1) 
} else if(taskType == TaskType.TYPE_TWO) { 
    typeTwoProcessing(); 
    hm.put(TaskType.TYPE_TWO, 1) 
} else if(taskType == TaskType.TYPE_THREE) { 
    typeThreeProcessing(); 
    hm.put(TaskType.TYPE_THREE, 1) 
} 

... 

for (TaskType t : TaskType.values()) { 
    if(hm.get(t)!=1) 
    // Trigger the alarm 
} 

Вы можете даже сосчитать раз элемент был засчитан, если вам это нужно

0

Вы можете сделать включатель случая на перечислении и потерпеть неудачу, если по умолчанию хита:

switch(taskType){ 
    case TYPE_ONE: ... break; 
    case TYPE_TWO: ... break; 
    case TYPE_THREE: ... break; 
    default: 
    throw new IllegalStateException("Unsupported task type:"+taskType); 
    } 
+1

Это не обнаружит проблему во время компиляции. Он обнаружит его во время выполнения. –

1

AFAIK вы не можете сделать это автоматически.

Чтобы свести к минимуму риск забыть добавить if/case для нового значения, у вас может быть один класс обслуживания для каждого значения перечисления и фабрика, которая предоставляет конкретную услугу для значения перечисления.

E.g. вместо:

void methodA(TaskType type) { 
    doSth(); 
    switch(type) { 
     case TYPE_ONE: 
     foo1(); 
     break; 
     case TYPE_TWO: 
     foo2(); 
     break; 
     ... 
    } 
} 
void methodB(TaskType type) { 
    doSthElse(); 
    switch(type) { 
     case TYPE_ONE: 
     bar1(); 
     break; 
     case TYPE_TWO: 
     bar2(); 
     break; 
     ... 
    } 
} 

сделать:

interface Service { 
    foo(); 
    bar(); 
} 
class ServiceFactory { 
    Service getInstance(TaskType type) { 
     switch(type) { 
     case TYPE_ONE: 
      return new TypeOneService(); 
     case TYPE_TWO: 
      return new TypeTwoService(); 
     default: 
      throw new IllegalArgumentException("Unsupported TaskType: " + type); 
     } 
    } 
} 

И тогда методы выше можно переписать следующим образом:

void methodX(TaskType type) { 
    doSth(); 
    ServiceFactory.getInstance(type).foo(); 
} 

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

Смежные вопросы