Skip to main content

A Java example showing the dangers of implementation inheritance

I ended my previous post with a paragraph from Josh Bloch's book, Effective Java. In his book, Josh explains why inheriting classes from a different package (especially inheriting from a class that may not be designed and documented for inheritance) can be dangerous. Below you will find Josh's example (from Effective Java) that explains the concept. Many many thanks to Josh Bloch for graciously giving me permission to reproduce the example here :-)

The class InstrumentedHashSet is a subclass of HashSet, that maintains a count of the number of objects added. It overrides the add(Object o) and addAll(Collection c) methods from HashSet to maintain a count of objects added to it. These methods increment the count and then delegate responsibility for adding the element to HashSet. The number of objects added can be obtained from the method getAddCount().

This seems like a reasonable way to count objects added to the Set. Unfortunately it's not. But first let's have a look at the code of InstrumentedHashSet.

 InstrumentedHashSet.JPG

 

Below is a test case for the addAll(Collection c) method of InstrumentedHashSet. By the way, this test was written by me, so if there is anything wrong with it, it's entirely my fault :-)

 

InstrumentedHashSetTest.JPG 

 

Now let us run the unit test.

InstrumentedHashSetTestReport.JPG 

Oops :-( we expected a count of 3, but looks like InstrumentedHashSet is reporting 6. Now, how did that happen???

Look again at InstrumentedHashSet. The method addAll(Collection c) calls super.addAll(c) after incrementing the count. This ends up invoking a method implemented in AbstractCollection in the JDK (ver 1.6). It has implemented addAll(Collection c) to iterate through the given Collection and invoke add(Object o) for every element in the Collection. Since we have overriden add(Object o), it is our implementation that get's called. The count is added for every element of the Collection in addAll(Collection c) as well as in add(Object o), resulting in the count increasing by 2 for every object. That's the reason we got a count of 6 instead of 3.

So to summarize. Be very careful when you subclass a class that is not in your control. It might be safer to use composition that inheritance in such situations. It is also equally important to remember that if you create a non-final class with public methods, please design it carefuly to ensure that it does not break a subclass that may extend from it. If there are any caveats, be sure to document them clearly.



Note: This text was originally posted on my earlier blog at http://www.adaptivelearningonline.net
Here are the comments from the original post

-----
COMMENT:
AUTHOR: Sanket Daru
URL:
DATE: 10/20/2007 11:40:37 AM
This was a serious eye opener.

Learnt a very good OOP lesson.

Comments

Popular posts from this blog

Running your own one person company

Recently there was a post on PuneTech on mom's re-entering the IT work force after a break. Two of the biggest concerns mentioned were : Coping with vast advances (changes) in the IT landscape Balancing work and family responsibilities Since I have been running a one person company for a good amount of time, I suggested that as an option. In this post I will discuss various aspects of running a one person company. Advantages: You have full control of your time. You can choose to spend as much or as little time as you would like. There is also a good chance that you will be able to decide when you want to spend that time. You get to work on something that you enjoy doing. Tremendous work satisfaction. You have the option of working from home. Disadvantages: It can take a little while for the work to get set, so you may not be able to see revenues for some time. It takes a huge amount of discipline to work without a boss, and without deadlines. You will not get the benefits (insuranc

Testing Groovy domain classes

If you are trying to test Grails domain class constraints by putting your unit test cases in the 'test/unit' directory, then your tests will fail because the domain objects will not have the 'valdate' method. This can be resolved in two ways: Place the test cases inside test/integration (which will slow things down) Use the method 'mockForConstraintsTests(Trail)' to create mock method in your domain class and continue writing your test cases in 'test/unit' What follows is some example code around this finding. I am working on a Groovy on Grails project for a website to help programmers keep up and refresh their skills. I started with some domain classes and then moved on to write some unit tests. When we create a Grails project using grails create-app , it creates several directories, one of which is a directory called 'test' for holding unit tests. This directory contains two directories, 'unit', and 'integration' for unit and

Planning a User Guide - Part 3/5 - Co-ordinate the Team

Photo by  Helloquence  on  Unsplash This is the third post in a series of five posts on how to plan a user guide. In the first post , I wrote about how to conduct an audience analysis and the second post discussed how to define the overall scope of the manual. Once the overall scope of the user guide is defined, the next step is to coordinate the team that will work on creating the manual. A typical team will consist of the following roles. Many of these roles will be fulfilled by freelancers since they are one-off or intermittent work engagements. At the end of the article, I have provided a list of websites where you can find good freelancers. Creative Artist You'll need to work with a creative artist to design the cover page and any other images for the user guide. Most small to mid-sized companies don't have a dedicated creative artist on their rolls. But that's not a problem. There are several freelancing websites where you can work with great creative ar