Rescuing instanceof from abuse

  WE DO OUR best to keep our maxims positive and encouraging in tone. We'd rather not be perceived as Java thought-police, snooping 'round to detect violations of good practice and beating up on offenders. But poor instanceof is itself so easily abused that we feel obliged to protect it from mistreatment with a negative maxim.

MAXIM 5: DON'T USE INSTANCEOF IN PLACE OF POLYMORPHISM
We'll take you through a number of examples of what we mean by mistreatment. Some of our later examples are commonly accepted practice, and appear in code from experienced Java developers. We start, though, with an example that we know all experienced developers would shun; nevertheless, it's surprising how often egregious examples like this are seen in production code. See if you can detect the abuse of instanceof here:



class Bathroom {
	String getSize() { return "small"; }
}
class Kitchen {
	String getSize() { return "medium"; }
}
class Lounge {
	String getSize() { return "large"; }
}
class House {
	private Object[] rooms = new object[3];

	public House() {
		rooms[0] = new Bathroom();
		rooms[1] = new Kitchen();
		rooms[2] = new Lounge();
	}

	public void displaySizes() {
	  for (int i = 0; i < rooms.length;="" ++i)="" {="" string="" size;="" if="" (rooms[i]="" instanceof="" lounge)="" size="((Lounge)" rooms[i]).getsize();="" else="" if="" (rooms[i]="" instanceof="" kitchen)="" size="((Kitchen)" rooms[i]).getsize();="" else="" if="" (rooms[i]="" instanceof="" bathroom)="" size="((Bathroom)" rooms[i]).getsize());="" system.out.println("room="" "="" +="" (i+1)="" +="" "="" is="" "="" +="" size);="" }="" }="" }="">


What is the problem here? Just this: the writer of this code doesn't understand polymorphism. Well, hang on, protests the coder, I do understand polywhatever. It means that an object of a superclass can take on the form of an object of one of its subclasses. Just as each of the elements in my rooms array (of class Object) can also be made to look like an instance of (instanceof, get it?) the actual class they were constructed from. The coder does have a glimmer of understanding of polymorphism. It is true that polymorphism allows the programmer to use a superclass reference to refer to an instance of one of its subclasses. But the coder's glimmer of comprehension fades completely when he or she uses instanceof. Someone who truly understands polymorphism wouldn't do that. They'd extend the various room classes from a common superclass (other than the implicit Object superclass) so that polymorphism may be used. In this example, they'd define a Room class with an abstract getSize() method, which ensures that all subclasses of the Room class will provide a suitable implementation of the getSize() method.


abstract class Room {
	abstract String getSize();
}

class Kitchen extends Room {
	String getSize() { return "medium"; }
}


Each of the room classes are made subclasses of Room, with their getSize() methods each an implementation of Room's getSize(). The array of Objects becomes an array of Rooms and the for loop is rewritten like this:


for (int i = 0; i < rooms.length;="" ++i)="" system.out.println(="" "room="" "="" +="" (i+1)="" +="" "="" is="" "="" +="" room.getsize();="">


But hey, the original coder says, that won't work. How does the system know what class the Room object really belongs to unless I tell it? Which version of getSize() is it going to invoke?
However, despite the simplicity of this scheme, it demonstrates the trickiness of working with multiple threads, as the peril we spoke of is lurking within the mechanicalService() method. The problem arises when the maintenance crew determines that the airplane is not airworthy and decides to replace it with a new plane that the airline keeps for just this sort of contingency:


airplane = new Airplane();


This new instance is not locked-that's okay though, because only the mechanical crew is privy to this exchange of airplanes (the other crews will go about their tasks unaware that the planes have been switched).
That's where the coder shows that his or her understanding of polymorphism is a mere glimmer. Fully polymorphic objects, such as Java allow us to have know implicitly what essential type they are, without the coder having to carry out any checking. So when the code says rooms[0].getSize(), the result is the bathroom's size. The runtime system has dynamically resolved the Room object reference (rooms[0]) to the Bathroom object it refers to. No need for instanceof.
Exactly what is a polymorphic object? It's an object whose true type is hidden behind the façade of a superclass. Ostensibly, the type of rooms[0] is Room, but hidden behind that cover is an actual Bathroom object. We can also say that Room is the static type of rooms[0] while Bathroom is the dynamic type. With the polymorphic getSize() method, we've eliminated the need for instanceof because each Room object is able to report its size without regard to its actual type. The coder who's wise to polymorphism doesn't use instanceof where method overriding will do all the work. Here's a good general rule to remember (we'll see later on, that like all useful rules, this one has its exceptions): Any design that depends on instanceof can be redesigned to use polymorphism and vice versa. Polymorphism, seen at its most powerful, eliminates the need to actually know what the "real" types of objects are. The first example merely exhibits a do-it-yourself polymorphism where the developer is performing the hard work, using (or rather, abusing) instanceof to determine objects' actual types. A full engagement of polymorphism would eliminate the need. Thinking in terms of polymorphic types and performing similar operations through superclass references instead of thinking about the actual types of objects is a good habit to get into. Different cases need to be handled in different ways. Here's an example that requires a different approach to convert it to a polymorphic design:
TWe're writing an inspect() method for the House class to have the architect check every room and the plumber check every bathroom (and only bathrooms). Our first sketch of the method looks like this:


