Java - 施展 Set 的缺点?

Java - downsides to casting a Set?

为了好玩,我正在 Java 开发基于实体组件系统的基本游戏引擎。

我需要一种快速获取特定类型的所有 Component 的方法。 示例:

public class MovementSystem extends System {
    @Override
    public void update() {
        Set<Location> locations = ComponentManager.getActiveComponents(Location.class);
        for (Location locationComponents : locations) {
            // do stuff
        }
    }
}

所以我有一个 ComponentManager 应该可以处理这个问题:

public class ComponentManager {
    private static final Map<Class<? extends Component>, Set<Component>> active = new HashMap<>();

    public static void activate(Component component) {
        // if component.class does not have an entry in
        // active yet, initialize with new HashSet
        active.computeIfAbsent(component.getClass(), compClass -> new HashSet<>());
        active.get(component.getClass()).add(component);
    }
    public static void deactivate(Component component) {
        Set<Component> comps = active.get(component.getClass());
        if (comps != null)
            comps.remove(component);
    }
    public static <T extends Component> Set<T> getActiveComponents(Class<T> compClass) {
        return (Set<T>) active.get(compClass);
    }
}

我的 IDE 不喜欢 (Set<T>) active.get(compClass);(以黄色突出显示)。然而,这是基于非常基本的测试工作的,并且比单独投射 Set 中的每个项目并将它们添加到新的 Set 更快,但是这样做的潜在缺点是什么?

您看到的 IDE 警告是未经检查的转换。

在这里,您将 getActiveComponents() 的 return 分配给 Set<Location>

Set<Location> locations = ComponentManager.getActiveComponents(Location.class);

但是根据对象的来源,它可能是任何类型的Components(你从Set<Component>中取出),不一定是Locations :

private static final Map<Class<? extends Component>, Set<Component>> active = new HashMap<>();

当你使用它时:

 return (Set<T>) active.get(compClass);

but what are the potential downsides to doing this?

今天您的代码是正确的,因为它在 Map 条目中添加了值,其中 Set 的对象将 class 的运行时类型用作键。
但是如果稍后代码在映射中添加不遵循此规则的内容,编译器将无法检测到键入错误。
所以你只会在运行时发现它:这是缺点。

你的情况不好吗?视情况而定。
当您开发 manipulate/group 对象为 type/category 的某种库时,可能会发生此类问题。你一般要研究类型安全和代码之间的平衡flexibility/maintainability.
在这里你 "lose" 键入安全以减少要维护的结构的数量。
如果您的 Component subclasses 数量有限并且它不会移动,您可以将地图分解成几组:每个 subclass 一组。这样,您就不会再有未经检查的转换了。
但是,如果 Component subclasses 的数量很重要并且它会移动(您可以及时添加或删除它),则未经检查的转换警告可能是可以接受的。在这种情况下,非常欢迎编写一个单元测试来确保您只检索预期类型的​​实例,因为这提供了编译器在您执行未经检查的转换时无法为您做的保证。