Thursday, January 13, 2011

Learning Objects for Constructors in Java

Summary: These learning objects explain constructors, which are used to create and initialize objects in Java.





Learning Objects for Constructors

Concept The process of creating an object involves allocating memory for the object and assigning the reference to this block of memory to a variable.Constructors enable arbitrary initialization of the object during its creation.
These source code of these learning objects can be found in constructor.zip.
TABLE 1
LOTopicJava Files (.java)Prerequisites
"What are constructors for?"What are constructors for?Constructor01A, B, C 
"Computation within constructors"Computation within constructorsConstructor021
"Overloading constructors"Overloading constructorsConstructor032
"Invoking an overloaded constructor from within a constructor"Invoking another constructorConstructor043
"Explicit default constructors"Explicit default constructorsConstructor053
"Constructors for subclasses"Constructors for subclassesConstructor06A, B, C3
"Constructors with object parameters"Constructors with object parametersConstructor073
"Constructors with subclass object parameters"Constructors with subclass  
 object parametersConstructor086, 7
Program The example used in these LOs is class Song with three fields: the name of the song, the length of the song in seconds and the pricePerSecond. The class is to be used to implement a website which charges for downloading the song; the price is the product of the length of the song in second and the price per second. To focus the discussion on constructors, the fields are not declared private.

What are constructors for?

Concept An object is created by allocating memory for its fields. The fields are given the default values for their types. A reference to the object is returned and assigned to a variable; the reference can be used to access the fields and methods of the object.
Program: Constructor01B.java
// Learning Object Constructor01A
//    what are constructors for?
class Song {
    String name;
    int seconds;
    double pricePerSecond;
 
    public double computePrice() {
        return seconds * pricePerSecond;
    }
}
 
public class Constructor01A {
    public static void main(/*String[] args*/) {
        Song song1 = new Song();
        song1.name = "Waterloo";
        song1.seconds = 164;
        song1.pricePerSecond = 0.01;
        double price = song1.computePrice();
    }
}
If a constructor is not explicitly declared a default constructor is called.
  • The variable song1 is allocated and contains the null value.
  • Memory is allocated for the fields of the object; this is displayed in the Instance and Array Area. Default values are assigned to the three fields.
  • The default constructor is called but does nothing except return a reference to the object.
  • The reference is stored in the variable song1.
  • The reference in song1 is used to assign values to the fields of the object.
  • The reference in song1 is used to call the method computePrice on the object; the method computes and returns the price, which is assigned to the variable price.
Concept An explicit constructor method can be declared and used to initialize each object. The constructor method is identified by a special syntax: the name of the method is the same as the name of the class and there is no return type (because the value returned is of the type of the class itself).
Program: Constructor01B.java
// Learning Object Constructor01B
//    what are constructors for?
class Song {
    String name;
    int seconds;
    double pricePerSecond;
 
    Song() {
        name = "Waterloo";
        seconds = 164;
        pricePerSecond = 0.01;
    }
 
    public double computePrice() {
        return seconds * pricePerSecond;
    }
}
 
public class Constructor01B {
    public static void main(/*String[] args*/) {
        Song song1 = new Song();
        double price = song1.computePrice();
    }
}
This program is the same as the previous one except that the assignment of nondefault values to the fields of the object is moved to an explicit constructor.
  • The variable song1 is allocated and contains the null value.
  • Memory is allocated for the fields of the object; this is displayed in the Instance and Array Area. Default values are assigned to the three fields.
  • The constructor is called and assigns values to the three fields; then it returns a reference to the object.
  • The reference is stored in the variable song1.
  • The reference in song1 is used to call the method computePrice on the object; the method computes and returns the price, which is assigned to the variable price.
Exercise Add the creation of a second object song2 to the program and verify that it is initialized to the same values.
Concept Of course, it is highly unlikely that all objects created from a class will be initialized with the same values. A constructor can have formal parameters like any other method and is called with actual parameters.
Program: Constructor01C.java
// Learning Object Constructor01C
//    what are constructors for?
class Song {
    String name;
    int seconds;
    double pricePerSecond;
 
    Song(String n, int s, double p) {
        name = n;
        seconds = s;
        pricePerSecond = p;
    }
 
    public double computePrice() {
        return seconds * pricePerSecond;
    }
}
 