void inspect(Room room) {
	// check(Room R) in Architectclass
	architect.check(room); 
	// check(Bathroom b) in Plumberclass
	
plumber.check(room); // won't compile
	}


Because Plumber's check() method is designed to deal only with Bathroom objects, the compiler refuses to accept an argument of type Room. But because a method is being invoked with the Room object as its argument instead of a method of the Room class being invoked against the object itself, it doesn't offer us an overridden method to invoke. Of course, we could use instanceof to reveal its dynamic type. If it meets this test, it then needs to be downcast to the Bathroom subclass to meet the argument requirements for Plumber's check():


void inspect(Room room) {
	architect.check(room);

	if (room instanceof Bathroom)
		plumber.check((Bathroom) room);
}

But if we resort to this convenient solution, we need to be aware that we'll pay heavily for our use of instanceof. That payment falls due when we extend our repertoire of classes. Suppose we add another type of room, a type that wasn't contemplated when our House was being built. We decide to add a music room and we need its acoustics checked. This addition requires us to carry out reconstructive surgery on inspect():


void inspect(Room room) {
	architect.check(room);

	if (room instanceof Bathroom)
		plumber.check((Bathroom) room);

	if (room instanceof MusicRoom)
		acousticsExpert.check((MusicRoom) room);
}

Such intrusive recoding is plainly undesirable. In line with our maxim, can we in some way accomplish the same outcome non-intrusively with the use of polymorphism? Indeed we can. We only need take a more object-oriented approach to the design of the classes in our KitSetHomes package. And in carrying out this redesign, we'll see that this current use of instanceof points to a more subtle case of design pathology.
The solution we used with the getSize() method won't work here though, because the check() method is a method of the Architect, Plumber, and AcousticsExpert classes, and not of the Room class (and in each case, with a different method signature).
Let's assume that a requirement to use instanceof indicates a fundamental fault in the design, and consider where the responsibility for the house inspection currently lies. It's the House class that has been given that responsibility. It's obliged to take each room in turn and carry out the inspection itself, using its own inspect() method and subjecting each Room object in turn to what it has determined to be an appropriate inspection.
Think about what's going on here. In effect, it's nothing but good old procedure-oriented manipulation of passive data items that have no behavior of their own! In an object-oriented language like Java, we can (and should) do better.
Objects of the Room class should be savvy enough to carry out their own inspection: room.inspect(). We first need to give our Room superclass an appropriate default behavior for inspect():


abstract class Room {
	void inspect() {
		architect.check(this);
	}
}

Then, as we create a subclass of Room, we either choose to let it simply inherit Room's default behavior or to override it with a specialized implementation, as in the case of Bathroom:


class Bathroom {
	void inspect() {
		// note the polymorphism here too:
super.inspect(this);   
		plumber.check(this);
	}
}
Now instanceof has no need to make an appearance in House.inspect():
class House {
	void inspect(Room room) {
		room.inspect();
	}
}

The beneficial outcome is that when we add MusicRoom to Room's family of subclasses (overriding the inherited inspect() method), we now need make no intrusion into the House class. Herein lies the real benefit of polymorphism compared against manual type checking with instanceof: solutions based on polymorphism permit greater extensibility and more complete encapsulation by reducing the need to modify existing classes when new ones are added to the system. There are some situations where existing designs, which were not architected to take advantage of polymorphism, force consumers of the class to use instanceof. Event handlers which use the 1.0 Abstract Window Toolkit (AWT) are examples. Here's one that came from the SunSoft stable (see Geary, D. and A. McClellan, Graphic Java, SunSoft Press, Palo Alto, CA, 1997):

public boolean 
action (Event event, Object what) {
		if (event.target instanceof Button)
			System.out.print("Button:  ");
		if (event.target instanceof Choice)
			System.out.print("Choice:  ");
		if (event.target instanceof TextField)
			System.out.print("TextField:  ");
		if (event.target instanceof Checkbox)
			System.out.print("Checkbox:  ");

		return true;
	}

