Constructors

Figure 246. Creating and initializing rectangles Slide presentation Create comment in forum
int a;
a = 33;
Rectangle r = new Rectangle();

r.width = 28;
r.height = 10;
r.hasSolidBorder = false;

Combining statements desired:

int a = 33; // works!
Rectangle r = new Rectangle(28, 10, false); // how ???

How to get this work?


Figure 247. Defining a constructor Slide presentation Create comment in forum
public class Rectangle {
    int width, height;
    boolean hasSolidBorder;
         ...
    public  Rectangle  (int width, int height, boolean hasSolidBorder){
        this.width = width;
        this.height = height;
        this.hasSolidBorder = hasSolidBorder;
    }
}

Similar to Figure 223, “Method definition syntax ” but:

No return type declaration.

Constructor's name equals class name.

Figure 248. Constructor syntax Slide presentation Create comment in forum
constructorName (listOfArguments) {
    [constructor body]
}
Empty argument list

Default constructor e.g. obj = new MyClass().

Non-empty argument list

Non-default constructor, e.g. obj = new MyClass("xyz").


Figure 249. Constructors Slide presentation Create comment in forum
  • Can only be executed on object creation.

  • Are being called prior to any non-constructor method.

  • Only one of potentially multiple constructors will be executed exactly one time.


Figure 250. Multiple constructors by overloading Slide presentation Create comment in forum
public class Rectangle {
    int width, height;

    public Rectangle() {
        width = height = 1;
    }
    public Rectangle(int width, int height){
        this.width = width;
        this.height = height;
    }
    public Rectangle(int widthAndHeight) {
        width = height = widthAndHeight;
    }
}
Multiple constructors by overloading

Figure 251. Constructor calls within constructor Slide presentation Create comment in forum
public class Rectangle {
  int width, height;

  public Rectangle(int width,
                int height){
    this.width = width;
    this.height = height;
  }
  public Rectangle() {
    width = height = 1;
  }
  public Rectangle(
     int widthAndHeight) {
    width = height =
          widthAndHeight;
  }
}
public class Rectangle {
  int width, height;

  public Rectangle(int width, 
                   int height){
    this.width = width;
    this.height = height;
  }
  public Rectangle() {
    this(1, 1); 
  }
  public Rectangle(
    int widthAndHeight) {
    this(widthAndHeight,
         widthAndHeight); 
  }
}

Reusing constructor Rectangle(int width, int height) with parameters width = height = 1.

Reusing constructor Rectangle(int width, int height) with parameters width = height = widthAndHeight.


Figure 252. Instances by overloaded constructors Slide presentation Create comment in forum
Rectangle standard = new Rectangle(); // 1 x 1
Rectangle square = new Rectangle(2); // 2 x 2
Rectangle individual = new Rectangle(2, 7); // 2 x 7
Instances by overloaded constructors

Figure 253. No constructor vs. default constructor Slide presentation Create comment in forum
Equivalent: Rectangle r = new Rectangle();
public class Rectangle {
    int width, height;
    boolean hasSolidBorder;

    // Default constructor, empty body.
    public Rectangle ( ){}
}
public class Rectangle {
    int width, height;
    boolean hasSolidBorder;
}

Figure 254. Absent default constructor Slide presentation Create comment in forum
public class Rectangle {
  int width, height;
  boolean hasSolidBorder;

  public Rectangle(int width,
                  int height,
    boolean hasSolidBorder){
      this.width = width;
      this.height = height;
      this.hasSolidBorder =
                hasSolidBorder;
  }
}
Rectangle r = 
   new Rectangle(3, 6, false);

o.K.: Using non-default constructor.

Rectangle r = new Rectangle();

Wrong: Default constructor undefined, but non-default constructor present.


exercise No. 87

Extending the employee example. Create comment in forum

Q:

Extend the Employee example from chapter 4 of [Kurniawan] by adding two methods:

  • /**
     * Raise the employee's salary by the given percentage.
     *
     * Example: If the current annual salary is 30000 € then
     * raising by 2,5% will result in 30750 €
     *
     * @param percentage Raise the current salary by this percentage.
     *
     */
    public void raiseSalary(double percentage) {
      // TODO: implement me!
    }
  • /**
     * Print the employee's current state to standard output like e.g.:
     *
     * <pre>Age:25
    Salary:30000.00</pre>
     */
    public void print() {
      // TODO: implement me!
    }