public class Constructor01C {
    public static void main(/*String[] args*/) {
        Song song1 = new Song("Waterloo", 164, 0.01);
        double price = song1.computePrice();
    }
}
This program is the same as the previous one except that the constructor has formal parameters and the actual parameters passed to the constructor are assigned to the fields of the object.
  • The variable song1 is allocated and contains the null value.
  • Memory is allocated for the fields of the object; this is displayed in the Instance and Array Area. Default values are assigned to the three fields.
  • The constructor is called with three actual parameters; these values are assigned to the formal parameters of the constructor method.
  • The values of the formal parameters are assigned to the three fields; then the constructor returns a reference to the object.
  • The reference is stored in the variable song1.
  • The reference in song1 is used to call the method computePrice on the object; the method computes and returns the price, which is assigned to the variable price.
Exercise Modify the class so that the second parameter passes the number of minutes; the value of the field seconds will have to be computed in the constructor.

Computation within constructors

Concept Constructors are often used simply for assigning initial values to fields of an object; however, an arbitrary initializing computation can be carried out within the constructor.
Program: Constructor02.java
// Learning Object Constructor02
//    computation within constructors
class Song {
    String name;
    int seconds;
    double pricePerSecond;
    double price;
 
    Song(String n, int s, double p) {
        name = n;
        seconds = s;
        pricePerSecond = p;
        price = computePrice();
    }
 
    private double computePrice() {
        return seconds * pricePerSecond;
    }
}
 
public class Constructor02 {
    public static void main(/*String[] args*/) {
        Song song1 = new Song("Waterloo", 164, 0.01);
    }
}
The price of a song will not change as long as the fields second and pricePerSecond do not change; to avoid recomputing the price each time it is needed, the class contains a field price whose value is computed within the constructor. The method computePrice is declared to be private because it is needed only by the constructor.
  • The variable song1 is allocated and contains the null value.
  • Memory is allocated for the fields of the object and default values are assigned to the three fields.
  • The constructor is called with three actual parameters; these values are assigned to the formal parameters of the constructor method and the values of the formal parameters are assigned to the three fields.
  • The method computePrice is called; it returns a value which stored in the field price.
  • The constructor returns a reference to the object, which is stored in the variable song1. The field price can be accessed to obtain the price of a song.
Exercise Modify the class so that no song has a price greater than two currency units.

Overloading constructors

Concept Constructors can be overloaded like other methods. A method is overloaded when there is more than one method with the same name; the parameter signature is used to decide which method to call. For constructors, overloading is usually done when some of the fields of an object can be initialized with default values, although we want to retain the possibility of explicitly supplying all the initial values.
Program: Constructor03.java
// Learning Object Constructor03
//    overloading constructors
class Song {
    String name;
    int seconds;
    double pricePerSecond;
    double price;
    final static double DEFAULT_PRICE = 0.005;
 
    Song(String n, int s, double p) {
        name = n;
        seconds = s;
        pricePerSecond = p;
        price = computePrice();
    }
 
    Song(String n, int s) {
        name = n;
        seconds = s;
        pricePerSecond = DEFAULT_PRICE;
        price = computePrice();
    }
 
    private double computePrice() {
        return seconds * pricePerSecond;
    }
}
 
public class Constructor03 {
    public static void main(/*String[] args*/) {
        Song song1 = new Song("Waterloo", 164, 0.01);
        Song song2 = new Song("Fernando", 253);
    }
}
The website charges a uniform price per second for all songs, except for special offers. We define two constructors, one that specifies a price for special offers and another that uses a default price for ordinary songs.
  • The value of the static constant DEFAULT_PRICE is set as soon as the class is loaded and is displayed in the Constant area.
  • The variable song1 is allocated and contains the null value.
  • Memory is allocated for the four fields of the object and default values are assigned to the fields.
  • The constructor is called with three actual parameters; the call is resolved so that the first constructor is executed. These values are assigned to the formal parameters of the constructor method and the values of the formal parameters are assigned to the three fields.
  • The method computePrice is called; it returns a value which stored in the field price.
  • The constructor returns a reference to the object, which is stored in the variable song1.
  • The computation is then repeated for song2. Since the constructor is called with just two parameters (for name and seconds), the second constructor is executed. The value of the field pricePerSecond is assigned from the constant, not from a parameter.
