![]() |
|
|
|||||||
![]() |
Java - instanceof NOT (always) bad? The instanceof myth. |
|
|
Thread Tools | Search this Thread |
|
|
#1 |
|
Posts: n/a
|
Hi all.
I just decided to write a message regarding the myth that the use of the instanceof keyword is categorically bad. Not all java programmers have fallen prey to the myth, but after years on the newsgroups it has almost gotten to the point where if it was discovered that you used instanceof anywhere in your app, it *must* be because you have a poorly designed system. Is this a situation of 'bad-practice-phobia', where the fear of being thought of as a newbie programmer (or worse, a 'hack' who still 'doesn't get' OO) makes people create the most elaborately complex and unmaintainable designs to avoid using a specific 'bad practice'? I think what's very often forgotten is that a bad practice can never be reduced to something as simplistic as 'never ever use keyword X'. All bad practices have there 'scope of ill-use'--which may be very big--but are almost never (if ever) absolute. Specifics: Alex Blewitt's Javaworld article ================================ It has been said that, in most cases, instanceof should not be used to test for object equality (see [BLEWITT1]) as it can break the symmetric property of the equals method's equivalence relation. In this case, using the getClass method of Object is the appropriate choice. This makes a lot of sense. But notice that Mr. Blewitt's article never simply says 'always use getClass over instanceof'. He explicitly states a specific situation in which using instanceof is not appropriate. So next time someone cites Mr. Blewitt's article to prove your use of instanceof is bad without even seeing your code, you can be fairly certain they've probably never actually read the article. They are simply deep in the 'instanceof is always, always bad' myth. Yoe see, getClass will always return the runtime class of an object and is useless if what you want to determine is whether an object falls within a specific inheritance hierarchy. In the example below, we want to determine if an object's class implements a specific interface. Here getClass does not help us. class A implements IShared { } A a = new A(); a instanceof A; // true a instanceof IShared; // true a.getClass() == A.class // true a.getClass() == IShared.class // false Now, of course, this is a contrived example with no context, but the need to check whether an object can be polymorphically treated as a specific type--wherever that type may exist in its inheritance hierarchy--can often be useful in practice (see the references section for some specific examples of this, such as those uses found in the Ecplipse framework). Avoiding instanceof =================== Another thing you may find on the net are people who will try their darndest to provide you with an example of how you can get around using instanceof. They will explain that you need their complex, difficult to maintain, cyclic-dependency-creating solutions because your design is bad (you used instanceof right?... so it *must* be bad), and that your solution will certainly be a nightmare to maintain in the future. Let's take a brief look at some of the common 'solutions'. Use method overloading instead ============================== Some may say that you simply need to overload a number of methods with different class parameters. Why use instanceof to determine if an object's class implements a specific interface? Let the overloading functionality of the language do this for you! The problem is that interface Animal { public void move(); } class Fish implements Animal { public void move() { System.out.println("swim!"); } public void eatAlgae(){} } class Mouse implements Animal { public void move() { System.out.println("scurry!"); } public void makeHoleInWall(){} } So now we can create methods such as class Test { public void scare(Mouse m) { m.move(); m.makeHoleInWall(); } public void scare(Fish fish) { f.move(); f.eatAlgae(); } public Test() { Animal fish = new Fish(); scare(fish); // does not compile } } So what happened? Why doesn't this even compile? Because the fish object's compile-time type is Animal, *not Fish*. Of course, we could get around this by creating a new scare(Animal a) method, but then we're right back where we started. Visitor (and Proxy Visitor) =========================== Another option that is often brought up is the use of the Visitor pattern. The problem with this is that it creates a huge maintenance issue. Visitor requires updating all of the 'visitable' classes every time we add new visitable class into the pattern. Why not use adapter classes then? The problem is that in Java, its not always simple for 'visitable' classes to extend a 'VistableAdapter' (to avoid implementing an accept method for every possible visitable class) due Java's single inheritance (e.g.: if your class is already extending a class, you're out of luck unless you want to get into some messy code). This turns the Visitor pattern into a maintenance nightmare. Now I have obviously skimmed over this one very quickly, but this is because Robert C. Martin has already ready written on this better than I could. Check out his web page on the subject [MARTIN1], as well (if you have ACM Portal access) his published paper on it [MARTIN2]. Other people using instanceof ============================= I encourage people to check out some of the other sources I've listed here. Maybe if people found out that the Eclipse framework uses instanceof [OTAKU], or if after they read an article by Bill Venners with a section explicitly titles 'When to use instanceof' [VENNERS], they will see that one should always beware of grand sweeping generalizations in any situation. So go ahead and use instanceof when it's appropriate, avoid it when it's not appropriate, and don't believe the hype! Cheers! Michael N. Christoff References ========== BLEWITT Object equality--Writing equals and hashCode methods for data objects http://www.javaworld.com/javaworld/j...14-equals.html MARTIN1 Visitor vs instanceof http://butunclebob.com/ArticleS.Uncl...rsusInstanceOf MARTIN2 Acyclic visitor http://portal.acm.org/citation.cfm?i...CM&coll=portal VENNERS See section "When to use instanceof" at the following link: http://www.artima.com/objectsandjava...terfaces1.html OTAKU See section "Extensibility the interface way" http://www.beust.com/weblog/archives/2005_06.html |
|
|
|
#2 |
|
Posts: n/a
|
<> wrote in message news: oups.com... > > I think what's very often forgotten is that a bad practice can never be > reduced to something as simplistic as 'never ever use keyword X'. All > bad practices have there 'scope of ill-use'--which may be very big--but > are almost never (if ever) absolute. Never use keyword "goto" (unless your goal is to make your Java code not compile). > > Specifics: > > > > Alex Blewitt's Javaworld article > ================================ > It has been said that, in most cases, instanceof should not be used to > test for object equality (see [BLEWITT1]) as it can break the symmetric > property of the equals method's equivalence relation. In this case, > using the getClass method of Object is the appropriate choice. This > makes a lot of sense. > > But notice that Mr. Blewitt's article never simply says 'always use > getClass over instanceof'. He explicitly states a specific situation > in which using instanceof is not appropriate. So next time someone > cites Mr. Blewitt's article to prove your use of instanceof is bad > without even seeing your code, you can be fairly certain they've > probably never actually read the article. They are simply deep in the > 'instanceof is always, always bad' myth. Actually, I think it's a good idea to use instanceof in the equals() method. Here's how I usually implement it (written off the top of my head, might contain some syntax errors, but hopefully the semantics is clear): <code> class Foo { private int myField; @Overrides public boolean equals(Object other) { if (other == this) { return true; } if (other instanceof Foo) { return this.equals((Foo)other); } return false; } public boolean equals(Foo other) { if (other == this) { return true; } if (!this.getClass().equals(other.getClass())) { /*this is explained below*/ return other.equals(this); } if (this.myField != other.myField) { return false; } return true; } } </code> The .getClass() conditional is basically checking whether other's exact class is Foo, or if it is a strict-subclass of Foo. In the case that it is exactly a Foo, then we have all the information we need to determine equality, and so the method proceeds as normal. In the case that "other" is a subclass of Foo, then I invoke that subclasses equals() method, because the subclass would knows more about Foo than Foo knows about the subclass. Note that with this pattern, all subclasses of Foo *MUST* override the equals(Foo) method, or else you'll end up with an infinite recursion. [...] > > > Use method overloading instead > ============================== > Some may say that you simply need to overload a number of methods with > different class parameters. Why use instanceof to determine if an > object's class implements a specific interface? Let the overloading > functionality of the language do this for you! > > The problem is that > > interface Animal > { > public void move(); > } > > class Fish implements Animal > { > public void move() > { > System.out.println("swim!"); > } > > public void eatAlgae(){} > } > > class Mouse implements Animal > { > public void move() > { > System.out.println("scurry!"); > } > > public void makeHoleInWall(){} > } > > > So now we can create methods such as > > class Test > { > > public void scare(Mouse m) > { > m.move(); > m.makeHoleInWall(); > } > > public void scare(Fish fish) > { > f.move(); > f.eatAlgae(); > } > > public Test() > { > Animal fish = new Fish(); > > scare(fish); // does not compile > } > > } > > > So what happened? Why doesn't this even compile? Because the fish > object's compile-time type is Animal, *not Fish*. Of course, we could > get around this by creating a new scare(Animal a) method, but then > we're right back where we started. > I'm not sure that this example is an argument for/against using instanceof. At best, it's an argument against Java's choice of resolving method calls based on the declared type of a reference, rather than the runtime type. A cleaner design that doesn't involve instanceof or its lack thereof might be: <code> public interface Animal { public void actScared(); } public class Test { public Test() { Animal fish = new Fish(); fish.actScared(); } } </code> > > > Visitor (and Proxy Visitor) > =========================== > Another option that is often brought up is the use of the Visitor > pattern. The problem with this is that it creates a huge maintenance > issue. Visitor requires updating all of the 'visitable' classes every > time we add new visitable class into the pattern. > > Why not use adapter classes then? The problem is that in Java, its not > always simple for 'visitable' classes to extend a 'VistableAdapter' (to > avoid implementing an accept method for every possible visitable class) > due Java's single inheritance (e.g.: if your class is already extending > a class, you're out of luck unless you want to get into some messy > code). This turns the Visitor pattern into a maintenance nightmare. > > Now I have obviously skimmed over this one very quickly, but this is > because Robert C. Martin has already ready written on this better than > I could. Check out his web page on the subject [MARTIN1], as well (if > you have ACM Portal access) his published paper on it [MARTIN2]. I only read [MARTIN1], and it seems the main argument is to "avoid recompiling", which IMHO isn't a significant gain. - Oliver |
|
|
|
#3 |
|
Posts: n/a
|
On Tue, 20 Jun 2006 19:03:54 +0100, <> wrote:
> Yoe see, getClass will always return the runtime class of an object and > is useless if what you want to determine is whether an object falls > within a specific inheritance hierarchy. In the example below, we want > to determine if an object's class implements a specific interface. > Here getClass does not help us. This isn't an argument for or against instanceof, but you can achieve what you describe by using the Class object returned by getClass: http://java.sun.com/j2se/1.5.0/docs/...ava.lang.Class) Dan. -- Daniel Dyer http://www.dandyer.co.uk |
|
|
|
#4 |
|
Posts: n/a
|
<> wrote:
> I just decided to write a message regarding the myth that the use of > the instanceof keyword is categorically bad. Not all java programmers > have fallen prey to the myth, but after years on the newsgroups it has > almost gotten to the point where if it was discovered that you used > instanceof anywhere in your app, it *must* be because you have a poorly > designed system. On these newsgroups? I've followed these newsgroups for many years (since about 199 the impression has been given, then it is incorrect. What has often been said is that avoiding runtime type dependencies in general is a good idea whenever possible. Typically, though, this advice is applied to reflection instead of use of the instanceof keyword. I can also see that there are a lot of places where someone unfamiliar with OO programming may wish to use instanceof, but which should be replaced with polymorphism. Again, though, I see no justification for an absolute rule forbidding instanceof. Indeed, inheritance is often poorly applied to situations where it breaks proper separation of concerns. > It has been said that, in most cases, instanceof should not be used to > test for object equality (see [BLEWITT1]) as it can break the symmetric > property of the equals method's equivalence relation. In this case, > using the getClass method of Object is the appropriate choice. This > makes a lot of sense. I am completely with you on this one. I have noticed a tendency (not so much on this newsgroup, but in other places, at least, such as introductory textbooks of fly-by-night tutorial web sites that are sometimes posted here) to do the following: 1. Assume that the programmer intends to write a certain kind of very mechanical implementation of the equals or hashCode methods. 2. Draw conclusions about how to uphold their contracts, on the basis of these mechanical implementations. 3. Forget that less mechanical implementations are possible. This tendency is exaggerated by the section (which is a good one, by the way) in Josh Bloch's Effective Java that gives a mechanical way to handle hashCode (but that *also* notes that it only applies to information that is relevant to object identity... a qualification that is often lost in translation), and by the kinds of silly introductory examples to overriding hashCode and equals that are frequently in intro textbooks or tutorials. An excellent example of where this does NOT apply is java.util.List. The contract for java.util.List.equals contains a requirement that any List should compare equal to any other List with the same contents. I think this is a very poor requirement, but it is nevertheless a requirement. Beginning an implementation of MyList.equals by comparing this.getClass() to other.getClass() is a sure-fire guarantee of breaking the contract. What is true, though, is that in the absence of some specification for how to declare dissimilar classes, the task shoulod be considered impossible... i.e., they should always be considered unequal. That specification logically ought to occur in the documentation of some supertype. Hence, it may be valid to say that instanceof should never be used to check equals UNLESS there is some specification in some supertype that defines equality for some containing inheritance sub- hierarchy. > Another thing you may find on the net are people who will try their > darndest to provide you with an example of how you can get around using > instanceof. They will explain that you need their complex, difficult > to maintain, cyclic-dependency-creating solutions because your design > is bad (you used instanceof right?... so it *must* be bad), and that > your solution will certainly be a nightmare to maintain in the future. Hopefully, you don't mean to include here situations in which someone provides not only general advice on how to avoid using instanceof, but also specific negative consequences of the use of instanceof, and how their proposed solutions avoids those drawbacks. For example, one common problem with instanceof is that it introduces a single point that must be extended in order to completely integrate a new subtype into the system. If the solution can avoid this by moving certain type-specific code into the subclass itself, this is nearly always a good thing. Therefore, there is reason to be cautious with this keyword when working in a situation where the list of possible subtypes is not theoretically limited, such as when working with a hierarchy of objects representing different accounting policies for various legal jurisdictions. > Use method overloading instead > ============================== > Visitor (and Proxy Visitor) > =========================== You missed the obvious one. If possible, take the type-specific behavior, and move it into a polymorphic method of the class. Doing so can require some creativity in design... but that's because you're really doing design rather than following someone's furmulaic answer. Design is a creative process. It involves finding and using the abstractions that are inherent to the system. My suspicion is that when most advice is given to avoid instanceof, this is what is meant. Don't use instanceof, but rather identify and describe the inherent abstractions that allow you to write something that applies to all relevant types. If the advice is taken to mean something else, such as that a specific trick should be employed to remove the keyword in an automatable way, then someone has profoundly missed the point. > Other people using instanceof > ============================= Note that most of the really good examples of using instanceof come from writing code that is inherently somewhat dynamic systems. The only exception I can think of comes from so-called "marker" interfaces, which are problematic from a design standpoint anyway; or from backward- compatibility limitations that prevent making appropriate changes to a common supertype. -- Chris Smith - Lead Software Developer / Technical Trainer MindIQ Corporation |
|
|
|
#5 |
|
Posts: n/a
|
Daniel Dyer wrote:
> This isn't an argument for or against instanceof, but you can achieve what > you describe by using the Class object returned by getClass: > http://java.sun.com/j2se/1.5.0/docs/...ava.lang.Class) "Foo.class.isAssignableFrom(object.getClass()) " is just a very roundabout and awkward version of "object instanceof Foo" is it not? |
|
|
|
#6 |
|
Posts: n/a
|
On Wed, 21 Jun 2006 08:04:49 +0100, Twisted <> wrote:
> Daniel Dyer wrote: >> This isn't an argument for or against instanceof, but you can achieve >> what >> you describe by using the Class object returned by getClass: >> http://java.sun.com/j2se/1.5.0/docs/...ava.lang.Class) > > "Foo.class.isAssignableFrom(object.getClass()) " is just a very > roundabout and awkward version of "object instanceof Foo" is it not? Yes, but the point is that the OP claimed it was not possible. instanceof would be preferable in most situations, but isAssignable from can be useful in a situation where you don't know at compile-time exactly which class that you are using. Dan. -- Daniel Dyer http://www.dandyer.co.uk |
|
|
|
#7 |
|
Posts: n/a
|
Chris Smith wrote:
> An excellent example of where this does NOT apply is java.util.List. > The contract for java.util.List.equals contains a requirement that any > List should compare equal to any other List with the same contents. I > think this is a very poor requirement, but it is nevertheless a > requirement. I hadn't realised that. Live and learn... I like the idea that a List<java.awt.Point> can be equal to a List<byte[]> > My suspicion is that when most advice is given to avoid instanceof, this > is what is meant. Don't use instanceof, but rather identify and > describe the inherent abstractions that allow you to write something > that applies to all relevant types. If the advice is taken to mean > something else, such as that a specific trick should be employed to > remove the keyword in an automatable way, then someone has profoundly > missed the point. +1 (and in spades). > Note that most of the really good examples of using instanceof come from > writing code that is inherently somewhat dynamic systems. The only > exception I can think of comes from so-called "marker" interfaces, which > are problematic from a design standpoint anyway; or from backward- > compatibility limitations that prevent making appropriate changes to a > common supertype. And +1 again. -- chris |
|
|
|
#8 |
|
Posts: n/a
|
Chris Uppal wrote:
> I like the idea that a List<java.awt.Point> can be equal to a List<byte[]> I think it's more that a LinkedList<Foo> may be equal to an ArrayList<Foo>. I expect sets are even more flexible, comparing equal no matter what order the objects are in too. > +1 (and in spades). > > And +1 again. What's this -- an obscure way of saying "I agree" or "Seconded"? You do realize it is possible to take conciseness *too* far? |
|
|
|
#9 |
|
Posts: n/a
|
Twisted <> wrote:
> Chris Uppal wrote: > > +1 (and in spades). > > > > And +1 again. > > What's this -- an obscure way of saying "I agree" or "Seconded"? You do > realize it is possible to take conciseness *too* far? It is a means of expressing agreement. I believe it originated (at least I first saw it) in open-source software project practices. In that setting, it's an official voting mechanism: someone makes a suggestion about a course of action on the project mailing list, and any active developers (often, committers) for the project may post +1 or -1 or 0, and the votes are all tallied at the end to see whether the vote passed or failed. I doubt it is used merely for conciseness; it's a sort of cultural heritage thing, as well. -- Chris Smith - Lead Software Developer / Technical Trainer MindIQ Corporation |
|
|
|
#10 |
|
Posts: n/a
|
Chris Smith schrieb:
> Twisted <> wrote: >> Chris Uppal wrote: >>> +1 (and in spades). >>> >>> And +1 again. >> What's this -- an obscure way of saying "I agree" or "Seconded"? You do >> realize it is possible to take conciseness *too* far? > > It is a means of expressing agreement. > > I believe it originated (at least I first saw it) in open-source > software project practices. In that setting, it's an official voting > mechanism: someone makes a suggestion about a course of action on the > project mailing list, and any active developers (often, committers) for > the project may post +1 or -1 or 0, and the votes are all tallied at the > end to see whether the vote passed or failed. > > I doubt it is used merely for conciseness; If you call for votes on a topic and receive hundreds of replies, the conciseness of +1/-1 is very useful. It is much easier to count occurrences of "+1" instead of having to consider dozens of variants of the expression of agreement. It also helps to keep a voting thread from trailing off. Timo |
|
![]() |
| Thread Tools | Search this Thread |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| Debunking the myth of DVD rot | Smaug69 | DVD Video | 15 | 08-22-2004 01:16 PM |
| Walt Disney: The Man Behind the Myth DVD - any good online prices? | Mark | DVD Video | 0 | 04-13-2004 05:09 AM |
| Walt: The Man Behind the Myth DVD - over $20 everywhere? | John | DVD Video | 0 | 04-08-2004 02:52 PM |