6

The title pretty much explains the question. I have an interface method:

Set<Field> getFieldSet()

and I have a class, User which looks something like this

class User {
    enum Fields implements Field {
        USERNAME, PASSWORD;
        ...
    }
    ...
}

Now I want to implement User's getFieldSet() method. The naive way seems to just return EnumSet.allOf(Fields.class) but I get the following error:

> Type mismatch: cannot convert from Set<User.Fields> to Set<Field>

Other than manually copying the EnumSet to Set<Field>, is there a good way to do this?

Cameron Skinner
  • 51,692
  • 2
  • 65
  • 86
Amir Rachum
  • 76,817
  • 74
  • 166
  • 248

4 Answers4

6

You could return new HashSet<Field>(EnumSet.allOf(Fields.class));.

That will get around the fact that you can't assign a value of type Set<User.Fields> to a variable of type Set<Field>.

Alternatively, your interface could be Set<? extends Field> getFields() instead. You can assign Set<User.Field> to a capturing variable.

Cameron Skinner
  • 51,692
  • 2
  • 65
  • 86
  • 1
    Note that constructing a new HashSet will cause the set entries to get copied, which removes the performance benefit of using an EnumSet in the first place. – Daniel Pryden Jun 30 '11 at 00:11
  • Yup. Your answer is better :) – Cameron Skinner Jun 30 '11 at 00:13
  • 1
    Returning wildcard types from APIs is generally discouraged: it adds complexity to classes calling the API. Probably better to just return a `Set`. – Daniel Jun 30 '11 at 00:21
  • @Daniel: I agree. It does make things more complex, but in some cases it's still useful. I mentioned it only because it is a *possible* solution depending on the OPs overall design. – Cameron Skinner Jun 30 '11 at 00:38
  • Good point. It does seem like it's a better idea for SPIs, to make interfaces more flexible, so that the implementation provider to return whatever they want. – Daniel Jun 30 '11 at 01:15
3

Use Collections.unmodifiableSet:

return Collections.<Field>unmodifiableSet(EnumSet.allOf(Fields.class));

Pros:

  • No unsafe conversion cast: the operation is typesafe at runtime
  • The returned set is actually a Set<Field>, not a Set<? extends Field>
  • The set isn't copied, only wrapped
  • The returned set cannot be mutated

Cons:

  • The returned set cannot be mutated, but it wouldn't be safe to do so anyway.
Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
1

The reason this doesn't work is that Set<Fields> isn't a subtype of Set<Field>. For example, if you returned a Set<Fields> from your method, you could get a situation like the following:

Set<Field> fieldSet = user.getFieldSet(); //Returns an EnumSet<Fields>
fieldSet.add(new Field(){}); //Would compile, but would blow up at runtime, 
                             //because the set can only contain Fields enum 
                             //constants

Your best option here is to use a different set implementation (usually an unmodifiable set) to return the values. For example:

Set<Field> getFieldSet() {
    return Collections.unmodifiableSet(EnumSet.allOf(Fields.class));
}

or, if you need the set to be mutable (probably not a good idea, usually)

Set<Field> getFieldSet() {
    return new HashSet(EnumSet.allOf(Fields.class));
}
Daniel
  • 10,115
  • 3
  • 44
  • 62
0

Here is I think a good solution. It's not exactly what you want but close enough

import java.util.EnumSet;
import java.util.Set;

public class User {
    enum Fields implements Field {
        USERNAME,
        PASSWORD;
    }

    Set< ? extends Field> getFieldSet() {
        return EnumSet.allOf(Fields.class);
    }
}
Jarek Potiuk
  • 19,317
  • 2
  • 60
  • 61