Exercise Modify the class to include a constructor with one parameter for the name and with a default song length of three minutes.
Exercise Modify the class to include a constructor with no parameters, so that all fields receive default values. Is there any meaning to the following constructor?
Song() {
}

Invoking an overloaded constructor from within a constructor

Concept Constructors can be overloaded like other methods. A method is overloaded when there is more than one method with the same name; the parameter signature is used to decide which method to call. For constructors, overloading is usually done when some of the fields of an object can be initialized with default values, although we want to retain the possibility of explicitly supplying all the initial values. In such cases, it is convenient to invoke one constructor from within another in order to avoid duplicating code. Invoking the methodthis within one constructor calls another constructor with the appropriate parameter signature.
Program: Constructor04.java
// Learning Object Constructor04
//    invoking one constructor from another
class Song {
    String name;
    int seconds;
    double pricePerSecond;
    double price;
    final static double DEFAULT_PRICE = 0.005;
 
    Song(String n, int s, double p) {
        name = n;
        seconds = s;
        pricePerSecond = p;
        price = computePrice();
    }
 
    Song(String n, int s) {
        this(n, s, DEFAULT_PRICE);
    }
 
    private double computePrice() {
        return seconds * pricePerSecond;
    }
}
 
public class Constructor04 {
    public static void main(/*String[] args*/) {
        Song song1 = new Song("Waterloo", 164);
    }
}
The website charges a uniform price per second for all songs, except for special offers. We define two constructors, one that specifies a price for special offers and another that uses a default price for ordinary songs.
  • The value of the static constant DEFAULT_PRICE is set as soon as the class is loaded and is displayed in the Constant area.
  • The variable song1 is allocated and contains the null value.
  • Memory is allocated for the four fields of the object and default values are assigned to the fields.
  • The constructor is called with two actual parameters; the call is resolved so that it is the second constructor that is executed.
  • The two parameters, together with the default price, are immediately used to call the first constructor that has three parameters. The method name this means: call a constructor from this class. This constructor initializes the first three fields from the parameters, and the value of the fourth field is computed by calling the methodcomputePrice.
  • The constructor returns a reference to the object, which is stored in the variable song1.
Exercise Modify the class to include a constructor with one parameter, the name, and with a default song length of three minutes. Can this constructor call the two-parameter constructor which in turn calls the three-parameter constructor? Can a constructor call two other constructors, one after another?

Explicit default constructors

Concept When no constructor is explicitly written in a class, a default implicit constructor with no parameters exists; this constructor does nothing. If, however, one or more explicit constructors are given, there is no longer a constructor with no parameters. Should you want one, you have to write it explicitly.
Program: Constructor05.java
// Learning Object Constructor05
//    explicit default constructors
class Song {
    String name;
    int seconds;
    double pricePerSecond;
    double price;
 
    Song(String n, int s, double p) {
        name = n;
        seconds = s;
        pricePerSecond = p;
        price = computePrice();
    }
 
    Song() {
        this("No song", 0, 0.0);
    }
 
    private double computePrice() {
        return seconds * pricePerSecond;
    }
}
 
public class Constructor05 {
    public static void main(/*String[] args*/) {
        Song song1 = new Song();
    }
}
This program includes an explicit constructor with no parameters that calls the constructor with three parameters to perform initialization.
  • The variable song1 is allocated and contains the null value.
  • Memory is allocated for the four fields of the object and default values are assigned to the fields.
  • The constructor is called with no actual parameters; the call is resolved so that it is the second constructor that is executed.
  • Three constant values are used to call the first constructor. The method name this means: call a constructor from this class. This constructor initializes the first three fields from the parameters, and the value of the fourth field is computed by calling the method computePrice.
  • The constructor returns a reference to the object, which is stored in the variable song1.
Exercise Modify the class so that the constructor without parameters obtains initial values from the input.

Constructors for subclasses

Concept Constructors are not inherited. You must explicitly define a constructor for a subclass (with or without parameters). As its first statement, the constructor for the subclass must call a constructor for the superclass using the method super.
Program: Constructor06A.java
// Learning Object Constructor06A
//    constructors for subclasses
class Song {
    String name;
    int seconds;
    double pricePerSecond;
    double price;
 
