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.
LO | Topic | Java Files (.java) | Prerequisites |
"What are constructors for?" | What are constructors for? | Constructor01A, B, C | |
"Computation within constructors" | Computation within constructors | Constructor02 | 1 |
"Overloading constructors" | Overloading constructors | Constructor03 | 2 |
"Invoking an overloaded constructor from within a constructor" | Invoking another constructor | Constructor04 | 3 |
"Explicit default constructors" | Explicit default constructors | Constructor05 | 3 |
"Constructors for subclasses" | Constructors for subclasses | Constructor06A, B, C | 3 |
"Constructors with object parameters" | Constructors with object parameters | Constructor07 | 3 |
"Constructors with subclass object parameters" | Constructors with subclass | ||
object parameters | Constructor08 | 6, 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 methodcomputePrice
on the object; the method computes and returns the price, which is assigned to the variableprice
.
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 methodcomputePrice
on the object; the method computes and returns the price, which is assigned to the variableprice
.
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 methodcomputePrice
on the object; the method computes and returns the price, which is assigned to the variableprice
.
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 fieldprice
. - The constructor returns a reference to the object, which is stored in the variable
song1
. The fieldprice
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 fieldprice
. - 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 (forname
andseconds
), the second constructor is executed. The value of the fieldpricePerSecond
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 method
this
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 methodcomputePrice
. - 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 fielddiscount
added by the subclass. - The constructor for the subclass
DiscountSong
is called with four parameters. It calls the constructor for the superclassSong
which assigns values to three fields from the parameters and the fourth by callingcomputePrice
. - 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 variablessong1
andsong2
. (You may want to selectAnimation / Run Until (ctrl-T)
to skip the animation of these declarations.) - A variable
set
is allocated. An object of typeSongSet
is allocated with default null fields. - The constructor for
SongSet
is called and the references in the two variablessong1
andsong2
are passed as actual parameters. These references are stored in the two fieldstrack1
andtrack2
. - The reference to the object of class
SongSet
is returned and stored inset
. - The prices of the two objects are obtained and stored in the variables
price1
andprice2
.set
is an object of typeSongset
, whileset.track1
is an object of typeSong
and thus can be used to call the methodcomputePrice
.
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 typeDiscountSong
are allocated and their references assigned to the variablessong1
andsong2
, respectively. (You may want to selectAnimation / Run Until (ctrl-T)
to skip the animation of these declarations.) - The variable
set
is allocated, and an object of typeSongSet
is allocated with default null fields. - The constructor for
SongSet
is called and the references in the two variablessong1
andsong2
are passed as actual parameters. These references are stored in the two fieldstrack1
andtrack2
. - The reference to the object of class
SongSet
is returned and stored inset
. - The prices of the two objects are obtained and stored in the variables
price1
andprice2
.set
is an object of typeSongset
, whileset.track1
is an object of typeSong
and thus can be used to call the methodcomputePrice
of that class. Similarly forprice2
, except thatset.track2
is an object of typeDiscountSong
; check that the methodcomputePrice
of this class is called.
No comments:
Post a Comment