You are on page 1of 6

Intermediate Java programming https://www6.software.ibm.com/developerworks/education/j-intermed/se...

Intermediate Java programming


Inheritance and abstraction
What's inheritance?
Classes in Java code exist in hierarchies. Classes above a given class in a hierarchy are superclasses of that class.
That particular class is a subclass of every class higher up. A subclass inherits from its superclasses. The class
Object is at the top of every class's hierarchy. In other words, every class is a subclass of (and inherits from)
Object.

For example, suppose we have an Adult class that looks like this:
public class Adult {
protected int age = 0;
protected String firstname = "firstname";
protected String lastname = "lastname";
protected String gender = "MALE";
protected int progress = 0;

public Adult() { }
public void move() {
System.out.println("Moved.");
}
public void talk() {
System.out.println("Spoke.");
}
}

Our Adult class implicitly inherits from Object. That's assumed for any class, so you don't have to type extends
Object in the class definition. But what does it mean to say that our class inherits from its superclass(es)? It simply
means that Adult has access to the exposed variables and methods in its superclasses. In this case, it means that
Adult can see and use the following from any of its superclasses (we have only one at the moment):

public methods and variables


protected methods and variables
Package protected methods and variables (that is, those without an access specifier), if the superclass is in the
same package as Adult
Constructors are special. They aren't full-fledged OO members, so they aren't inherited.

If a subclass overrides a method or variable from the superclass -- if the subclass implements a member with the same
name, in other words -- that hides the superclass's member. To be accurate, overriding a variable hides it, and
overriding a method simply overrides it, but the effect is the same: The overridden member is essentially hidden. You
can still get to the members of the superclass by using the super keyword:
super.hiddenMemberName

In the case of Adult, all it inherits at the moment are the methods on Object (toString(), for example). So, the
following code snippet is perfectly acceptable:
Adult anAdult = new Adult();
anAdult.toString();

The toString() method doesn't exist explicitly on Adult, but Adult inherits it.

There are "gotchas" here that you should keep in mind. First, it's very easy to give variables and methods in a subclass

1 of 6 9/18/2009 11:15 AM
Intermediate Java programming https://www6.software.ibm.com/developerworks/education/j-intermed/se...

the same name as variables and methods in that class's superclass, then get confused when you can't call an inherited
method. Remember, when you give a method the same name in a subclass as one that already exists in a superclass,
you've hidden it. Second, constructors aren't inherited, but they are called. There is an implicit call to the superclass
constructor in any subclass constructor you write, and it's the first thing that the subclass constructor does. You must
live with this; there's nothing you can do to change it. For example, our Adult constructor actually looks like this at
runtime, even though we didn't type anything in the body:
public Adult() {
super();
}

That line in the constructor body calls the no-argument constructor on the superclass. In this case, that's the constructor
of Object.

Defining a class hierarchy


Suppose we have another class called Baby. It looks like this:
public class Baby {
protected int age = 0;
protected String firstname = "firstname";
protected String lastname = "lastname";
protected String gender = "MALE";
protected int progress = 0;

public Baby() {
}
public void move() {
System.out.println("Moved.");
}
public void talk() {
System.out.println("Spoke.");
}
}

Our Adult and Baby classes look very similar. In fact, they're almost identical. That kind of code duplication makes
maintaining code more painful than it needs to be. We can create a superclass, move all the common elements up to
that class, and remove the code duplication. Our superclass could be called Person, and it might look like this:
public class Person {
protected int age = 0;
protected String firstname = "firstname";
protected String lastname = "lastname";
protected String gender = "MALE";
protected int progress = 0;
public Person() {
}
public void move() {
System.out.println("Moved.");
}
public void talk() {
System.out.println("Spoke.");
}
}

Now we can have Adult and Baby subclass Person, which makes those two classes pretty simple at the moment:
public class Adult {
public Adult() {

2 of 6 9/18/2009 11:15 AM
Intermediate Java programming https://www6.software.ibm.com/developerworks/education/j-intermed/se...

}
}
public class Baby {
public Baby() {
}
}

One we have this hierarchy, we can refer to an instance of each subclass as an instance of any of its superclasses in the
hierarchy. For example:
Adult anAdult = new Adult();
System.out.println("anAdult is an Object: " + (Adult instanceof Object));
System.out.println("anAdult is a Person: " + (Adult instanceof Person));
System.out.println("anAdult is anAdult: " + (Adult instanceof Adult));
System.out.println("anAdult is a Baby: " + (Adult instanceof Baby));

This code will give us three true results and one false result. You can also cast an object to any type higher up in
its hierarchy, like so:
Adult anAdult = new Adult();
Person aPerson = (Person) anAdult;
aPerson.move();

This code will compile without problems. We can cast an Adult to type Person, then call a Person method on it.

Because we have this hierarchy, the code on our subclasses is simpler. But do you see a problem here? Now all
Adults and all Babys (excuse the bad plural) will talk and move in the same way. There's only one implementation of
each behavior. That's not what we want, because grownups don't speak or move like babies. We could override
move() and talk() on the subclasses, but then we've got essentially useless "standard" behavior defined on our
superclass. What we really want is a way to force each of our subclasses to move and talk in its own particular way.
That's what abstract classes are for.

Abstraction
In an OO context, abstraction refers to the act of generalizing data and behavior to a type higher up the hierarchy than
the current class. When you move variables or methods from a subclass to a superclass, you're abstracting those
members.

