16

The scenario. I'm writting game-related code. In that game a Player(its also a class) has a list of Item. There are other types of items that inherit from Item, for example ContainerItem, DurableItem or WeaponItem.

Obviously it is very conveniant for me to just have List<Item>. But when I get the players items, the only way for me to distinguish between what type of item is by using the instanceof keyword. I'm sure I've read that reliaing on it is bad practice.

Is it ok to use it in this case? Or should I rethink all of my structure?

Justas S
  • 584
  • 4
  • 12
  • 34
  • You can avoid the need of using instanceof if Item defines an abstract method, forcing each subclass to implement its own. – NickJ Jun 17 '15 at 14:11
  • Probably rethink your structure. The pattern you are describing is really an anti-pattern. – MadConan Jun 17 '15 at 14:12
  • Yes, in general using `instanceof`` is a symptom of a bad/poor design. The trick is to use double dispatch or visitor pattern. Look on the web. – Jean-Baptiste Yunès Jun 17 '15 at 14:13
  • It would be easier to give you some examples if you included a bit of detail about how you are using `instanceof`. – MadConan Jun 17 '15 at 14:16
  • possible duplicate of [When is using instanceof the right decision?](http://stackoverflow.com/questions/8957762/when-is-using-instanceof-the-right-decision) – dimo414 Jun 17 '15 at 18:08

6 Answers6

22

Let's say I am writing some inventory code:

public void showInventory(List<Item> items) {
    for (Item item : items) {
        if (item instanceof ContainerItem) {
            // container display logic here
        }
        else if (item instanceof WeaponItem) {
            // weapon display logic here
        }
        // etc etc
    }
}

That will compile and work just fine. But it misses out on a key idea of object oriented design: You can define parent classes to do general useful things, and have child classes fill in specific, important details.

Alternate approach to above:

abstract class Item {
    // insert methods that act exactly the same for all items here

    // now define one that subclasses must fill in themselves
    public abstract void show()
}
class ContainerItem extends Item {
    @Override public void show() {
        // container display logic here instead
    }
}
class WeaponItem extends Item {
    @Override public void show() {
        // weapon display logic here instead
    }
}

Now we have one place to look, the show() method, in all our child classes for inventory display logic. How do we access it? Easy!

public void showInventory(List<Item> items) {
    for (Item item : items) {
        item.show();
    }
}

We are keeping all the item-specific logic inside specific Item subclasses. This makes your codebase easier to maintain and extend. It reduces the cognitive strain of the long for-each loop in the first code sample. And it readies show() to be reusable in places you haven't even designed yet.

Matt Stephenson
  • 8,442
  • 1
  • 19
  • 19
  • 2
    This is the correct way of doing it, but sometimes it is not enough. Take for instance the class `Record` - we have 2 kind of records - `File` and `Directory`. The common stuff can be abstracted in record, but lets say `File` has a `download()` method. So from a `Set` you cannot download the files unless you know that they are indeed files. So if you fall into that case, polymorphism is not enough, but in most cases it's the best solution, so +1 for the good answer – Svetlin Zarev Jun 17 '15 at 14:35
  • @SvetlinZarev You can abstract that, too - by implementing methods that make sense either to throw UnsupportedOperationException (e.g. java.util.Iterator.remove), providing a Null-Object/Default return or adding methods to query if a method is applicable. Its all a question of effort, not a principal impossibility. – Durandal Jun 17 '15 at 18:05
  • 2
    I consider it bad design to add methods to an interface, just to throw `UnsupportedOperationException`. Also the purpose of the null-object pattern is completely different. Yes, it might get the job done, but it'll be more like a hack, than areal solution. Take for instance my 'Record' example. Why should I pollute the `Record` interface with methods which are not applicable to each record ? By doing this I'm only making the API more difficult to use - i.e. the clients will have to deal with stupid exceptions or usless return values. – Svetlin Zarev Jun 17 '15 at 19:06
  • @svetlin zarev how would implement the download method in your example? – markus Feb 16 '18 at 14:41
8

IMHO using instanceof is a code smell. Simply put - it makes your code procedural, not object oriented. The OO way of doing this is using the visitor pattern.

enter image description here

The visitor pattern also allows you to easily build decorators and chain of responsibility on top of it, thus achieving separation of concerns, which results in shorter, cleaner and easier to read and test code.

Also do you really need to know the exact class ? Cant you take advantage of polymorphism ? After all Axe IS a Weapon just as Sword is.

Svetlin Zarev
  • 14,713
  • 4
  • 53
  • 82
  • 1
    I disagree, the viditor pattern allows you to easily build `decorator` and `chain of responsibility` on top of it to achieve `separation of concerns`, and thus making your code much simpler and easier to read. It also enforces type safety - you cannot add new subclass of T without stumbling on a compile time error, while eith `instanceoff` you'l ldiscover the issue as late as during runtime – Svetlin Zarev Jun 17 '15 at 14:17
  • My comment was not serious, but I have tried repeatedly to get my head around the visitor pattern, and never succeeded. I have had to maintain code with the visitor pattern and found it frustrating. – NickJ Jun 17 '15 at 14:22
  • Well I will need to do some intense googling to understand this pattern.... But correct me if I'm wrong, but isn't it very similiar to @Matt Spephensons post? – Justas S Jun 17 '15 at 17:52
  • 1
    No, it isn't. Also Matt posted it after me. He is showing you how to take advantage of the polymorphic features of the language. And basically his approach is the best solution to the problem, but sometimes it's not enough - see my comment under his post. My post is about the OO way of doing `instanceof` - it's a technique called `double dispatch` and it is implemented through the visitor pattern. But don't get me wrong. My answer is not the same or opposite to his. It is complimentary. You cannot implement the visitor if you don't correctly implement your class hierarchy. – Svetlin Zarev Jun 17 '15 at 18:59
  • I didn't mean anything offensive. The visitor patterns seems complicated to me and the tutorials I saw were pretty similiar to his code. – Justas S Jun 18 '15 at 13:53
  • Using visitor pattern will still result in empty visit functions right? Is that okay to have? – Gopikrishna K S Oct 02 '22 at 21:32
3

You should rethink your structure, instanceof in non-meta code is almost always a sign for an anti-pattern. Try to define the behaviour all Items have in common (like having a picture, a description and something happening when you click on them) in the Item-class/interface, making use of the abstract-keyword if appropiate, and then use polymorphism to implement the specifics.

LionC
  • 3,106
  • 1
  • 22
  • 31
3

You should rethink maybe and try to use polymorphism to implement your List<Item> idea.

Here is some references for your problem that can probably help :


(References from Is instanceof considered bad practice? If so, under what circumstances is instanceof still preferable? )

Community
  • 1
  • 1
Valentin Montmirail
  • 2,594
  • 1
  • 25
  • 53
1

It's ok if it's easy for you to understand.

Moving branching logics from where they naturally belong all to subclasses is not necessarily a good idea. It may create the wrong dependency; it may cause bloated classes, and it may be hard to navigate and understand.

In the end, it's all about how to physically organize our code with multiple dimensions of concerns in a one dimensional space. It's not a trivial problem, it is subjective, and there is no panacea.

In particular, our industry inherited a lot of rules of thumbs that were made in the last century based on technical and economical constraints of that era. For example, tooling were very limited; programmers were highly expensive; applications evolved slowly.

Some of these rules may no longer apply today.

ZhongYu
  • 19,446
  • 5
  • 33
  • 61
-1

I don't necessarily think instanceof is bad for coders who know what they are doing and use it to avoid having to write more complicated code to get around it. There is a use for everything, and also a mis-use.

With that said, the description you provide does not require instanceof. There are various ways you can implement this without instanceof, and (the most important thing) the alternate solution must be better than using instanceof. Don't just go with a non-instanceof solution to avoid instanceof because you heard it is bad.

I think that for your scenario an non-instanceof solution has benefits in making the solution more easily extensible.

for (Item i: items) {
    if (ItemTypes.WEAPON_ITEM.equals(i.getType)) {
        processWeaponType(i);
    }

}

or

for (Item i: items) {
    if (WeaponItem.class.equals(i.getType)) {
        processWeaponType(i);
    }

}
Jose Martinez
  • 11,452
  • 7
  • 53
  • 68
  • well, but your solution isn't any more readable than `instanceof`. Performance-wise, `instanceof` is really fast; it's definitely faster than comparing our own `type` field; it may even be faster than comparing `Class` with `==`. – ZhongYu Jun 17 '15 at 14:55
  • 1
    Maybe, but is speed the desired outcome or software design and architecture with regards to this question? I mean we can get rid of classes all together and just put everything in one long method if you really think the difference in performance is worth considering for this question. Being that the JIT compiler will re-rorganize your code anyways, its hard to tell which is faster. – Jose Martinez Jun 17 '15 at 16:09
  • This is not better than instanceof, its a *disguise* for instanceof. Neither suggestion solves the problem of having to check/update all client code when adding a new child type. – Durandal Jun 17 '15 at 18:09