Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Introduction to Java programming, Part 2: Constructs for real-world applications

More-advanced Java language features

J Steven Perry, Principal Consultant, Makoto Consulting Group, Inc.
Photo of J Steven Perry
J. Steven Perry is a software developer, architect, and general Java nut who has been developing software professionally since 1991. His professional interests range from the inner workings of the JVM to UML modeling and everything in between. Steve has a passion for writing and mentoring; he is the author of Java Management Extensions (O'Reilly), Log4j (O'Reilly), and the IBM developerWorks articles "Joda-Time" and OpenID for Java Web applications." In his spare time, he hangs out with his three kids, rides his bike, and teaches yoga.

Summary:  In Part 1 of this tutorial, professional Java™ programmer J. Steven Perry introduced the Java language syntax and libraries you need to write simple Java applications. Part 2, still geared toward developers new to Java application development, introduces the more-sophisticated programming constructs required for building complex, real-world Java applications. Topics covered include exception handling, inheritance and abstraction, regular expressions, generics, Java I/O, and Java serialization.

View more content in this series

Date:  19 Aug 2010
Level:  Introductory PDF:  A4 and Letter (904 KB | 53 pages)Get Adobe® Reader®

Comments:  

Inheritance

You've encountered examples of inheritance a few times already in this tutorial. This section reviews some of Part 1's material on inheritance and explains in more detail how inheritance works — including the inheritance hierarchy, constructors and inheritance, and inheritance abstraction.

How inheritance works

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 the hierarchy. A subclass inherits from its superclasses. The java.lang.Object class is at the top of the class hierarchy, meaning every Java class is a subclass of, and inherits from, Object.

For example, suppose you have a Person class that looks like the one in Listing 12:


Listing 12. Public Person class

package com.makotogroup.intro;

// . . .
public class Person {
 public static final String GENDER_MALE = "MALE";
 public static final String GENDER_FEMALE = "FEMALE";
 public Person() {
 //Nothing to do...
 }
 private String name;
 private int age;
 private int height;
 private int weight;
 private String eyeColor;
 private String gender;
// . . .

}

The Person class in Listing 12 implicitly inherits from Object. Because that's assumed for every class, you don't need to type extends Object for every class you define. But what does it mean to say that a class inherits from its superclass? It simply means that Person has access to the exposed variables and methods in its superclasses. In this case, Person can see and use Object's public methods and variables and Object's protected methods and variables.


Defining a class hierarchy

Now suppose you have an Employee class that inherits from Person. Its class definition (or inheritance graph) would look something like this:

public class Employee extends Person {

 private String taxpayerIdentificationNumber;
 private String employeeNumber;
 private BigDecimal salary;
// . . .
}

Multiple vs. single inheritance

Languages like C++ support the concept of multiple inheritance: at any point in the hierarchy a class can inherit from one or more classes. The Java language supports only single inheritance, which means you can only use the extends keyword with a single class. So the class hierarchy for any given Java class always consists of a straight line all the way up to java.lang.Object.

However, the Java language supports implementing multiple interfaces in a single class, which gives you a workaround of sorts to single inheritance. I'll introduce you to multiple interfaces later in the tutorial.

The Employee inheritance graph implies that Employee has access to all public and protected variables and methods in Person (because it directly extends it), as well as Object (because it actually extends that class, too, though indirectly). However, because Employee and Person are in the same package, Employee also has access to the package-private (sometimes called friendly) variables and methods in Person.

To go one step deeper into the class hierarchy, you could create a third class that extends Employee:

public class Manager extends Employee {
// . . .
}

In the Java language, any class can have at most one superclass, but a class can have any number of subclasses. That is the most important thing to remember about inheritance hierarchy in the Java language.


Constructors and inheritance

Constructors aren't full-fledged object-oriented members, so they aren't inherited; instead, you must explicitly implement them in subclasses. Before I go into that, I'll review some basic rules about how constructors are defined and invoked.

Constructor basics

Remember that a constructor always has the same name as the class it is used to construct, and it has no return type. For example:

public class Person {
 public Person() {
 }
}

Every class has at least one constructor, and if you don't explicitly define a constructor for your class, the compiler will generate one for you, called the default constructor. The preceding class definition and this one are identical in how they function:

public class Person {
}

Invoking a superclass constructor

To invoke a superclass constructor other than the default constructor, you must do so explicitly. For example, suppose Person has a constructor that takes the name of the Person object being created. From Employee's default constructor, you could invoke the Person constructor shown in Listing 13:


Listing 13. Initializing a new Employee

public class Person {
 private String name;
 public Person() {
 }
 public Person(String name) {
   this.name = name;
 }
}

// Meanwhile, in Employee.java
public class Employee extends Person {
 public Employee() {
   super("Elmer J Fudd");
 }
}

You would probably never want to initialize a new Employee object this way, however. Until you get more comfortable with object-oriented concepts, and Java syntax in general, it's a good idea to implement superclass constructors in subclasses if you think you will need them, and invoke them homogeneously. Listing 14 defines a constructor in Employee that looks like the one in Person so that they match up. It's much less confusing from a maintenance standpoint.


Listing 14. Invoking a superclass homogeneously

public class Person {
 private String name;
 public Person(String name) {
   this.name = name;
 }
}
// Meanwhile, in Employee.java
public class Employee extends Person {
 public Employee(String name) {
   super(name);
 }
}

Declaring a constructor

The first thing a constructor does is invoke the default constructor of its immediate superclass, unless you — on the first line of code in the constructor — invoke a different constructor. For example, these two declarations are functionally identical, so pick one:

public class Person {
 public Person() {
 }
}
// Meanwhile, in Employee.java
public class Employee extends Person {
 public Employee() {
 }
}

Or:

public class Person {
 public Person() {
 }
}
// Meanwhile, in Employee.java
public class Employee extends Person {
 public Employee() {
   super();
 }
}

No-arg constructors

If you provide an alternate constructor, you must explicitly provide the default constructor, or it is not available. For example, the following code would give you a compile error:

public class Person {
 private String name;
 public Person(String name) {
   this.name = name;
 }
}
// Meanwhile, in Employee.java
public class Employee extends Person {
 public Employee() {
 }
}

This example has no default constructor, because it provides an alternate constructor without explicitly including the default constructor. This is why the default constructor is sometimes called the no-argument (or no-arg) constructor; because there are conditions under which it is not included, it's not really a default.

How constructors invoke constructors

A constructor from within a class can be invoked by another constructor using the this keyword, along with an argument list. Just like super(), the this() call must be the first line in the constructor. For example:

public class Person {
 private String name;
 public Person() {
   this("Some reasonable default?");
 }
 public Person(String name) {
   this.name = name;
 }
}
// Meanwhile, in Employee.java

You will see this idiom frequently, where one constructor delegates to another, passing in some default value if that constructor is invoked. It's also a great way to add a new constructor to a class while minimizing impact on code that already uses an older constructor.

Constructor access levels

Constructors can have any access level you want, and certain rules of visibility apply. Table 1 summarizes the rules of constructor access:


Table 1. Constructor access rules
Constructor access modifier Description
publicConstructor can be invoked by any class.
protectedConstructor can be invoked by an class in the same package or any subclass.
No modifier (package-private)Constructor can be invoked by any class in the same package.
privateConstructor can be invoked only by the class in which the constructor is defined.

You may be able to think of use cases where constructors would be declared protected or even package-private, but how is a private constructor useful? I've used private constructors when I didn't want to allow direct creation of an object through the new keyword when implementing, say, the Factory pattern (see Resources). In that case, a static method would be used to create instances of the class, and that method, being included in the class itself, would be allowed to invoke the private constructor:


Inheritance and abstraction

If a subclass overrides a method from a superclass, that method is essentially hidden because calling that method through a reference to the subclass invokes the subclass's version of the method, not the superclass's version. This isn't to say the superclass method is no longer accessible. The subclass can invoke the superclass method by prefacing the name of the method with the super keyword (and unlike with the constructor rules, this can be done from any line in the subclass method, or even in a different method altogether). By default, a Java program will call the subclass method if it is invoked through a reference to the subclass.