Those are general terms, and they apply in the Java language. But the language also adds the concepts of abstract
classes and abstract methods. An abstract class is a class that can't be instantiated. For example, you might create a
class called Animal. It makes no sense to instantiate such a class: In practice, you'd only want to create instances of a
concrete class like Dog. But all Animals have some things in common, such as the ability to make noise. Saying that
an Animal makes noise doesn't tell you much. The noise it makes depends on the kind of animal it is. How do you
model that? You define the common stuff on the abstract class, and you force subclasses to implement concrete
behavior specific to their types.

You can have both abstract and concrete classes in your hierarchies.

Using abstraction
Our Person class contains some method behavior that we don't know we need yet. Let's remove it and force

3 of 6 9/18/2009 11:15 AM
Intermediate Java programming https://www6.software.ibm.com/developerworks/education/j-intermed/se...

subclasses to implement that behavior polymorphically. We can do that by defining the methods on Person to be
abstract. Then our subclasses will have to implement those methods.
public abstract class Person {
...
abstract void move();
abstract void talk();
}

public class Adult extends Person {


public Adult() {
}
public void move() {
System.out.println("Walked.");
}
public void talk() {
System.out.println("Spoke.");
}
}

public class Baby extends Person {


public Baby() {
}
public void move() {
System.out.println("Crawled.");
}
public void talk() {
System.out.println("Gurgled.");
}
}

What have we done in this listing?

We changed Person to make the methods abstract, forcing subclasses to implement them.
We made Adult subclass Person, and implemented the methods.
We made Baby subclass Person, and implemented the methods.
When you declare a method to be abstract, you require subclasses to implement the method, or to be abstract
themselves and pass along the implementation responsibility to their subclasses. You can implement some methods on
an abstract class, and force subclasses to implement others. That's up to you. Simply declare the ones you don't want
to implement as abstract, and don't provide a method body. If a subclass fails to implement an abstract method
from a superclass, the compiler will complain.

Now that both Adult and Baby subclass Person, we can refer to an instance of either class as being of type
Person.

Refactoring to abstract behavior


We now have Person, Adult, and Baby in our hierarchy. Suppose we wanted to make the two subclasses more
realistic by changing their move() methods, like this:
public class Adult extends Person {
...
public void move() {
this.progress++;
}
...
}

public class Baby extends Person {


...

4 of 6 9/18/2009 11:15 AM
Intermediate Java programming https://www6.software.ibm.com/developerworks/education/j-intermed/se...

public void move() {


this.progress++;
}
...
}

Now each class updates its instance variable to reflect some progress being made whenever we call move(). But
notice that the behavior is the same again. It makes sense to refactor the code to remove this code duplication. The
most likely refactoring is to move move() to Person.

Yes, we're adding the method implementation back to the superclass we just took it out of. This is a simplistic
example, so this back-and-forth might seem wasteful. But what we just experienced is a common occurrence when you
write Java code. You often see classes and methods change as your system grows, and sometimes you end up with
code duplication that you can refactor to superclasses. You might even do that, decide it was a mistake, and put the
behavior back down in the subclasses. You simply can't know the right place for all behavior at the beginning of the
development process. You learn the right place for behavior as you go.

Let's refactor our classes to put move() back on the superclass:


public abstract class Person {
...
public void move() {
this.progress++;
}
public abstract void talk();
}

public class Adult extends Person {


public Adult() {
}
public void talk() {
System.out.println("Spoke.");
}
}

public class Baby extends Person {


public Baby() {
}
public void talk() {
System.out.println("Gurgled.");
}
}

Now our subclasses implement their different versions of talk(), but share the same behavior for move().

When to abstract ... and when not to


Deciding when to abstract (or create a hierarchy) is a hotly debated topic in OO circles, especially among Java
language programmers. Certainly there are few right and wrong answers about how to structure class hierarchies. This
is an area where conscientious and skilled practitioners can (and often do) disagree. That said, there are some good
rules of thumb to follow with regard to hierarchies.

First, don't abstract first. Wait until the code tells you that you should. It is almost always better to refactor your way
to an abstraction than to assume that you need it at the outset. Don't assume that you need a hierarchy. Many Java
programmers overuse hierarchies.

Second, resist the use of abstract classes when you can. They're not bad; they're just restrictive. We used an abstract

5 of 6 9/18/2009 11:15 AM
Intermediate Java programming https://www6.software.ibm.com/developerworks/education/j-intermed/se...

class to force our subclasses to implement certain behavior. Would an interface (which we'll discuss in Interfaces ) be a
better idea? Quite possibly. Your code will be easier to understand if it isn't made up of complex hierarchies with a
mix of overridden and implemented methods. You might have a method defined three or four classes up the chain. By
the time you use it in a sub-sub-sub-subclass, you might have to hunt to discover what that method does. That can
make debugging frustrating.

Third, do use a hierarchy and/or abstract classes when it's smart to do so. There are many coding patterns that make
use of the Java language abstract method and abstract class concepts, such as the Gang of Four Template Method
pattern (see Resources).

Fourth, understand the price you pay when you use a hierarchy prematurely. It really can lead you down the wrong path
quickly, because having the classes there, named as they are, with the methods they have, makes it very easy to assume
all of that should be as it is. Maybe that hierarchy made sense when you created it, but it might not make sense
anymore. Inertia can make it resistant to change.

In a nutshell, be smart about using hierarchies. Experience will help you be smarter, but it won't make you all-wise.
Remember to refactor.

6 of 6 9/18/2009 11:15 AM

You might also like