    Song(String n, int s, double p) {
        name = n;
        seconds = s;
        pricePerSecond = p;
        price = computePrice();
    }
 
    private double computePrice() {
        return seconds * pricePerSecond;
    }
}
 
class DiscountSong extends Song {
    double discount;
 
    DiscountSong(String n, int s, double p, double d) {
        super(n, s, p);
        discount = d;
    }
 
    private double computePrice() {
        return seconds * pricePerSecond * discount;
    }
}
 
public class Constructor06A {
    public static void main(/*String[] args*/) {
        DiscountSong song1 = new DiscountSong("Waterloo", 164, 0.01, 0.8);
        double price = song1.price;
    }
}
The website wants to sell certain songs at a discount. The subclass DiscountSong inherits from class Song, adds a field discount and overrides computePrice to includediscount in the computation. The constructor for the subclass calls the three-parameter constructor for the superclass, passing it the three parameters that it expects. The fourth parameter is used directly in the constructor DiscountSong to initialize the field discount.
  • The variable song1 is allocated and contains the null value.
  • Memory is allocated for the five fields of the object of the subclass DiscountSong and default values are assigned to the fields. Four fields inherited from the superclass and one field discount added by the subclass.
  • The constructor for the subclass DiscountSong is called with four parameters. It calls the constructor for the superclass Song which assigns values to three fields from the parameters and the fourth by calling computePrice.
  • The superclass constructor returns and then the fourth parameter of the subclass constructor is assigned to the field discount.
  • The reference to the subclass object is returned and assigned to a variable song1 of that type.
Unfortunately, this does not do what we intended, because the superclass method for computePrice is used to compute price instead of the method from the subclass.
Exercise Could song1 be declared to be of type Song? Explain your answer.
Program: Constructor06B.java
// Learning Object Constructor06B
//    constructors for subclasses
class Song {
    String name;
    int seconds;
    double pricePerSecond;
    double price;
 
    Song(String n, int s, double p) {
        name = n;
        seconds = s;
        pricePerSecond = p;
        price = computePrice();
    }
 
    private double computePrice() {
        return seconds * pricePerSecond;
    }
}
 
class DiscountSong extends Song {
    double discount;
 
    DiscountSong(String n, int s, double p, double d) {
        super(n, s, p);
        discount = d;
        price = computePrice();
    }
 
    private double computePrice() {
        return seconds * pricePerSecond * discount;
    }
}
 
public class Constructor06B {
    public static void main(/*String[] args*/) {
        DiscountSong song1 = new DiscountSong("Waterloo", 164, 0.01, 0.8);
        double price = song1.price;
    }
}
The problem can be solved by adding a call to computePrice in the constructor for the subclass.
Check this by executing the code and ensuring that the discounted price is computed.
The disadvantage of this solution is that we are calling computePrice twice.
Program: Constructor06C.java
// Learning Object Constructor06C
//    constructors for subclasses
class Song {
    String name;
    int seconds;
    double pricePerSecond;
 
    Song(String n, int s, double p) {
        name = n;
        seconds = s;
        pricePerSecond = p;
    }
 
    public double getPrice() {
        return seconds * pricePerSecond;
    }
}
 
class DiscountSong extends Song {
    double discount;
 
    DiscountSong(String n, int s, double p, double d) {
        super(n, s, p);
        discount = d;
    }
 
    public double getPrice() {
        return seconds * pricePerSecond * discount;
    }
}
 
public class Constructor06C {
    public static void main(/*String[] args*/) {
        Song song1 = new DiscountSong("Waterloo", 164, 0.01, 0.8);
        double price = song1.getPrice();
    }
}
Normally in an object-oriented program, all the fields of an object are private and an accessor method like getPrice() is used to access the values of the fields. If this is done, the computation of the price can be placed in the accessor for the superclass and overridden in accessor for the subclass.
Check this by executing the code and ensuring that the discounted price is computed.
The disadvantage of this solution is that the computation is performed for each access of the field price.
Exercise Develop other solutions for this problem: (a) Call computePrice explicitly after the call to the constructor; (b) Modify getPrice to compute the value of price on the first call and save it for future calls. Summarize the advantages and disadvantages of all the solutions for this problem.

Constructors with object parameters

