当A继承自B时,将EnumSet<A>转换为Set<B>

6

标题已经很明确地解释了问题。我有一个接口方法:

Set<Field> getFieldSet()

我是一名有用的助手,我可以为您进行翻译。

我有一个类,User,大致如下:

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

现在我想要实现User类的getFieldSet()方法。看起来朴素的做法是直接return EnumSet.allOf(Fields.class),但我遇到了以下错误:

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

除了手动将EnumSet复制到Set<Field>,还有其他好的方法吗?


你不能将它强制转换为 Set<Field> 吗? - Alex Gitelman
2
@Alex:不,Java不允许这样做。如果你这样做,那么它可能会导致问题,例如:Set<Object> o = new HashSet<Integer>(); o.add("I'm not an integer"); - Cameron Skinner
4个回答

6

您可以返回new HashSet<Field>(EnumSet.allOf(Fields.class));

这将解决您无法将类型为Set<User.Fields>的值分配给类型为Set<Field>的变量的问题。

或者,您的接口可以改为Set<? extends Field> getFields()。您可以将Set<User.Field>分配给捕获变量。


1
请注意,构建一个新的HashSet将导致集合条目被复制,这将消除首次使用EnumSet的性能优势。 - Daniel Pryden
是的,你的答案更好 :) - Cameron Skinner
1
通常不建议从API返回通配符类型:它会增加调用API的类的复杂性。最好只返回一个Set<Field> - Daniel
@Daniel:我同意。它确实使事情更加复杂,但在某些情况下仍然有用。我提到它只是因为它是一个可能的解决方案,取决于OP的整体设计。 - Cameron Skinner
很有道理。看起来对于SPI而言,让接口更加灵活似乎是个更好的想法,这样实现提供者就可以返回任何他们想要的东西。 - Daniel

3

使用Collections.unmodifiableSet

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

优点:

  • 没有不安全的类型转换:运行时操作是类型安全的
  • 返回的集合实际上是一个Set<Field>,而不是一个Set<? extends Field>
  • 集合没有被复制,只是被包装了一层
  • 返回的集合不能被改变

缺点:

  • 返回的集合不能被改变,但这样做也不安全。

1
这个不起作用的原因是 Set<Fields> 不是 Set<Field> 的子类型。例如,如果您从方法中返回了一个 Set<Fields>,您可能会遇到以下情况:
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

在这里,您最好使用不同的集合实现(通常是不可修改的集合)来返回值。例如:

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

或者,如果你需要集合是可变的(通常不是一个好主意)

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

0

这里我认为有一个不错的解决方案。虽然不完全符合你的要求,但已经足够接近了。

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

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