Welcome to Lesson 7!


Learning Objectives for Today's Lesson

By the end of today's lesson, you should know...
  • What is encapsulation?
  • What is information hiding?
  • What is the rationale for using encapsulation and information hiding and how do you implement them as part of your class?
  • What are mutator methods (setters) and accessor methods (getters)?
  • What is a toString() method and why is it useful?


1. Encapsulation and Information Hiding

Encapsulation and Information Hiding

  • As programs become larger, we need to change the way we work on them
  • Large programs usually have teams of people who work on them like:
    • One or two software engineers may work on the user interface
    • Another two or three may work on the logic of the application and various features
    • Another may work on data storage and retrieval
  • When many programmers work together, they need to take special care with the data
  • The data type of an application may need to change as the program develops
  • For example, we may start a program by keeping money in a variable of type double:
    double money = 1.23;
  • Later we may decide that two int variables are a better choice:
    int dollars = 1;
    int cents = 23;
    
  • Any part of the program that used the double variable would have to change

The Prescription for Larger Programs

  • To make larger programs more manageable, we use classes
  • Two important features of classes are:
    • Encapsulation
    • Information hiding
  • These two concepts are closely intertwined
  • Sometimes you will see a reference to one term when the writer means both terms

Encapsulation

Encapsulation: inclusion of a number of items into a single unit

  • We saw an example of encapsulation when we looked at our first phone class
  • Encapsulation allowed us to group related variables in one container (capsule)
  • If the variables in the grouping are logically related, we can more easily keep track of what is happening with the data
  • For example, we can keep all the data about a product inside one container
  • In addition to data, classes extend encapsulation to include member methods
  • Member methods are methods that work with the member variables
  • Thus, methods like addYear() are grouped into the class that describes a Person

Information Hiding

Information hiding: hiding the parts of the design that are most likely to change (a.k.a. data hiding)

  • As programs develop, developers change them frequently
  • Even after a program is first completed and released, successful programs are updated with new features
  • With large programs, making a change in one area can cause problems in other areas
  • By hiding the parts of a program most likely to change, a programmer can make implementing changes much easier
  • The ability to change parts of a program without affecting other parts becomes more important as programs grow larger
Making member variables private means that a user can only access them by calling methods

Private Data

  • Recall that the instance variables of our example classes
  • In the first example we declared the variables with the modifier public:
    public String brand;
    public double price;
    
  • Yet good practice dictates that we would make these variables private:
    private String brand;
    private double price;
    
  • The modifier public means there are no restrictions on accessing the variables. They can be accessed by any class.
  • The modifier private means the variable cannot be accessed outside of the class
  • We could test this feature by writing a test like the following:
    Phone iphone = new Phone();
    iphone.brand = "iPhone";
    iphone.price = 599.95;
    
  • If we try to compile this code, then we get an error message
  • However, if we remove the keyword private, then the test code compiles without error
  • So why bother with private data?
  • Good software engineering practice says you should almost always declare class variables private
    • One of the exceptions is that constants may be declared public
    • There are other rare cases, but you will not run across them in this course
    • Thus, for this course, always declare non-constant instance variables private
  • The most important reason is to hide the internal implementation details of the class
    • You want to prevent programmers from relying on those details so that you can safely modify your class implementation at any time without worrying that you will break code that uses the class
  • Additionally, our methods define how we want users of our class to interact with the data. We can control access and put checks in place in our methods to make sure the data of our class is handled properly by other programmers.

Example Student Class (from Lesson 6 Practice Exam Questions)


public class Student {

    public String name;
    public double gpa;

    public void printStudent() {
        System.out.println("Hi! My name is " + name + ".\n"
            + "And, my GPA is " + gpa + ".");
    }

}

public class StudentTest {
    public static void main(String[] args) {
        Student Jackie = new Student();
        Jackie.name = "Jackie Chan";
        Jackie.gpa = 3.5;
        Jackie.printStudent();
    }

}

  • Note we are not free to make revisions to our class design without affecting the way in which a user will interact with it.
  • For example, if we later revise our code to store first and last names of students as separate variables, the code in StudentTest (and all other classes relying on our code) will break.
public class Student {

    public String firstName;
    public String lastName;
    public double gpa;

