Java中枚举类型作为常量的替代

3
我听说现在应该使用枚举而不是常量。 这在所有情况下都可行吗?枚举是否可以替代常量
在下面的示例中,我在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
         }
     }
}

}

请在我的样例中建议我使用枚举是否更好,或者当前的使用常量已经足够好了?

2
似乎是与 JAVA 常量:枚举 VS 类 VS 接口 重复,另有一个可能是重复的候选项是 Java 中用枚举作为常量 - 这是一种好的方式吗? - Oleg Estekhin
从设计角度来看,与枚举相比,您的代码给出了完全不同的含义。 - TheLostMind
@有点吓人,我展示了这段代码,让观众知道我在不同步骤中使用常量来达到不同的目的。同时,我使用接口来定义常量...所以它们默认是public static final。 - Shirishkumar Bari
1
不要仅仅为了常量而使用接口。 - awksp
1
@TheLostMind 没错。已编辑。我在写那段话时没有看代码,所以无法评判设计,但是快速浏览后让我感到困惑... - awksp
显示剩余2条评论
6个回答

4
在您的情况下,使用枚举是一种经典的解决方案。
首先,让我们将您的“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 

在所有其他情况下,我更喜欢枚举。

2
您应该在此代码中使用枚举。
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();
   }
 }
}

1

只有枚举类型可以用于存储单一组的常量值。例如:星期、月份、颜色、性别或者进程状态。

使用单一的枚举来存储所有常量并不是一个好主意,相反我们可以为每个常量组使用一个枚举。

比如说,如果你要维护一些颜色代码,最好使用Colour枚举而不是将其保存为常量。


0

这在接口定义中是隐含的,但我想提一下。 - vinayknl
我使用接口来定义常量,因此它们默认为public static final。 - Shirishkumar Bari
@Shirish - 这篇文章已经有一年了。所以我希望你已经停止使用“常量接口”,并开始使用枚举或无法实例化的类。 - ring bearer

0

你对枚举类型的理解有误,它不是用来代替常量的:枚举类型是一组相关的常量,例如:

enum Days {
    SUNDAY, MONDAY, TUESDAY, ...
}

来自文档

枚举类型是一种特殊的数据类型,它使变量成为预定义常量的集合。


0
一个枚举并没有为使用它的类定义一个契约,而是接口。使用枚举的类不属于枚举类型。实现接口的类与接口本质上是相同类型(接口是父级..引用可以更改)。考虑到这些设计问题,请告诉我,你的方法正确吗?

我的类没有实现这个接口。我使用接口来定义常量……所以它们默认是公共的静态常量……我不必为每个常量编写它。 - Shirishkumar Bari
@Shree - 严格来说,一个“接口”隐式地定义了一个不可打破的契约。你可能没有使用它,但你仍然提供了它的能力(这违反了面向对象编程的概念)。 - TheLostMind
1
@Shree,你是让懒惰主导你的设计吗? - Scary Wombat
我已经阅读了<a href="https://dev59.com/dXRC5IYBdhLWcg3wXPwC"> URL</a>,对于为什么接口不适合用于编写常量有了很好的了解......我同意你的观点......我将在以后遵循这个原则......但是主要问题是关于使用枚举作为常量的替代方案。 - Shirishkumar Bari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接