【问题标题】:Java: Avoid 'instanceof' when adding, or removing elements from listJava:在列表中添加或删除元素时避免使用“instanceof”
【发布时间】:2013-01-19 21:56:48
【问题描述】:

我正在创建一个游戏。一个游戏有一个 GameMap。为了跟踪 GameMap 上事物的状态,我想为这些事物中的每一个创建 ArrayLists。问题是,我不想创建单独的方法来从每种类型的 ArrayList 中添加或删除项目。我是新手,所以我首先想到的当然是“instanceof”运算符。

请记住,目前,GameMap 是一个离散类,而不是接口或抽象类。目的是在游戏初始化时实例化一个 GameMap 对象。

public class GameMap {

//GameMap has a name
private String mapName;

//GameMap has rooms
private ArrayList<Room> roomsOnMap;

//GameMap has items
private ArrayList<Item> itemsOnMap;

//GameMap has people
private ArrayList<Person> peopleOnMap;

//construct GameMap with rooms, items, and people
private GameMap(String mapName, ArrayList someRooms, ArrayList someItems, ArrayList somePeople)
{
    this.mapName = mapName;
    this.roomsOnMap = someRooms;
    this.itemsOnMap = someItems;
    this.peopleOnMap = somePeople;
}

public void addObject(Object o)
{
    //HOW DO I AVOID THIS?
    if(o instanceof Room)
    {
        roomsOnMap.add((Room) o);
    }
    else if(o instanceof Item)
    {
        itemsOnMap.add((Item) o);
    }
    else if(o instanceof Person)
    {
        peopleOnMap.add((Person) o);
    }
}

【问题讨论】:

  • 我不明白你为什么不想要单独的方法。这将使您的代码更具可读性。
  • 每个对象、GameMap、Room、Item 和 Person 都包含 ArrayList。这意味着对于每个对象,我必须为每种类型的 ArrayList 编写单独的添加、删除方法。我只是厌倦了打字。
  • 您可以通过 1) 复制粘贴 2) 一些智能 IDE 代码生成来解决这个问题。
  • 你说得对......我想我正在尝试走“我如何做一些很酷的事情”的道路,从而摆脱了这项任务的单调。
  • 我也这么认为。对于它的价值:如果某事是学校作业/家庭作业,请在问题中提及。功课是学习,如果有人为你写了完整的代码,你什么也学不到,不是吗? (这在这种情况下不适用,但请注意。)

标签: java arraylist instanceof


【解决方案1】:

使用overloaded 方法:

void addObject(Room room) {
  roomsOnMap.add(room);
}

void addObject(Item item) {
  itemsOnMap.add(item);
}

..

【讨论】:

  • 不推荐这个。为什么不使用不同的方法?这太容易出错了
  • 你根本不推荐函数重载吗?我的意思是,它确实为此目的而存在。
  • 这是最干净的方法。可读性很好。
  • 可读性不是最好的,但要视情况而定。我可能有一个包含异构元素的类,这些元素可以通过相同的方法毫无问题地添加。根据安全性,没有任何变化,所以我不明白它应该如何容易出错,除非使用与继承相关的参数:编译器会抱怨任何可能克服的坏问题。
  • 这个答案正确有效。拥有一个addObject(Object obj) 是个坏主意。如果传入了不支持的对象类型会怎样?空怎么办?如果允许空值,它将被添加到哪个列表中?通过使用重载方法,您可以获得编译时类型检查。
【解决方案2】:

这里有一个技巧:

Map<Class, ArrayList> map = new HashMap<Class, ArrayList>();
map.put(Room.class, roomsOnMap);
map.put(Item.class, itemsOnMap);
map.put(Person.class, peopleOnMap);

// ...

public void addObject(Object o)
{
    map.get(o.getClass()).add(o); // be aware of NullPointerException here
}

不过,我建议为此使用重载方法。

【讨论】:

  • 如果你打电话给addObject(new SubclassOfRoom())会发生什么?这不是一个好主意。它完全打破了多态性。
【解决方案3】:

你预感到instanceOf 可能不是一个好主意是正确的。

如果 Room、Items 和 Persons 是某种“GameElements”或“MapElements”,您可以通过共同的父级在它们之间建立关系:

enum ElementType
{
    PERSON, ITEM, ROOM;
}

interface MapElement
{
    public ElementType getType();
}

class Room implements MapElement
{
    public ElementType getType()
    {
        return ElementType.ROOM;
    }
    //other attributes and methods...
}

class Person implements MapElement
{
    public ElementType getType()
    {
        return ElementType.PERSON;
    }
    //other attributes and methods...
}

class Item implements MapElement
{
    public ElementType getType()
    {
        return ElementType.ITEM;
    }
    //other attributes and methods...
}

那么你的add方法就可以在这个界面上操作了

public void addObject(MapElement e)
{
    if(e.getType == ElementType.Room)
    {
        roomsOnMap.add((Room) e);
    }
    ...
}

仅当元素相关时才执行此操作。如果不是,您应该有单独的方法。使用一种方法来执行此操作可能看起来很有效,但您不会获得任何收益。对于使用此类的任何类(以及编写它的开发人员),单独的 add 方法(如果元素不相关)比使用 Object 作为参数的通用 add 更直观。如果事实上你的类和方法变得更简单,那么你不会丢失任何东西,甚至不会丢失几行代码。

【讨论】:

  • 这与 OP 现在所做的没有什么不同;语义。
  • @AaronMcIver 为什么不呢?问题是如何避免实例。 OP 使用 instanceOf 是因为他在看似相关的类之间没有关系。
  • 我们在这里缺少的是我想实例化一个对象 GameMap 以便我可以跟踪游戏中事物的状态。但这确实解决了帮助问题,因为我可以创建一个类似这样的接口,称为 GameElements,这样我就可以从每个 Room 和 Person 类中提取重载方法。
【解决方案4】:

如果您想避免使用instanceof,请尝试Visitor 设计模式。

在这里你可以找到它的描述:http://www.oodesign.com/visitor-pattern.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-03
    • 2013-03-05
    • 1970-01-01
    • 1970-01-01
    • 2018-08-11
    • 2012-04-18
    • 2022-11-15
    • 1970-01-01
    相关资源
    最近更新 更多