halloei
halloei

Reputation: 2036

Check enum for multiple values

I have an enum FileType

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

FileType fileType = FileType.CSV;

Is there a better (cleaner) way to check fileType for multiple values than the following (like "myString".matches("a|b|c");)?

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

Upvotes: 15

Views: 17791

Answers (5)

Matze
Matze

Reputation: 401

You can write your own generic utility class for this purpose:

public final class EnumUtils {

    /**
     * Returns true if the given test enum is equal to any of the given search enums.
     */
    static public <T> boolean equalsAny(T testEnum, T... searchEnums) {
        for (T searchEnum : searchEnums) {
            if (testEnum == searchEnum) {
                return true;
            }
        }
        return false;
    }
}

Use it like this:

MyEnum x = ...;
if (EnumUtils.equalsAny(x, MyEnum.OPTION_A,  MyEnum.OPTION_B,  MyEnum.OPTION_C)) {
   ...
}

Upvotes: 0

Konstantin Yovkov
Konstantin Yovkov

Reputation: 62864

Why not use a switch:

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

This does the same as your if statement check, but it's more readable, imho.

But the problem with this code is not the switch or the if/else statement(s). The problem is that it breaks the Open-closed principle.

In order to fix that, I would completely remove the enum and create an interface:

interface FileType {
   boolean isInteresting();
}

Then, for each enum constant we used to have, I would create a separate interface implementation:

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

How does the switch statement change? We used to pass a fileType parameter, on which we checked the value. Now, we will pass an instance of FileType.

public void method(FileType fileType) {
  if (fileType.isInteresting()) {
    doSomething();
  }
}

The advantage of this is that when you introduce a new FileType (which you would introduce as a new enum constant), you don't have to modify the switch/if/else statement to handle the case when this new file type is interesting or not. The code will simply work here without modification, which is the essence of the Open-closed principle: "Open for extensions, closed for modifications".

Upvotes: 16

khelwood
khelwood

Reputation: 59113

Option 1: Add a boolean field to your enum.

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()) {
    ...
}

Option 2: use an EnumSet. EnumSets use bitfields under the hood, so they are very fast and low memory.

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

Option 3: use a switch, as kocko suggests

Upvotes: 31

Barani r
Barani r

Reputation: 2347

Adding a different example:

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

Upvotes: -1

halloei
halloei

Reputation: 2036

I ended up writing a method:

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);
  }
}

And then:

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

Nice and clean!

Upvotes: 10

Related Questions