    public void printStudent() {
        System.out.println("Hi! My name is " + name + ".\n"
            + "And, my GPA is " + gpa + ".");
    }

}

public class StudentTest {
    public static void main(String[] args) {
        Student Jackie = new Student();
        Jackie.name = "Jackie Chan"; //breaks!
        Jackie.gpa = 3.5;
        Jackie.printStudent();
    }

}

2. Accessor and Mutator Methods

  • You should always make all instance variables of a class private
  • However, you will still need to interact with the data of a class object.
  • Applying the principle of information hiding, we can revise our Phone class from the prior lesson like so:
public class Phone {
private String brand; 
private String model;
private double price;

public String getBrand() {
return brand;
}

public String getModel() {
return model;
}
 
public double getPrice() {
return price;
}

public void setBrand(String newBrand) {
brand = newBrand;
}

public void setModel(String newModel) {
model = newModel;
}

public void setPrice(double newPrice) {
price = newPrice;
}

public void makeCall() {
System.out.println(brand + " is now making a call.");
}

public void makeCall(String name) {
System.out.println(brand + " is now making a call to " + name);
}
}

  • Above, we make the member variables private, and define methods to allow users to interact with objects of the Phone class.
  • We can then revise our PhoneTest class to call these methods:

public class PhoneTest {
    public static void main(String[] args) {
        Phone iphone = new Phone();
        iphone.setBrand("iPhone");
        iphone.setModel("7S");
        iphone.setPrice(521.56);

        Phone samsung = new Phone(); samsung.setBrand("Samsung"); samsung.setModel("Galaxy S8"); samsung.setPrice(499.99);
        
iphone.makeCall();         samsung.makeCall(); } }


Mutator Methods

  • Recall the setbrand(), setModel() and setPrice() methods from the Phone class:
public class Phone {
private String brand; 
private String model;
private double price;

public void setBrand(String newBrand) {
brand = newBrand;
}

public void setModel(String newModel) {
model = newModel;
}
 
public void setPrice(double newPrice) {
price = newPrice;
}
}
  • These methods are mutator methods - an operation that modifies the object
    • Recall that the word mutate means to change
    • After a mutator method executes, the state of the class changes
  • In this case, the setPrice(), setModel(), setBrand() methods change the values of the private member variables
  • Typically, mutator methods have a void return type, but do take a parameter


Accessor Methods

  • Recall the getBrand(), getModel() and getPrice() methods from the Phone class:
public class Phone {
private String brand; 
private String model;
private double price;

public String getBrand() {
return brand;
}

public String getModel() {
return model;
}
 
public double getPrice() {
return price;
}
}
  • The getBrand(), getModel(), getPrice() methods are accessor methods
  • An accessor method queries the object for some information without changing the state of the object
  • These methods return information about a Phone without changing the state of the object.
  • For example:
System.out.println("The price of this phone: " + iphone.getPrice());
  • Therefore, these methods frequently have a non-void return type, but no parameters.


Get and Set Methods

  • Changing and retrieving a single value is a common operation
  • In these cases, the standard practice is to use the word get or set in front of the variable name
  • Thus, the name for a mutator method to change the value of price is setPrice():
    public void setPrice(double newPrice) {
        price = newPrice;
    }
    
  • Colloquially, these mutator methods are known as "setters"
  • The name for an accessor method to retrieve the value of price is getPrice():
    public double getPrice() {
        return price;
    }
  • Colloquially, these accessor methods are called "getters" 


Phone Class with Accessor and Mutator Methods

  • At the end of last class, we altered our Phone class to employ "getters" and "setters" to allow users to access or modify the data stored in the member variables.
  • Below is the Phone class that includes these getters and setters, as well as a new method called toString() which now replaces the printPhone method. 

Example Phone Class

/**
 * Phone.java example
 * @author parrishj
 *
 */

public class Phone {
    public String brand;
    public String model;
    public double price;
   
    /**Accessors*/

    /**
     * Returns the brand of phone
     * e.g. iPhone or Samsung
     * @return the brand
     */
    public String getBrand() {
        return brand;
    }
   
    /**
     * Returns the phone's model
     * e.g. 7 or Galaxy S5
     * @return the model
     */
    public String getModel() {
        return model;
    }
   
    /**
     * Returns the phone's current
     * market price
     * @return the price
     */
    public double getPrice() {
        return price;
    }
   
