Check enum for multiple values
Asked Answered
F

5

15

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) {}
Ferret answered 27/11, 2014 at 14:25 Comment(2)
An EnumSet? Or a boolean field on your FileType enum?Toucan
EnumSet is what you want to use in this caseGentilesse
F
10

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!

Ferret answered 12/5, 2015 at 8:20 Comment(4)
This is basically the same thing as what a few other people suggested. It'd be nice if you accepted their answer since they took the time to help you out.Hunnicutt
can you explain how fileType == this workedTallula
This doesn't work. You've created an instance of the enum?? Since you've written fileType.in rather than FileType.in. Otherwise the functions also need to be static and then 'this' does not work as Rajat pointed out.Vyky
@Rajat @Vyky fileType is an instance of the class you want to check against one or many file types (here CSV, TXT, FIXED_LENGTH). Since its an instance, the method in() is not static and this can be used.Ferret
T
31

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

Toucan answered 27/11, 2014 at 14:30 Comment(1)
+ 1 for mentioning the EnumSet. Note that an EnumSet is just another implementation of Set so you can write: Set<FileType> interestingFileTypes = EnumSet.of(...);Antilebanon
B
16

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".

Borderline answered 27/11, 2014 at 14:26 Comment(3)
That's even worse, instead of using one long line it uses loads of lines and makes your code look so much longer.Vyky
@Hasen, what is actually worse here, is that the switch statement (and also the if/else aprpoach) is breaking the Open-closed principle in OOP. So in this case I would actually throw away the enum and the if/else/switch statements and use an interface FileType with a few implementations, instead. But note that I've written this answer 7 years ago. :) :)Borderline
@Hasen, I updated my answer, now it's actually even better. Long story short: Avoid using enums and switch statements. These bring more issues than you would think. :)Borderline
F
10

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!

Ferret answered 12/5, 2015 at 8:20 Comment(4)
This is basically the same thing as what a few other people suggested. It'd be nice if you accepted their answer since they took the time to help you out.Hunnicutt
can you explain how fileType == this workedTallula
This doesn't work. You've created an instance of the enum?? Since you've written fileType.in rather than FileType.in. Otherwise the functions also need to be static and then 'this' does not work as Rajat pointed out.Vyky
@Rajat @Vyky fileType is an instance of the class you want to check against one or many file types (here CSV, TXT, FIXED_LENGTH). Since its an instance, the method in() is not static and this can be used.Ferret
F
0

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)) {
   ...
}
Frustule answered 14/8 at 12:0 Comment(0)
I
-1

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
Intracranial answered 4/8, 2017 at 10:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.