Run your implementation by a separate class Driver:

package company;

public class Driver {

   public static void main(String[] args) {

      final Employee jim = new Employee(25, 30_000.00);
      jim.print();

      System.out.println("Raising salary by 2%");

      jim.raiseSalary(2);
      jim.print();
   }
}

The expected output reads:

Age:25
Salary:30000.00
Raising salary by 2%
Age:25
Salary:30600.00

A:

/**
 * Raise the employee's salary by the given percentage.
 *
 * Example: If the current annual salary is 30000 € then
 * raising by 2,5% will result in 30750 €
 *
 * @param percentage Raise the current salary by this percentage.
 *
 */
public void raiseSalary(double percentage) {
  salary *= (1 + percentage / 100);
}

/**
 * Print the employee's current state to standard output like e.g.:
 *
 * <pre>Age:25
Salary:30000.00</pre>
*/
public void print() {
  System.out.println("Age:" + age);
  System.out.format("Salary:%6.2f\n", salary);
}

exercise No. 88

Refining access to an employee's attributes Create comment in forum

Q:

In Extending the employee example. you had two classes:

  • A class Employee defining an employee's attributes and methods.

  • A class Driver containing a main method to start the application.

Currently both classes (hopefully!) reside in the same package company. We will assume the two attributes age and salary yet have no public, private or protected access modifiers :

package company;

public class Employee {
   int age;
   double salary;
 ...
}

Does the following code work?

package company;

public class Driver {

   public static void main(String[] args) {

      final Employee jim = new Employee(25, 30_000.00);

      jim.salary=40_000.00; // Raising jim's salary by direct member access

      jim.print();

   }
}

Modify your application by:

  • Creating a new package named model.

  • Move your Employee class from its current package company to model but leave your Driver class in its current package company. You may simply use your IDE's drag'n drop capabilities.

What do you observe? Explain this result. How do you solve the new problem?

Tip

Read the section on public, private, protected and default access level.

A:

Moving the Employee class from its company package to model changes just one line of code in your Employee.java class namely its package declaration:

package company;

public class Employee {
   int age;
   double salary;
   ...
package model;

public class Employee {
   int age;
   double salary;
   ...

The movement also adds an additional import statement to your Driver class which is required since the Employee class now resides in a different package:

package company;

public class Driver {