The same applies to variables, provided the caller has access to the variable (that is, the variable is visible to the code trying to access it). This can cause you no end of grief as you gain proficiency in Java programming. Eclipse will provide ample warnings that you are hiding a variable from a superclass, however, or that a method call won't call what you think it will.

In an OOP context, abstraction refers to generalizing data and behavior to a type higher up the inheritance hierarchy than the current class. When you move variables or methods from a subclass to a superclass, you say you are abstracting those members. The main reason for doing this is to reuse common code by pushing it as far up the hierarchy as possible. Having common code in one place makes it easier to maintain.

Abstract classes and methods

There are times when you will want to create classes that only serve as abstractions and do not necessarily ever need to be instantiated. Such classes are called abstract classes. By the same token, you will find that there are times when certain methods need to be implemented differently for each subclass that implements the superclass. Such methods are abstract methods. Here are some basic rules for abstract classes and methods:

  • Any class can be declared abstract.
  • Abstract classes cannot be instantiated.
  • An abstract method cannot contain a method body.
  • Any class with an abstract method must be declared abstract.

Using abstraction

Suppose you don't want to allow the Employee class to be instantiated directly. You simply declare it using the abstract keyword, and you're done:

public abstract class Employee extends Person {
// etc.
}

If you try to run this code, you'll get a compile error:

public void someMethodSomwhere() {
 Employee p = new Employee();// compile error!!
}

The compiler is complaining that Employee is abstract and cannot be instantiated.

The power of abstraction

Suppose that you need a method to examine the state of an Employee object and make sure it is valid. This need would seem to be common to all Employee objects, but would behave sufficiently differently among all potential subclasses that there is zero potential for reuse. In that case, you declare the validate() method abstract (forcing all subclasses to implement it):

public abstract class Employee extends Person {
 public abstract boolean validate();
}

Every direct subclass of Employee (such as Manager) is now required to implement the validate() method. However, once a subclass has implemented the validate() method, none of its subclasses need to implement it.

For example, suppose you have an Executive object that extends Manager. This definition would be perfectly valid:

public class Executive extends Manager {
 public Executive() {
 }
}

When (not) to abstract: Two rules

As a first rule of thumb, don't abstract in your initial design. Using abstract classes early in the design forces you down a certain path, and that could restrict your application. Remember, common behavior (which is the entire point of having abstract classes) can always be refactored further up the inheritance graph. It is almost always better to do this once you've discovered that you do need it. Eclipse has wonderful support for refactoring.

Second, as powerful as they are, resist the use of abstract classes when you can. Unless your superclasses contain lots of common behavior, and on their own are not really meaningful, let them remain nonabstract. Deep inheritance graphs can make code maintenance difficult. Consider the trade-off between classes that are too large and maintainable code.


Assignments: Classes

When assigning a reference from one class to a variable of a type belonging to another class, you can do so, but there are rules. Let's look at this example:

Manager m = new Manager();
Employee e = new Employee();
Person p = m; // okay
p = e; // still okay
Employee e2 = e; // yep, okay
e = m; // still okay
e2 = p; // wrong!

The destination variable must be of a supertype of the class belonging to the source reference, or the compiler will give you an error. Basically, whatever is on the right side of the assignment must be a subclass or the same class as the thing on the left. If not, it's possible for assignments of objects with different inheritance graphs (such as Manager and Employee) to be assigned to a variable of the wrong type. Consider this example:

Manager m = new Manager();
Person p = m; // so far so good
Employee e = m; // okay
Employee e = p; // wrong!

While an Employee is a Person, it is most definitely not a Manager, and the compiler enforces this.

5 of 14 | Previous | Next

Comments



static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=508383
TutorialTitle=Introduction to Java programming, Part 2: Constructs for real-world applications
publish-date=08192010
author1-email=steve@makotoconsulting.com
author1-email-cc=jaloi@us.ibm.com