Concept An object can contain fields of other user-defined objects, not just of primitive and predefined types. There is no difference in the constructors, except that references to objects are passed as actual parameters and assigned to fields of the object.
Program: Constructor07.java
// Learning Object Constructor07
//    constructors with object parameters
class Song {
    String name;
    int seconds;
    double pricePerSecond;
 
    Song(String n, int s, double p) {
        name = n;
        seconds = s;
        pricePerSecond = p;
    }
 
    public double computePrice() {
        return seconds * pricePerSecond;
    }
}
 
class SongSet {
    public Song track1, track2;
 
    public SongSet(Song t1, Song t2) {
        track1 = t1; track2 = t2;
    }
}
 
public class Constructor07 {
    public static void main(/*String[] args*/) {
        Song song1 = new Song("Waterloo", 164, 0.01);
        Song song2 = new Song("Fernando", 253, 0.01);
        SongSet set = new SongSet(song1, song2);
        double price1 = set.track1.computePrice();
        double price2 = set.track2.computePrice();
    }
}
Two objects of type Song are allocated and assigned to fields of another object of type SongSet which has two fields of type Song.
  • Execute the program until the two objects of type Song are allocated and their references assigned to the variables song1 and song2. (You may want to select Animation / Run Until (ctrl-T) to skip the animation of these declarations.)
  • A variable set is allocated. An object of type SongSet is allocated with default null fields.
  • The constructor for SongSet is called and the references in the two variables song1 and song2 are passed as actual parameters. These references are stored in the two fieldstrack1 and track2.
  • The reference to the object of class SongSet is returned and stored in set.
  • The prices of the two objects are obtained and stored in the variables price1 and price2set is an object of type Songset, while set.track1 is an object of type Songand thus can be used to call the method computePrice.
Exercise Modify the program so that the variables song1 and song2 are not used; instead, the constructors for the songs are embedded within the constructor call for SongSet.
Exercise Modify the program so the constructors for the songs are call within the constructor for SongSet. Under what circumstances would this be done?

Constructors with subclass object parameters

Concept An object of a subclass is also an object of the type of the superclass. Therefore, it can be used when an actual parameter is expected.
Program: Constructor08.java
// Learning Object Constructor08
//    constructors with subclass object parameters
class Song {
    String name;
    int seconds;
    double pricePerSecond;
 
    Song(String n, int s, double p) {
        name = n;
        seconds = s;
        pricePerSecond = p;
    }
 
    public double computePrice() {
        return seconds * pricePerSecond;
    }
}
 
class DiscountSong extends Song {
    double discount;
 
    DiscountSong(String n, int s, double p, double d) {
        super(n, s, p);
        discount = d;
    }
 
    public double computePrice() {
        return seconds * pricePerSecond * discount;
    }
}
 
class SongSet {
    public Song track1, track2;
 
    public SongSet(Song t1, Song t2) {
        track1 = t1; track2 = t2;
    }
}
 
public class Constructor08 {
    public static void main(/*String[] args*/) {
        Song song1 = new Song("Waterloo", 164, 0.01);
        DiscountSong song2 = new DiscountSong("Fernando", 253, 0.01, 0.8);
        SongSet set = new SongSet(song1, song2);
        double price1 = set.track1.computePrice();
        double price2 = set.track2.computePrice();
    }
}
We allocate two objects, one of type Song and one of type DiscountSong, and use them as actual parameters in the constructor for an object of type SongSet that expects two parameters of type Song.
  • Execute the program until the two objects one of type Song the other of type DiscountSong are allocated and their references assigned to the variables song1 and song2, respectively. (You may want to select Animation / Run Until (ctrl-T) to skip the animation of these declarations.)
  • The variable set is allocated, and an object of type SongSet is allocated with default null fields.
  • The constructor for SongSet is called and the references in the two variables song1 and song2 are passed as actual parameters. These references are stored in the two fieldstrack1 and track2.
  • The reference to the object of class SongSet is returned and stored in set.
  • The prices of the two objects are obtained and stored in the variables price1 and price2set is an object of type Songset, while set.track1 is an object of type Songand thus can be used to call the method computePrice of that class. Similarly for price2, except that set.track2 is an object of type DiscountSong; check that the method computePrice of this class is called.

No comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...

java

Popular java Topics