   public static void
     main(String[] args) {
      ...
package company;

import model.Employee;

public class Driver {

   public static void
       main(String[] args) {
      ...

Unfortunately a Driver class still residing in our company package will no longer compile due to a newly introduced access restriction:

package company;

public class Driver {

   public static void main(String[] args) {

      final Employee jim = new Employee(25, 30_000.00);

      jim.salary=40_000.00; // Compiler error:
                            // "The field Employee.salary is not visible"

      jim.print();

   }
}

This error message is in accordance with Table 4.1 from the Encapsulation and Access Control section of [Kurniawan]: The attributes age and salary have no access modifiers at all. Hence default access prohibits classes belonging to other packages accessing the attributes in question. More clearly: Members from class Driver have no permission to access either age or salary.

There are two possible ways to overcome this problem:

Granting access to alien classes (quick and dirty)

Raising access level from default to public thereby violating the principle of encapsulation:

public class Employee {
   public int age;
   public double salary;
 ...
Providing a so called setter method in our Employee class of access type public
package model;

public class Employee {
   int age;
   double salary;

   public void
     setSalary(double salary) {
      this.salary = salary;
   }
   ...
package company;

import model.Employee;

public class Driver {

  public static void
      main(String[] args) {

    final Employee jim =
      new Employee(25, 30_000.00);

      jim.setSalary(40_000.00);
 ...

The novice reader may question the advantage of the second approach: Alien classes have essentially the same means changing an employee's salary as with the first approach.

There is however one apparent benefit: Developers do have better bug tracking options on offer:

public void setSalary(double salary) {
  if (salary < 0) {
  System.err.println("This is odd: A salary may not become negative!" +
                     " Ask your union about minimum wages.");
  } else {
    this.salary = salary;
  }
}

exercise No. 89

Constructors variable names and this. Create comment in forum

Q:

Currently our constructor is being implemented as:

public Employee(int ageValue, double salaryValue) {
  age = ageValue;
  salary = salaryValue;
}

This imposes problems with respect to proper documentation. A developer will try to choose reasonable variable names clearly indicating the desired purpose like age and salary.

But the ratio for choosing ageValue and salaryValue originates from the need to avoid shadowing of variable names. From the viewpoint of code comprehensibility we prefer:

public Employee(int age, double salary) {
                    // Compiler warnings:
   age = age;       // The assignment to variable age has no effect
   salary = salary; // The assignment to variable salary has no effect
}

This compiles correctly but yields an unexpected result: The constructor variables int age and int salary will be assigned to themselves as being indicated by a corresponding compiler warning. More important their values don't make it to the age and salary attributes being defined on class level.

Apparently we are missing something. Explain these two compiler warnings. How can the underlying conflict be resolved?

Tip

Read the section in [Kurniawan] about the this keyword.

A:

When choosing public Employee(int age, double salary) we have two sets of variables (age, salary) in two different conflicting scopes:

Class scope:
public class Employee {
   public int age;
   public double salary;
  ...
}
Method's formal parameter list scope:
public Employee(int age, double salary) {.../* Constructor's method body */}

Within the constructor's method body the parameter list scope will take precedence over class scope. Thus the assignment age = age will assign the constructor's argument age to itself rather than assigning it to the instance variable age being defined within class scope.

We may explicitly resolve this scope conflict in our favour by qualifying the instance variables age and salary using their respective scope being represented by the this keyword:

public Employee(int age, double salary) {
  // The "this" keyword refers to class scope
  this.age = age;
  this.salary = salary;
}

exercise No. 90

Your personal String class Create comment in forum

Q:

This exercise is about using packages for resolving class name clashes.

Define your own String class having a suitable constructor and a public String toString() method allowing for the following snippet:

public static void main(...) {
  String john = new String("John");
  System.out.println("Name: " + john);
  System.out.println(john.sayHello());
}

The expected output reads:

Name: John
Hello 'John'

Tip

  1. Use a java.lang.String instance variable inside your private String class.

  2. Your class' constructor is bound to accept standard Java string literals. Using java.lang.String internally requires a fully qualified class name.

A:

We implement our private String class in a package de.hdm_stuttgart.mi.sd1.tooling:

package de.hdm_stuttgart.mi.sd1.tooling;

public class String { 

    final java.lang.String s; 

    public String(final java.lang.String s ) {
        this.s = s;
    }

    public java.lang.String  sayHello() {
        return "Hello '" + s + "', good morning!";
    }
}

Shadowing Java java.lang.String.

Using a standard Java java.lang.String instance internally. Fully qualified class name required resolving potential name clash.

Our constructor also accepts a standard Java java.lang.String instance.

Returning a java.lang.String instance.

There are basically two ways using de.hdm_stuttgart.mi.sd1.tooling.String and java.lang.String in the same scope:

Fully qualify java.lang.String:
package de.hdm_stuttgart.mi;

import de.hdm_stuttgart.mi.sd1.tooling.String;

public class Main {

    public static void main(java.lang.String  [] args) {
        String  s = new String("John");
        System.out.println(s.sayHello());
    }
}

Qualifying is required to avoid shadowing by de.hdm_stuttgart.mi.sd1.tooling.String.

Due to the import statement this is actually de.hdm_stuttgart.mi.sd1.tooling.String.

Fully qualify de.hdm_stuttgart.mi.sd1.tooling.String:
package de.hdm_stuttgart.mi;

public class Main {

    public static void main(String  [] args) {
        de.hdm_stuttgart.mi.sd1.tooling.String  s =
                new de.hdm_stuttgart.mi.sd1.tooling.String("John");
        System.out.println(s.sayHello());
    }
}

A Java standard String instance.

In order not to override the implicit import java.lang.String we have to fully qualify our own de.hdm_stuttgart.mi.sd1.tooling.String class.