Сегодня я услышал, что мы должны использовать Enums вместо Constants. Возможно ли это во всех случаях? Являются ли перечисления заменой констант?
В приведенном ниже примере у меня есть константы, определенные в файле Constants, и ConstantsTest использует их
public final class Constants {
private Constants(){
}
public static final String ACCOUNT="Account";
public static final String EVENT_ITEM ="EventItem";
public static final int MULTIPLIER_ONE = 1;
public static final int MULTIPLIER_NEGATIVE_ONE = -1;
public static final String BALANCE_AFTER_MODIFICATION = "BalanceAfterModification";
public static final String COMMA = ",";
public static final String DOTPSV =".psv";
public static final String NEW_LINE = "\n";
}
// Test Class
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class ConstantsTest {
private static File rootDir = new File(".");
public static void main(String[] args) throws IOException {
Map<String,Integer> accountBalance = new HashMap<String, Integer>();
accountBalance.put("123",55000);
accountBalance.put("223",15000);
writeToFile(Constants.ACCOUNT, accountBalance, true, 2000);
// do operation
}
/**
*
* @param fileType
* @param inputData
* @param add if true add balance else substract the balance
* @return
* @throws IOException
*/
private static File writeToFile(String fileType , Map<String,Integer>accountBalance ,boolean add, int amount) throws IOException{
File file = null;
FileWriter fw = null;
try{
if(Constants.ACCOUNT.equals(fileType)){
file = new File(rootDir,Constants.ACCOUNT+Constants.DOTPSV);//creating a fileName using constants
fw = new FileWriter(file);
fw.write(Constants.ACCOUNT+Constants.COMMA+Constants.BALANCE_AFTER_MODIFICATION);//Writing Header in file using constant values
updateBalance(accountBalance, add, amount);
for(String key:accountBalance.keySet()){
fw.write(Constants.NEW_LINE);
fw.write(key+Constants.COMMA+accountBalance.get(key));
}
}
else if(Constants.EVENT_ITEM.equals(fileType))
{
// write to EventItem.psv
}
} finally{
if (null!=fw){
fw.close();
}
}
System.out.println("File created successfully");
return file;
}
private static void updateBalance(Map<String, Integer> accountBalance,
boolean add, int amount) {
for(String key:accountBalance.keySet()){
int currentBal = accountBalance.get(key);
if(add){
accountBalance.put(key,currentBal+amount*Constants.MULTIPLIER_ONE); // do lot of calculations
}else{
accountBalance.put(key,currentBal+amount*Constants.MULTIPLIER_NEGATIVE_ONE);// do a lot of calculations
}
}
}
}
Пожалуйста, предложите в моем примере пример перечислений будет лучше, или мой текущий подход к использованию констант достаточно хорош?
В вашем конкретном случае использование enums является классическим решением.
Во-первых, позвольте переписать свои Constants
как перечисление:
public enum Constants {
ACCOUNT,
EVENT_ITEM,
;
}
public enum Operation {
MULTIPLIER_ONE {
public int action(int value) {
return value;
}
},
MULTIPLIER_NEGATIVE_ONE {
public int action(int value) {
return value * (-1);
}
},
;
private Operation(int coef) {
this.coef = coef;
}
public abstract int action(int value);
}
Теперь вместо написания:
if(Constants.ACCOUNT.equals(fileType)){
} else if(....)
Вы можете использовать либо switch/case
или даже лучше не определить: определить метод (пусть называют это action()
в перечислении и вызывать его из кода Смотрите пример в. Operation
перечисления выше в этом случае вы код становится тривиальным:. не более if/else
или switch. Все просто: проверка выполняется во время компиляции: вы определили абстрактный метод в перечислении, вы не можете добавить еще один элемент в перечисление без реализации этого метода для него. Этого не происходит при использовании if/else
структурного обслуживания, из которого является ответственностью программиста.
Я знаю только одно ограничение перечислений: использование аннотаций строки в константах. Существует много аннотаций со строковыми атрибутами. Например XmlElement(name="foo")
. Даже если вы определяете перечисление
enum FooBar {
foo, bar
}
вы не можете использовать его в аннотациях:
@XmlElement(name=FooBar.foo) // is wrong because String type is required
@XmlElement(name=FooBar.foo.name()) // is wrong because annotations do not support method invocation
Во всех остальных случаях я предпочитаю перечисление.
Вы должны использовать перечисления этого кода
enum Constants {
ACCOUNT,
EVENT_ITEM ,
COMMA ,
DOTPSV ,
BALANCE_AFTER_MODIFICATION ;
@Override
public String toString() {
switch(this) {
case ACCOUNT: return "Account";
case EVENT_ITEM : return "EventItem";
case COMMA : return ",";
case DOTPSV : return ".psv";
case BALANCE_AFTER_MODIFICATION : return "BalanceAfterModification";
default: throw new IllegalArgumentException();
}
}
}
Только мы можем использовать Enums для постоянных значений, которые находятся в одной группе. Предположим: недели, месяцы, цвета, пол, состояния процесса
Не рекомендуется использовать единый enum для хранения всех констант. Вместо этого мы можем использовать одно перечисление для каждой группы констант.
Предположим, что у вас есть некоторые цветовые коды, тогда лучше иметь цветное перечисление вместо сохранения в качестве констант.
Enum
не определяет contract
для класса, использующего его, interface
делает. Класс, который использует Enum, не относится к типу Enum. Класс, который реализует интерфейс, фактически имеет тот же тип, что и интерфейс (интерфейс является родительским.. и ссылка может быть изменена). Учитывая эти проблемы проектирования. Скажите, ваш подход правильный?
Interface
неявно определяет неразрывный контракт. Возможно, вы не используете его. Но вы все еще предоставляете возможность для этого (что противоречит концепциям ООП).
Вы ошиблись в перечислении, вам не нужно создавать перечисление вместо константы: enum - это группа констант, которые связаны, например:
enum Days {
SUNDAY, MONDAY, TUESDAY, ...
}
Из документов:
Тип перечисления - это особый тип данных, который позволяет переменной представлять собой набор предопределенных констант.
Константы будут лучше для приведенного примера. По умолчанию переменные интерфейса являются общедоступными.
public static final String ACCOUNT="Account";
См. Почему по умолчанию переменные интерфейса статичны и окончательны?