Lesson 9.3 Other Constructors
Do you remember overloaded methods from Lesson 4.4? Overloading means that you can have methods with the same name, if the number, type, or order of the arguments varies. This combination of name and argument type and order is called the method's signature.
While overloading is a convenience for methods--after all, you could simply use different method names if overloading were not available--it is a necessity for constructors, because constructors must have the same name as their classes.
In this lesson we'll look at some overloaded constructors for the LTextField class, and then have a look at a few Java language features that facilitate writing and using constructors: the this object and the this() method.
Overloaded Constructors
When we first looked at the LTextField class, we talked about how we wanted to use LTextField objects. Answering that question gives us some insight into how the different LTextField() constructors should work.
For instance, we'd like constructors that:
- Allow you to supply a name for the Label portion of the object, like this:
| LTextField first = new LTextField("Name:"); |
- Allow you to supply a width for the TextField portion of the object, like this:
| LTextField addr = new LTextField(40); |
- Allow you to do both, like this:
| LTextField city = new LTextField("City", 15); |
Normally, when designing constructors, there will be one constructor that allows you to initialize most of the fields. This constructor is usually called the working constructor.
|
Which Fields Should Be Intialized?
|
| When talking about the working constructor, you'll often hear people say that the working constructor should give the user the ability to initialize all of the object's fields. That is often true, but not always.
The working constructor is the constructor that allows you to specify values for all of the user-definable fields.
It is very common for a class to have fields which are automatically initialized through a built-in calculation, often involving other fields. When this is the case, it is important that those fields not be initialized through arguments to the working constructor. |
The Working Constructor
The working constructor for the LTextField class is the one that allows us to specify both the text we want to use for the Label portion, as well as the width of the TextField portion.
Let's take LTextField2.java and copy it to LTextField3.java. Once you've made your copy, you have to make the usual changes. You can universally change every instance of LTextField2 to LTextField3 by using the Search | Replace menu selection in your editor.
Here's what your file should look like as you start:
|
LTextField3.java - Starting
|
import java.awt.*;
import java.applet.*;
public class LTextField3 extends Panel
{
Label theLabel;
TextField theText;
public LTextField3()
{
theLabel = new Label("",
Label.RIGHT);
theText = new TextField(15);
add(theLabel);
add(theText);
}
} |
The Skeleton
Once you've made the changes to LTextField3, it's time to add the new constructor. Here are the specifications:
- The name will be the same as the existing, default constructor, but the arguments will change. Start by adding the basic skeleton like this:
- Now, add a String argument for the Label text. Your revised class header should now look like this:
public LTextField3(String txt)
{
} |
- Now, add an int argument for the TextField size like this:
public LTextField3(String txt, int size)
{
} |
The Body
Inside the body of your new constructor you have to initialize both of the fields inside your LTextField object.
Use the String argument to initialize your Label, and the size argument to initialize your TextField.
The finished constructor should look like this:
|
LTextField3 Working Constructor
|
public LTextField3(String txt, int size)
{
theLabel = new Label(txt,Label.RIGHT);
theText = new TextField(size);
add(theLabel);
add(theText);
} |
Once you're finished, let's take User2.java and make User3.java. Make the usual changes and then add a few LTextField3 objects to User3.java, like this:
|
User3.java and the LTextField3 Working Constructor
|
import java.awt.*;
import java.applet.*;
public class User3 extends Applet
{
LTextField3 name =
new LTextField3("Name:", 30);
LTextField3 addr = new LTextField3();
LTextField3 city =
new LTextField3("City:", 15);
public void init()
{
add(name);
add(addr);
add(city);
}
} |
Once you compile everything up and run it, you'll see that we can use both the default constructor, as we've done with addr, or we can use the working constructor, as we've done with city and name. The working constructor allows us to change the text our LTextField displays as well as the width of the TextField portion.
Each of these works pretty well, but there are still a few constructors to write. We want to be able to change the width without passing a String, and we want to be able to pass a String without specifying a width. You'll do that in the lesson wrap-up.
Using this and this()
When you send a message to an object, you are, in effect, calling one of that object's methods. When an object is running a method, you can refer to the object that is running the method by using the keyword this.
It turns out that this is less useful than it might seem. Most of the things that you can do by using the keyword this, are easily done in another manner. For instance, inside a method, you can tell the current object to execute one of its methods by sending the message to this, like so:
public void init()
{
// Tell this applet to add button
this.add(myButton);
} |
If you don't use this, however, the message is sent to the current object anyway, so most folks just don't bother.
Argument or Field?
One place where this is useful is in differentiating fields from arguments of the same name. If you notice in the LTextField3 constructor we used the name txt for the String argument that was used to initialize the field theLabel, and the name size for the int argument that was used to initialize theText.
In this case, there's not much chance of confusing the two, since one is an int and the other a String. Imagine, however, a working constructor where you had five or six String arguments used to initialize various fields. It would be very easy to accidentally initialize a field with the wrong argument.
One commonly accepted practice is to make the names of the formal arguments the same as the name of the field that the argument initializes. Of course, if the arguments and the fields have the same name, how are you going to tell them apart?
You guessed it. You can use this, like this:
public MyClass(int number, double value)
{
this.number = number;
this.value = value;
} |
Since this refers to the "current" object, this.number is a field, but number is the formal argument passed to the MyClass constructor. To avoid a surfeit of confusion, by convention this construction is only used in constructors.
...and this( )
In addition to the this object, there is also a "method" called this(). The method this() allows you to call one constructor from another constructor. The invocation of this() must be the first statement in the constructor, and you cannot call this() from any other methods.
The advantage of this() is that it allows you to "factor" out common code in a constructor. You might have noticed that both of the LTextField3 constructors have code to construct each of the fields and to add the fields to the component's surface. This duplication is wasteful, and will lead to errors eventually when you update one constructor, but forget to update another.
Using this() you can replace the default constructor in LTextField3 with this much simpler version, which calls the working constructor:
public LTextField3()
{
this("", 15);
} |
When you invoke the this() method, passing a String and an int, Java looks for a constructor in the current class that has that signature. Finding our working constructor, it calls it before returning.
The only trick to using this() is that you have to remember to call this() before you do any other initialization in your constructor.
Constructor Review
Here are the things you should remember about constructors:
- Constructors are used to create objects.
- Constructors act like an object factory, with the class definition working as a blueprint.
- Constructors are called by using the new operator.
- Constructors have the same name as their class.
- Constructors never have a return value.
- Constructors may have arguments, allowing you to customize object construction.
- Constructors may have no arguments, but provide a default value for every field instead.
Something to Talk About
Here are some exercises to get you working with constructors. Download the final version of LTextField3.java, and add these constructors.
LTextField3 zip = new LTextField3("Zip:");
LTextField3 addr = new LTextField3(35); |
Change the LTextField3 constructor [or constructors] so that each LTextField3 object:
- Has a default size of 35 for the TextField portion.
- Uses a 12 point Monospaced Font.
- Has a TextField background of yellow.
- Has a Panel background of light gray.
Please continue to the next section of this lesson.
|