检查枚举是否具有多个值

12

我有一个枚举类型 FileType

public static enum FileType {
  CSV, XML, XLS, TXT, FIXED_LENGTH
}

FileType fileType = FileType.CSV;

除了以下这种方式(如 "myString".matches("a|b|c");),检查fileType是否为多个值的更好(更干净)的方法是否存在?

if(fileType == FileType.CSV || fileType == FileType.TXT || fileType == FileType.FIXED_LENGTH) {}

2
EnumSet?还是在您的FileType枚举上使用布尔字段? - khelwood
在这种情况下,您需要使用EnumSet。 - user2717954
4个回答

27

选项1:向您的枚举添加一个布尔字段。

public static enum FileType {
    CSV(true), XML(false), XLS(false), TXT(true), FIXED_LENGTH(true);

    private final boolean interesting;

    FileType(boolean interesting) {
        this.interesting = interesting;
    }
    public boolean isInteresting() {
        return this.interesting;
    }
}

...

if (fileType!=null && fileType.isInteresting()) {
    ...
}

选项2:使用 EnumSet

EnumSets 在底层使用位域,因此它们非常快速并且内存占用低。

Set<FileType> interestingFileTypes = EnumSet.of(FileType.CSV, FileType.TXT, FileType.FIXED_LENGTH);
...
if (interestingFileTypes.contains(fileType)) {
   ...
}

选项3:使用 switch,如 kocko 建议的那样


2
在提到EnumSet时加1分。请注意,EnumSet只是Set的另一种实现方式,因此您可以编写:Set<FileType> interestingFileTypes = EnumSet.of(...); - Erwin Bolwidt

16

为什么不使用 switch 语句:

switch(fileType) {
   case CSV:
   case TXT:
   case FIXED_LENGTH:
       doSomething();
       break;
}

这个做法与你的if语句检查相同,但我认为这个更易读。

但是,这段代码存在的问题不在于 switchif/else 语句。问题在于它违反了开闭原则

为了解决这个问题,我将完全删除enum并创建一个接口:

interface FileType {
   boolean isInteresting();
}

那么,针对我们以前拥有的每个枚举常量,我会创建一个单独的接口实现:

public class Txt implements FileType {
  @Override
  public boolean isInteresting() {
    return false;
  } 
}

switch语句怎么改变了呢?我们之前是传递一个fileType参数,然后检查它的值。现在,我们将传递一个FileType实例。

public void method(FileType fileType) {
  if (fileType.isInteresting()) {
    doSomething();
  }
}
这样的好处在于,当您引入新的 FileType 时(将其作为新的枚举常量引入),您不必修改 switch/if/else 语句来处理此新文件类型是否有趣的情况。该代码将在无需修改的情况下正常工作,这是“开放扩展,关闭修改”的开闭原则的精髓。

这甚至更糟,它不是使用一行长代码,而是使用大量的行,使你的代码看起来更长。 - Hasen
@Hasen,实际上更糟糕的是,switch语句(以及if/else方法)违反了面向对象编程中的开闭原则。因此,在这种情况下,我会抛弃枚举和if/else/switch语句,并使用一个带有几个实现的接口FileType。但请注意,我写下这个答案已经7年了。:) :) - Konstantin Yovkov
@Hasen,我更新了我的回答,现在它甚至更好了。长话短说:避免使用枚举和switch语句。它们会带来比你想象的更多问题。 :) - Konstantin Yovkov

10

我最终写出了一个方法:

public static enum FileType {
  CSV, XML, XLS, TXT, FIXED_LENGTH;

  // Java < 8
  public boolean in(FileType... fileTypes) {
    for(FileType fileType : fileTypes) {
      if(this == fileType) {
        return true;
      }
    }

    return false;
  }

  // Java 8
  public boolean in(FileType... fileTypes) {
    return Arrays.stream(fileTypes).anyMatch(fileType -> fileType == this);
  }
}

然后:
if(fileType.in(FileType.CSV, FileType.TXT, FileType.FIXED_LENGTH)) {}

很好,干净整洁!

7
这基本上与其他几个人建议的一样。如果你接受他们的答案会很好,因为他们花时间帮助了你。 - Jeffrey Blattman
你能解释一下 fileType == this 是如何工作的吗? - Rajat
这个不行。你创建了一个枚举实例?因为你写的是 fileType.in 而不是 FileType.in。否则函数也需要是静态的,然后像Rajat指出的那样,'this'就不能工作。 - Hasen
@Rajat @Hasen fileType 是你想要检查一个或多个文件类型的类的实例(这里是 CSVTXTFIXED_LENGTH)。由于它是一个实例,方法 in() 不是静态的,因此可以使用 this - halloei

-1

添加一个不同的例子:

public class JavaApplication {

    public enum CustomerStatus {
        ACTIVE("Active"),
        DISCONNECTED("Disconnected"),
        PENDING("Pending"),
        CANCELLED("cancelled"),
        NEW("new");

    }

    public static void main(String[] args) {
        EnumSet<CustomerStatus> setA = EnumSet.of(CustomerStatus.ACTIVE, CustomerStatus.NEW);
        EnumSet<CustomerStatus> setB = EnumSet.of(CustomerStatus.PENDING, CustomerStatus.CANCELLED);
        if (setA.contains(CustomerStatus.ACTIVE)) {
            System.out.println("ACTIVE : customer active");
        }
        if (setB.contains(CustomerStatus.ACTIVE)) {
            System.out.println("ACTIVE: Customer is no longer active");
        }
        if (setB.contains(CustomerStatus.CANCELLED)   {
            System.out.println("CANCELLED: Customer is no longer active");
        }

    }
}


**Output**:
ACTIVE : customer active
CANCELLED: Customer is no longer active

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