http://www.developer.com/

Back to article

Object Construction


July 1, 2005

This series, The Object-Oriented Thought Process, is intended for someone just learning an object-oriented language and wants to understand the basic concepts before jumping into the code or someone who wants to understand the infrastructure behind an OOP language they are already using. Click here to start at the beginning of the series.

In keeping with the code examples used in the previous articles, Java will be the language used to implement the concepts in code. One of the reasons that I like to use Java is that you can download the Java compiler for personal use at the Sun Microsystems Web site http://java.sun.com/. You can download the J2SE 1.4.2 SDK (software development kit) to compile and execute these applications and I will provide the code listings for all examples in this article. I have the SDK 1.4.0 loaded on my machine. I will also provide figures and the output (when appropriate) for these examples. See the previous articles in this series for detailed descriptions for compiling and running all the code examples in this series.

In the last column, I focused on what I call object responsibility. Object responsibility refers to the design concept that requires all objects to provide (or at least know how to find) any services that it may need to function properly. This concept is very important when it comes to moving objects across a network. One of these responsibilities involves the way that an object constructs itself. Proper construction puts the object in a safe-state and is efficient with computer memory. In this article, you will explore the art and science of constructing objects.

Constructing an Object

One of the more confusing aspects of moving from structured programming to object-oriented programming is the concept of constructing an object. Perhaps the reason for this is that there is really no comparable concept in structured programming. Object-oriented programming languages, at least the ones I have used, have a feature called a constructor. This is a physical piece of code (in fact, it is a special type of method) that is used to construct and initialize objects. Whereas it is true that these tasks can be accomplished in structured code in various ways (obviously, you can initialize variables in a structured program), the fact that constructors are built into the language makes constructing objects much safer—safe in the sense that the compiler and runtime environment can do a lot of the safety checks for you.

This topic of built-in functionality is a major theme throughout this series. This is because built-in functionality is a major design goal of object-oriented technologies. You saw this in the last column when the compiler was able to enforce the contract regarding the abstract methods. If you design your system properly, you can use the features of the object-oriented languages, compilers, and runtimes to assist you in building safe, reusable objects.

Constructors

Constructors are a new concept for people doing structured programming. Constructors do not normally exist in non-O-O languages such as C and Basic. Earlier, you learned about special methods that are used to construct objects. In Java and C++, as well as other O-O languages, constructors are methods that share the same name as the class and have no return type. For example, a constructor for the Cabbie class would look like this:

public class Cabbie {
   public Cabbie(){
      /* code to construct the object */
   }
}

The compiler will recognize that the method name is identical to the class name and consider the method a constructor. Note that a constructor does not have a return value. If you provide a return value, the compiler will not treat the method as a constructor—it will simply be treated as just another method.

When Is a Constructor Called?

When a new object is created, one of the first things to occur is that the constructor is called. Check out the following code:

class Test {
   public static void main(String args[]) {
      Cabbie myCabbie = new Cabbie();
   }
}

