5
\$\begingroup\$

I'm looking to simplify the initialisation of the second constant, preferably to just one line. Any ideas? Anything from Guava or JDK (up to 1.6) is ok to use.

enum Box {
    PROMO_HEADER,
    FREEMIUM,
    EXTRA_STORIES,
    TOP_STORIES,
    TOP_CHARACTERS,
    TOP_THEMES,
    TOP_ARTISTS,
    TOP_ISSUES
    // several more ...
} 

final static List<Box> FREEMIUM_BOXES = ImmutableList.copyOf(Box.values());
final static List<Box> DEFAULT_BOXES = initDefaultBoxes(); // All except FREEMIUM

static List<Box> initDefaultBoxes() {
    List<Box> boxes = Lists.newArrayList(FREEMIUM_BOXES);
    boxes.remove(Box.FREEMIUM);
    return ImmutableList.copyOf(boxes);
}

Granted, it's not that bad, but still I'd like to get rid of the init method. :-) (Static initialiser wouldn't be any better.)

NB: I want to define the order of boxes only once, in the enum definition itself, so ImmutableList.of(PROMO_HEADER, EXTRA_STORIES, ...) is not considered a good solution for the purposes of this refactoring.


A single-statement Guava solution would be this, using filter() and a Predicate, but arguably this is less clean than the original, because this kind of stuff is verbose in Java...

final static List<Box> DEFAULT_BOXES =
    ImmutableList.copyOf(Iterables.filter(FREEMIUM_BOXES, new Predicate<Box>() {
        @Override
        public boolean apply(Box box) {
            return box != Box.FREEMIUM;
        }
    }));

Resolution

Went with this, which is the cleanest option considering I indeed need the constants defined as Lists:

final static List<Box> FREEMIUM_BOXES = ImmutableList.copyOf(Box.values());
final static List<Box> DEFAULT_BOXES = 
    Sets.immutableEnumSet(EnumSet.complementOf(EnumSet.of(Box.FREEMIUM))).asList();

Props to Xaerxess!

\$\endgroup\$
7
\$\begingroup\$

Use EnumSet for that. From docs:

A specialized Set implementation for use with enum types. (...) The iterator returned by the iterator method traverses the elements in their natural order (the order in which the enum constants are declared).

It's also very efficent:

Implementation note: All basic operations execute in constant time. They are likely (though not guaranteed) to be much faster than their HashSet counterparts. Even bulk operations execute in constant time if their argument is also an enum set.

So in your case:

final static Set<Box> DEFAULT_BOXES = 
    EnumSet.complementOf(EnumSet.of(Box.FREEMIUM));

or if you prefer immutable one, use Sets.immutableEnumSet(Iterable):

final static ImmutableSet<Box> DEFAULT_BOXES = 
    Sets.immutableEnumSet(EnumSet.complementOf(EnumSet.of(Box.FREEMIUM)));

or if you insist for List (but why would you?):

final static ImmutableList<Box> DEFAULT_BOXES = 
    Sets.immutableEnumSet(EnumSet.complementOf(EnumSet.of(Box.FREEMIUM))).asList();

P.S. All constants can be represented as:

final static Set<Box> FREEMIUM_BOXES = EnumSet.allOf(Box.class); // or wrap it with Sets.immutableEnumSet to make it ImmutableSet
\$\endgroup\$
  • \$\begingroup\$ Why List? Simple: order matters. This affects the order of UI elements and that must be correct. It might work with some Sets too, but when the constant is defined as List, it's explicit and guaranteed that the order will be right. \$\endgroup\$ – Jonik Jan 22 '14 at 15:44
  • \$\begingroup\$ ImmutableSet also has defined order (insertion order), so does Sets.immutableEnumSet (The iteration order of the returned set follows the enum's iteration order, so this is what you want), you can always use .asList() on it which is a view (does not copy elements). Anyway, I agree with you, and that said I'd use .asList() with ImmutableList. \$\endgroup\$ – Xaerxess Jan 22 '14 at 15:46
  • \$\begingroup\$ Yes, I might actually define it as ImmutableSet, since it has "reliable, user-specified iteration order". Good call on EnumSet.complementOf() and EnumSet.allOf(), thanks! \$\endgroup\$ – Jonik Jan 22 '14 at 15:52
  • \$\begingroup\$ Ah, actually I will stick with List as the type, because what I need from these constants is get(int index) (in a method called createView(int position)). Using Set would unnecessarily complicate that code. \$\endgroup\$ – Jonik Jan 22 '14 at 15:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.