Java" instanceof"添加多个列表

时间:2016-12-28 02:57:17

标签: java list instanceof

我目前正在尝试重构我的代码,因为我读到这些实现比扩展更受欢迎。目前,我正在尝试创建一个将Object添加到场景中的函数。为了更好地定义每个对象的内容,有多个列表,例如用于更新,渲染等的列表。

private List<Updatable> updatables;
private List<Removable> removables;
private List<Renderable> renderables;
private List<Collidable> collidables;

我想在我的Scene类中创建一个函数,如下所示:

public void add(Object o) {
    if(o instanceof Updatable)
        updatables.add((Updatable) o);
    if(o instanceof Removable)
        removables.add((Removable) o);
    if(o instanceof Renderable)
        renderables.add((Renderable) o);
    if(o instanceof Collidable)
        collidables.add((Collidable) o);
}

同样我会创建一个类似的删除功能。我的问题是,如果这是糟糕的编码实践,因为我听说过instanceof的缺陷,其次,如果是,是否有办法解决这个/重构我的代码以使添加实例变得简单?我不想在每个列表中添加一个实例,并且每次都定义它属于哪个。

5 个答案:

答案 0 :(得分:2)

只需使用重载:add(Updateable u), add(Removable r),等。

答案 1 :(得分:1)

我会使用Map

与Factory模式类似
static Map<String, List> maps = new HashMap<>();
static {
    maps.put(Updatable.class.getName(), new ArrayList<Updatable>());
    maps.put(Removable.class.getName(), new ArrayList<Removable>());
    maps.put(Renderable.class.getName(), new ArrayList<Renderable>());
    maps.put(Collidable.class.getName(), new ArrayList<Collidable>());
}
public static void add(Object obj){
    maps.get(obj.getClass().getName()).add(obj);
}

答案 2 :(得分:1)

正如您在评论中提到的,您的用例有些特殊。 UpdatableRemovableRenderableCollidable都是接口。您有一些实现这些接口中的一个或多个的类。最终,您希望您的add方法将对象添加到它实现的接口的每个列表中。 (如果有任何错误,请纠正我。)

如果重载太冗长,你可以用反射完成这个,如下所示:

private List<Updatable> updatables;
private List<Removable> removables;
private List<Renderable> renderables;
private List<Collidable> collidables;

@SuppressWarnings("unchecked")
public void add(Object o) {
    try {
        for (Class c : o.getClass().getInterfaces()) {
            // Changes "Updatable" to "updatables", "Removable" to "removables", etc.
            String listName = c.getSimpleName().toLowerCase() + "s";

            // Adds o to the list named by listName.
            ((List) getClass().getDeclaredField(listName).get(this)).add(o);
        }
    } catch (IllegalAccessException e) {
        // TODO Handle it
    } catch (NoSuchFieldException e) {
        // TODO Handle it
    }
}

但如果你打算使用这种方法,请注意以下几点:

  1. 反射本质上很容易出现运行时错误。
  2. 如果您的层次结构更复杂,则可能会遇到问题。例如,如果您有Foo implements Updatable, RemovableBar extends Foo,则在getClass().getInterfaces()上调用Bar将返回一个空数组。如果这听起来像您的用例,您可能希望使用来自Apache Commons Lang的ClassUtils.getAllInterfaces

答案 3 :(得分:0)

我期望您对每个列表都有一个单独的add方法,如下所示:

public void addUpdatable(Updatable updatable) {
    updatables.add(updatable);
}

public void addRemovable(Removable removable) {
    removables.add(removable);
}

// and so on

这样做的原因是这些是单独的列表,虽然实施角度的关注点是使API优雅和光滑,但这些努力将从用户的角度消极地影响清晰度。

例如,在添加Updatable Removable时发生的事情的误解(在EJP的答案评论中)表明这种不明确性。虽然您对类型和状态使用类似的术语使其看起来多余,但您仍应明确将项目添加到哪个列表(或部分状态)。

无论如何,看起来你在更大的范围内正在努力解决问题。我认为花更多的时间来理解更大的问题是个好主意。

答案 4 :(得分:0)

从@Jerry06开始,为我添加不同类型的多个列表的实例的最佳方法是使用魔术列表获取器。

private Map<Class, List> maps = new HashMap<>();

'maps'用于查找您要查找的列表。

private <T> List<T> get(Class<T> c) {
    return maps.get(c);
}

而不是使用maps.get,我只使用get(SomeClass.class)。上面的函数自动将其转换为具有正确元素类型的列表。然后我能够做到:

    get(Updatable.class).stream().filter(Updatable::isAwake).forEach(Updatable::update);

而不是:

    updatables.stream().filter(Updatable::isAwake).forEach(Updatable::update);

我主要想要一个这样的结构,所以所有列表都可以很好地包装,其次我可以说map.add(SomeNewClass.class,new ArrayList()),然后随时调用它。