    /**Mutators*/

    /**
     * Assigns a brand to the phone
     * e.g. iPhone or Samsung
     * @param newBrand the brand of phone
     */
    public void setBrand(String newBrand) {
        brand = newBrand;
    }
   
    /**
     * Assigns a model to the phone
     * e.g. 7 or Galaxy S5
     * @param newModel the phone's model
     */
    public void setModel(String newModel) {
        model = newModel;
    }

    /**
     * Assigns the phone the current
     * market price for that brand
     * and model
     * @param newPrice the market price
     */
    public void setPrice(double newPrice) {
        price = newPrice;
    }
   
    /**Additional Methods*/

    /**
     * Displays a message alerting the user
     * that the phone is now making a call
     */
    public void makeCall() {
        System.out.println(brand + " is now making a call.");
    }
   
    /**
     * Displays a message alerting the user
     * that the phone is now making a call
     * to a specified person
     * @param name the person who is being called
     */
    public void makeCall(String name) {
        System.out.println(brand + " is now calling " + name);
    }
   
    /**
     * Returns a String of all the important
     * information about the phone, including
     * brand, model and price
     * @return the important phone information
     */
    @Override public String toString() {
        return "Brand: " + brand 
            + "\nModel: " + model
            + "\nPrice: " + price;
    }
}

Local Variables, Parameters and Shadowing

Shadows of two people
  • Shadowing is when a local variable or parameter has the same name as a member variable of a class
  • Local variables and parameters with same name as a member variable hide the member variable
  • Any use of the variable's name will refer to the local variable or parameter
  • This is the source of many an elusive bug
  • You should not use the same name for a local variable or parameter and a class member variable
  • Remember that a parameter is like a local variable but initialized in a special way
  • Thus you should use a different name for a parameter and a member variable
  • Which lines contain local shadowing in the following program and how do you correct the problem.

image source

Example of Shadowing

public void setBrand(String brand) {
    brand = brand;
}


What the Shadow Knows

  • We correct shadowing by making sure a parameter name is different than a member variable name
  • For example:
    public void setBrand(String theBrand) {
    brand = theBrand;
    }
  • In the above code, we changed the name of the parameter so that it differs from the name of the instance variable.


Activity 7.1: A Person Class with Accessors, Mutators, and Information Hiding (10 pts)

Showing a Person constructor

  • Open up Person.java class from the Lesson 6.
  • Alter the member variables to make the access modifier private. For example:
private String name;
//You alter the rest
  • Next, write accessor and mutator methods to allow users to access and modify the data stored in the instance variables. For example:
public String getName() {
    return name;
}

public void setName(String theName) {
    name = theName;
}
  • Note: As shown the mutator method definition above, please try to avoid "shadowing"
  • Copy and paste the new test file below into PersonTest.java (you can erase the old contents of the file).
  • Then add the method calls were indicated in below in bold.
/**
* PersonTest.java, Activity 7.1
* @author
* CIS 36B
*/

import java.util.Scanner;

public class PersonTest {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String name, gender;
        int age;
        Person person = new Person();
       