There is no alternative but to use instanceof here, because within action(), the actual type of event.target has been lost behind its ostensible Object type. Although the button, for example, was constructed as an object of class Button, that type information has been lost by the time the event handler hands it back as belonging to the Object class (actually, the type information was first lost when it was passed to the Container class's add() method, which takes an argument of type Component).* It's rather like lending a favorite book and having it returned minus its cover. The only way for the implementation of action() to recover that essential information is through dynamic type identification, and instanceof offers the only way of doing that.
Far better, of course, to undertake a radical redesign of the windowing event handling mechanism to allow a truly object-oriented approach. That is exactly what Sun has done with the 1.1 AWT and instanceof is no longer needed.
There's one other situation where instanceof has to serve in place of polymorphism. That is where we simply desire to know the actual type of an object without wanting it to perform any operation. To show you a typical situation of this sort, we'll set out to implement a method of the House class that will return the number of bathrooms it has. Doing our best to follow our maxim to the max, we'll strive to avoid the use of instanceof.


class House {
		int numberOfBathrooms();
}

To avoid using instanceof we could create a method in the Room superclass that will allow us to tell if a particular Room reference actually refers to a Bathroom instance without having to know the actual type of each Room object.

abstract class Room {
	boolean isBathroom() { return false; }
}

The Bathroom class would implement this method to return true, while all the other Room subclasses would accept this definition from Room. Now we can tell which Room objects should be counted in the total without explicitly testing their dynamic types.


int numberOfBathrooms() {
	int bathrooms = 0;

	for (int i = 0; i < rooms.length;="" ++i)="" if="" (rooms[i].isbathroom())="" ++bathrooms;="" return="" bathrooms;="" }="">

But let's not rush to congratulate ourselves on having once more saved instanceof from mistreatment. We need to consider what happens on each occasion we add another room subclass to the hierarchy and choose to take a count of rooms of its type, as for Bathroom. Yet again we have to invade and modify the superclass:


abstract class Room {
	// ...
	boolean isMusicRoom() { return false; }
}

This is exactly the sort of intrusive recoding we've been concerned to avoid. Can we eliminate the need for superclass intrusion with a more generalized approach, which would also allow other types of rooms to be counted? We might think of replacing isBathroom(), which only indicates whether the room is a bathroom, with getRoomType() which indicates what the type of the room is:


abstract class Room {
	static final int BATHROOM_ID = 0;
	static final int KITCHEN_ID = 1;
	static final int LOUNGE_ID = 2;

	abstract int getRoomType();
}

class Bathroom {
	int getRoomType() { return BATHROOM_ID; }
}

class Kitchen {
	int getRoomType() { return KITCHEN_ID; }
}

The numberOfBathrooms() method would invoke this new method. The test for whether a particular Room reference actually refers to a Bathroom object:

if (rooms[i].isBathroom())
would be replaced with
if (rooms[i].getRoomType() 
== Room.BATHROOM_ID)

Unfortunately, however, using a getRoomType() instead of isBathroom() still obliges us to invade the Room superclass each time we wish to add a new room type ID:

abstract class Room {
	// ...
	static final int MUSICROOM_ID = 3;
}

We can fix this, though. The essential problem is that we're returning a value from a bounded set. Wouldn't it be nice if there were an unbounded set from which we could instead select the appropriate value? In so doing, we would avoid the necessity of adding a fresh room ID value to the superclass each time we enlarge the set of its subclasses. The Class class provides exactly what we need. Because a new unique Class object is created each time a class is loaded by the system, we can simply have getRoomType() return a reference to that Class object:

abstract class Room {
	abstract Class getRoomClass();
}

class Bathroom {
	Class getRoomClass() {return Bathroom.class; }
}

The numberOfBathrooms() method can now compare the Class object it gets from getRoomClass() with the Class object of the Bathroom class†:

if (rooms[i].getRoomClass() 
== Bathroom.class)

getRoomType()'s signature and operation should look familiar-it's identical in all but name to the getClass() method defined in the Object class! In fact, we can eliminate it from the Room class and all its subclasses. No support methods are now required and the test in numberOfBathrooms() becomes:
if (rooms[i].getClass() == Bathroom.class) But what is really happening here? We're checking whether this Room object is actually a Bathroom object. This test is essentially equivalent to use of the instanceof test we've been so concerned to avoid‡:
if (rooms[i] instanceof Bathroom)


† The .class syntax evaluates to the Class object associated with a type; it was introduced in Java 1.1. An alternative is to use the Class class's static forName() method: Class.forName("KitSetHomes.Bathroom");

So our good polymorphic intentions have brought us full circle. It seemed that instanceof was banished forever from our vocabulary, and here we are uttering it as blatantly as our inexperienced polymorphically-challenged programmer.
What makes our use of instanceof acceptable here is that the only thing we needed to know about the object was its dynamic type. In extenuation of its use, we can rightly plead that we're not using it as a precursor to a downcasting operation, which is the circumstance where the most blatant abuse of instanceof occurs.
It's time now to offer a more precisely focused version of our maxim-and in the process, give it a positive spin: Strive to replace instanceof with polymorphism where it appears as a precursor to a downcasting operation. In saying this, we need to acknowledge that, on occasion, your best efforts will necessarily be frustrated, where the design of others' callback functions obliges you to use instanceof to recover seemingly lost type information.
The abuses of instanceof, spring from a fundamental failure to carry through on the principles of object orientation. Sometimes it's not within your power to rescue instanceof from the mistreatment that results from deficiency in design. In designing your own sets of classes, however, you can choose polymorphic solutions that let you to avoid abuses of instanceof.


‡ Comparing the Class object returned by getClass() tests for an exact type match, whereas instanceof tests whether the reference matches the type argument or a class subclassed from the type argument. !

Featured

Most   Popular
Upcoming Events

AppTrends

Sign up for our newsletter.

Terms and Privacy Policy consent

I agree to this site's Privacy Policy.