The new keyword indicates that a new instance of the Cabbie class is constructed, thus allocating the required memory. At this point, the constructor is called, passing the arguments in the parameter list (in this case there aren't any). The developer must do the appropriate initialization within the constructor—you will learn about this later.

Thus, the code new Cabbie() will instantiate a Cabbie object and call the Cabbie() method, which is the constructor.

What's Inside a Constructor?

Perhaps the most important function of a constructor is to initialize the memory allocated when the new keyword is encountered. In short, code included inside a constructor should set the newly created object to its initial, stable state.

For example, if you have a counter object with an attribute called count, you need to set count to zero in the constructor:

public class Cabbie {
   int count;
   public Cabbie(){
      count = 0;
   }
}

Initializing attributes is a common, and vital, function performed within a constructor.

The Default Constructor

If you write a class and do not include a constructor, the class will still compile and you can still use it.

public class Cabbie {
   int count;
   // no explicit constructor provided by programmer!!
   public void method01(){
      count = 0;
   }
}

If the class provides no explicit constructor, such as in C++ and Java, the compiler will provide a default constructor, which would look like this:

public class Cabbie {
   int count;
   public Cabbie(){    // this is not in the source code
                       // no code - it is really empty
   }
}

It is important to realize that this code is only inserted in the bytecodes generated by the compiler. The default constructor does not show up at all in the source code. In fact, you would need to decompile the bytecodes to see it.

After the memory is allocated, the default constructor calls the constructor of the superclass—if there is one (actually there is always a superclass—every written Java class ultimately will implicitly inherit from the Object class). You will observe this point in much more detail next month.

For example, if a constructor is not provided for the Cabbie class, the following default constructor is inserted:

public class Cabbie {
   int count;
   public Cabbie(){    // this is not in the source code
      super();         // there is an implicit call to super()
   }
}

Perhaps the default constructor may be sufficient in some cases; however, in most cases some sort of memory initialization should be performed. Regardless of the situation, it is good programming practice to always include at least one constructor in a class. In any case, if there are attributes in the class, they should be initialized in a constructor.

The rule of thumb is that you should always provide a constructor, even if you do not plan on doing anything inside it. You can provide a constructor with nothing in it and then add to it later. While there is technically nothing wrong with using the default constructor provided by the compiler, it is always nice to know exactly what your code looks like. You will explore this statement in much more detail next month.

Using Multiple Constructors

In many cases, an object can be constructed in more than one way. To accommodate this situation, you need to provide more than one constructor. Consider the Count class presented below.

public class Count {
   int count;
   public Count(){
      count = 0;
   }
}

On the one hand, you simply want to initialize the attribute count to count to zero. You can easily accomplish this by having a constructor initialize count to zero as follows:

public Count(){
   count = 0;
}

On the other hand, you might want to pass an initialization parameter that allows count to be set to various numbers:

public Count (int number){
   count = number;
}

This is called overloading a method (overloading pertains to all methods, not just constructors). Most O-O languages provide functionality for overloading a method.

Overloading Methods

Overloading allows a programmer to use the same method name over and over, as long as the signature of the method is different each time. You do this quite often with constructors. The signature consists of the method name and a parameter list (see Figure 1).

Thus, the following methods all have different signatures:

public void getCab();
// different parameter list
public void getCab (String cabbieName);
// different parameter list
public void getCab (int numberOfPassengers);

Figure 1: The components of a signature.

By using different signatures, you can construct objects in various ways depending on the constructor used.

In some languages, the signature may also include the return value.

For example, in Java, the following code would generate a compiler error even though the return values are different.

public class Count {
   public int method01(int x){
      ...
   }
   public void method01(int x){
      ...
   }
}

Using UML to Model Classes

Look at a database reader example to illustrate some of the points pertaining to a constructor. Consider that we have two ways we can construct a database reader:

  1. Pass the name of the database and position the cursor at the beginning of the database.
  2. Pass the name of the database and the position within the database where we want the cursor to position itself.

Figure 2 shows a class diagram for the DataBaseReader class. Note that the diagram lists two constructors for the class. Although the diagram shows the two constructors, without the parameter list there is no way to know which constructor is which. To distinguish the constructors, you can look at the corresponding code listed below.

Figure 2: The DataBaseReader class diagram.

Notice that in this class diagram the constructors do not have a return type. All other methods besides constructors must have return types.

Here is a code segment of the class that shows its constructors and the attributes that the constructors initialize (see Figure 3):

public class DataBaseReader {
   String DBName;
   int startPosition;
   // initialize just the name
   public DataBaseReader (String name){
   DBName = name;
};
   // initialize the name and the position
   public DataBaseReader (String name, int pos){
      DBName = name;
      startPosition = pos;
   };
   .. // rest of class
}

Figure 3: Creating a new object.

How the Superclass Is Constructed

When using inheritance, you must know how the parent class is constructed. Remember that when you use inheritance, you are inheriting everything about the parent. Thus, you must become intimately aware of all the parent's data and behavior. The inheritance of an attribute is fairly obvious.

However, how a constructor is inherited is not as obvious. After the new keyword is encountered and the object is allocated, the following steps occur (see Figure 4):

  1. The first thing that happens inside the constructor is that the constructor of the class's superclass is called.
  2. Then, each class attribute of the object is initialized. These are the attributes that are part of the class definition (instance variables), not the attributes inside the constructor or any other method (local variables). In the DataBaseReader code presented earlier, the integer startPosition is an instance variable of the class.
  3. Then, the rest of the code in the constructor executes.

Figure 4 Constructing an object.

The Design of Constructors

When designing a class, it is good practice to initialize all the attributes. In some languages, the compiler might attempt to do this initialization. As always, don't count on the compiler to initialize attributes! In Java, you cannot use an attribute until it is initialized. If the attribute is first set in the code, make sure that you initialize the attribute to some valid condition—for example, set an integer to zero.

Constructors are used to ensure that the application is in a stable state. For example, initializing an attribute to zero, when it is intended for use as a denominator in a division operation, may lead to an unstable application. You must take into consideration the fact that a division by zero is an illegal operation.

During the design, it is good practice to identify a stable state for all attributes and then initialize them to this stable state in the constructor.

Conclusion

This article started a discussion about constructing objects. You covered some of the technical aspects of actually using constructors. Constructing objects properly is a very important object-oriented responsibility. These responsibilities include appropriate memory management as well as putting an object in a safe-state by performing actions like initialization. Next month, you will explore object construction in more detail and see how certain design issues, such as inheritance, impact object constructors.

References

    Gilbert, Stephen, and Bill McCarty: Object-Oriented Design in Java. The Waite Group, 1998.

  • Meyers, Scott: Effective C++. Addison-Wesley, 1992.
  • Tyma, Paul, Gabriel Torok and Troy Downing: Java Primer Plus. The Waite Group, 1996.
  • Ambler, Scott: The Object Primer. Cambridge University Press, 1998.
  • Jaworski, Jamie: Java 1.1 Developers Guide. Sams Publishing, 1997.

About the Author

Matt Weisfeld is a faculty member at Cuyahoga Community College (Tri-C) in Cleveland, Ohio. Matt is a member of the Information Technology department, teaching programming languages such as C++, Java, and C# .NET as well as various web technologies. Prior to joining Tri-C, Matt spent 20 years in the information technology industry gaining experience in software development, project management, business development, corporate training, and part-time teaching. Matt holds an MS in computer science and an MBA in project management. Besides the first edition of The Object-Oriented Thought Process, Matt has published two other computer books, and more than a dozen articles in magazines and journals such as Dr. Dobb's Journal, The C/C++ Users Journal, Software Development Magazine, Java Report, and the international journal Project Management. Matt has presented at conferences throughout the United States and Canada.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date