        System.out.print("Welcome!\n\nEnter your name: ");
        name = input.nextLine();
        //call setName here
        System.out.println("You entered: " + //call getName here + "\n");

        System.out.print("Enter your age: ");
        age = input.nextInt();
        //call setAge here
        System.out.println("You entered: " + //call getAge here + "\n");
       
        System.out.print("Enter your gender: ");
        gender = input.next();
        //call setGender here
        System.out.println("You entered: " + //call getGender here + "\n");

        System.out.println("Your Summary:\n" + person.printPerson); //fix mistake on this line
    }
}
  • When you get the correct output (as shown below), upload Person.java and PersonTest.java to Canvas
Sample Output (Note that user input may vary):

Welcome!

Enter your name: Marta Gomez
You entered: Marta Gomez

Enter your age: 25
You entered: 25

Enter your gender: Female
You entered: Female

Your Summary:
Name: Marta Gomez
Age: 25
Gender: Female

3. Required Member Methods:  toString()

  • Java expects certain methods in all classes
  • The reason is the Java standard libraries have code that assumes such methods are defined
  • One of these methods is toString()
  • If you do not supply the method then Java will supply one for you
  • Java can supply default versions of required methods through something known as inheritance, which we will cover later
  • The default toString() method usually does not provide the behavior you want
  • Thus you should normally provide your own toString() method for a class
  • The purpose of toString() the method is to combine all of the interesting or useful information about one Object into a single String.
  • Once this String value is returned, it can be used to display the information about that Object either to the console or in a file.
  • In the case of a Phone, we return the brand, model and price:
    @Override public String toString() {
            return
    "Brand: " + brand 
                + "\nModel: " + model
                + "\nPrice: " + price
    ;
    }
  • Note that you should use the @Override tag to indicate you are re-defining the default method provided by Java.
  • Using the @Override tag alert the compiler to verify that your method is correctly defined.
  • Note that when you print a class, the toString() method gets called automatically.
  • Thus, the following are equivalent:
Phone samsung = new Phone();
samsung.setBrand("Samsung");
samsung.setModel("Galaxy S8");
samsung.setPrice(499.99);

System.out.println(samsung); //invokes toString() automatically

//alternately
System.out.println(samsung.toString());

File file = new File("someFile.txt");
PrintWriter out = new PrintWriter(file);

out.println(samsung); //invokes toString() automatically

//alternately
out.println(samsung.toString());

out.close();


Activity 7.2: A Person Class with a toString Method (10 pts)

Showing a Person constructor
  • Open Person.java class from the previous activity.
  • Remove the printPerson method from this class.
  • Instead, write a toString() method that can be used to display the Student Object in the following manner:
Name: <The Name>
Age: <The Age>
Gender: <The Gender>

  • Note the use of the <> above. Do not add these <> to your String. Instead, these <> are commonly used to mean a value will be filled in later.
  • Don't forget the @Override tag in front of the toString() method
  • Using the below starter code and the toString method, we will now display the Person object both to the console and to a File.
  • Copy and paste the new test file below into PersonTest.java (you can erase the old contents of the file).
  • Then, update the code to call the toString method to display the Person object both to the console and to a file.
  • Notice the versatility of this method!
  • Finally, refresh your project on Canvas (right-click on the project and select the refresh option). You should see the person.text file appear in the package exploreer. Open this file to verify that you get the correct output in person.txt.
/**
* PersonTest.java, Activity 7.2
* @author
* CIS 36B
*/

import java.util.Scanner;
import java.io.*;

public class PersonTest {
    public static void main(String[] args) throws IOException{
        Scanner input = new Scanner(System.in);
        File file = new File("person.txt");
        PrintWriter out = new PrintWriter(file);

        String name, gender;
        int age;
        Person person = new Person();
       
        System.out.print("Welcome!\n\nEnter your name: ");
        name = input.nextLine();
        person.setName(name);
        System.out.println("You entered: " + person.getName() + "\n");

        System.out.print("Enter your age: ");
        age = input.nextInt();
        person.setAge(age);
        System.out.println("You entered: " + person.getAge() + "\n");
       
        System.out.print("Enter your gender: ");
        gender = input.next();
        person.setGender(gender);
        System.out.println("You entered: " + person.getGender() + "\n");

        System.out.println("Your Summary:\n" + //call toString here);
        out.println("Your Summary: ");
        //add one line of code to write person to the file - Hint: call toString!
        out.close();
        

    }
}
  • When you get the correct output (as shown below), upload Person.java and PersonTest.java to Canvas
Sample Output (Note that user input may vary):

Welcome!

Enter your name: Marta Gomez
You entered: Marta Gomez

Enter your age: 25
You entered: 25

Enter your gender: Female
You entered: Female

Your Summary:
Name: Marta Gomez
Age: 25
Gender: Female

Sample Output in person.txt:

Your Summary:
Name: Marta Gomez
Age: 25
Gender: Female


Wrap Up

  • Answer the Lesson 7 Practice Exam Questions on Canvas


Upcoming Assignments:

  • Lesson 7 Practice Exam Questions due Tuesday at 11:59pm
  • Activities 7.1 and 7.2 due Tuesday at 11:59pm
  • Quiz 4 due Friday at 11:59pm
  • Peer Reviews of Lesson 7 Practice Exam Questions due Saturday at 11:59pm
  • Lab 4 due next Monday at 11:59pm

~ Have a Great Day! ~