mypages.valdosta.edu  · web viewthis is a tutorial on inheritance and the . arraylist . class....

24
CS 1302 – Lab 3 This is a tutorial on inheritance and the ArrayList class. There are 6 stages to complete this lab: Stag e Title Text Reference 1 Inheritance & Superclass-Subclass Relationships 11.1-11.4 2 Polymorphism & Dynamic Binding 11.7, 11.8 3 Casting 11.9 4 The ArrayList Class 11.11 5 An ArrayList of Dogs 11.11 6 Checking Account Example 11.14 To make this document easier to read, it is recommend that you turn off spell checking in Word: 1. Choose: File, Option, Proofing 2. At the very bottom, check: “Hide spelling errors…” and “Hide grammar errors…” Stage 1 - Inheritance & Superclass-Subclass Relationships In this stage we introduce the concept of inheritance and superclass-subclass relationships. 1. (Read, no action required). a. Suppose we define a Dog class for an application as shown on the right. Later we decide that we need a WolfDog class that is essentially the same as the Dog class except that it has an additional behavior, howl as shown on the right. For example, suppose the name instance variable, bark and getName methods are identical. It would be useful if we could share code between classes so that we could define these items that are in common one time and use them in multiple classes. b. Java allows us to extend another class. For example: public class WolfDog extends Dog { 1

Upload: truongxuyen

Post on 26-Dec-2018

214 views

Category:

Documents


0 download

TRANSCRIPT

CS 1302 – Lab 3

This is a tutorial on inheritance and the ArrayList class. There are 6 stages to complete this lab:

Stage Title Text Reference1 Inheritance & Superclass-Subclass Relationships 11.1-11.42 Polymorphism & Dynamic Binding 11.7, 11.83 Casting 11.94 The ArrayList Class 11.115 An ArrayList of Dogs 11.116 Checking Account Example 11.14

To make this document easier to read, it is recommend that you turn off spell checking in Word:

1. Choose: File, Option, Proofing2. At the very bottom, check: “Hide spelling errors…” and “Hide grammar errors…”

Stage 1 - Inheritance & Superclass-Subclass Relationships

In this stage we introduce the concept of inheritance and superclass-subclass relationships.

1. (Read, no action required).

a. Suppose we define a Dog class for an application as shown on the right. Later we decide that we need a WolfDog class that is essentially the same as the Dog class except that it has an additional behavior, howl as shown on the right. For example, suppose the name instance variable, bark and getName methods are identical. It would be useful if we could share code between classes so that we could define these items that are in common one time and use them in multiple classes.

b. Java allows us to extend another class. For example:

public class WolfDog extends Dog {

We describe this relationship as a superclass-subclass relationship, where Dog is the superclass and WolfDog is the subclass. In UML, this relationship is depicted with a hollow triangle on the end of a solid line connecting the two classes as shown in the class diagram on the right.

c. This relationship is referred to as inheritance which means that all the public members of the superclass are inherited by the subclass. In other words, bark and getName are defined in the Dog class and accessible (inherited) in the WolfDog class. For example, we can create a WolfDog and call the bark and getName methods:

WolfDog wd = new WolfDog(“Juno”);wd.bark();String name = wd.getName();

1

d. The subclass can also define specialized behaviors. For example, the howl method above.e. This relationship is also called an is-a relationship. For example, a WolfDog is-a Dog. This and other concepts

related to inheritance will be explored in much more depth in class.

2. Do the following:

a. Establish a Workspace – Create a folder on your drive where you will put your lab or use an existing one.

b. Run Eclipse – As the program begins to run, it will ask you to navigate to the Workspace you want to use.

c. Create a Project – We will create the Project for Lab 3.

i. Choose: File, New, Java Project.ii. Supply a project name, lab03_lastNameFirstInitial, e.g. lab03_gibsond

iii. Choose: Finish

3. Add the Dog Class

a. Choose: File, New, Classb. Set the Package to “ver1”c. Set the Name to “Dog”d. Choose: Finishe. Replace everything in the Dog class (except the package statement at the top) with the code below:

public class Dog {private String name;

public Dog(String name) {this.name = name;

}

public String getName() {return name;

}

public String bark() {return name + " is barking";

}

@Overridepublic String toString() {

return "Dog named " + name;}

}

4. Create a WolfDog class in the ver1 package. Replace everything in the WolfDog class (except the package statement at the top) with the code below:

public class WolfDog extends Dog {public WolfDog(String name) {

super(name);}

2

public String howl() {return getName() + " is howling";

}

@Overridepublic String toString() {

return "WolfDog named " + getName();}

}

5. Create a Driver class in the ver1 package. Replace everything in the Driver class except the package statement at the top with the code below:

public class Driver {public static void main(String[] args) {

WolfDog wd = new WolfDog("Juno");System.out.println(wd.toString());System.out.println(wd.bark());System.out.println(wd.howl());

}}

6. Run the code and observe the output.

7. (Read, no action required). Consider the figure below which shows the code for the two classes (the toString methods are not shown). Note the following:

a. The extends keyword is used in Java to denote that a subclass (WolfDog) is extending a superclass (Dog).

b. The WolfDog’s constructor calls the superclass constructor with: super, passing the argument, name.

c. WolfDog inherits the getName method and uses it in the howl method.

3

8. (Read, no action required) Next, we will step through the code using the debugger to see exactly how inheritance works. Remember these things about the debugger:

a. Strangeness can occur when you are using the debugger. If something doesn’t seem to work simply end the debugging session (choose: Run, Terminate or press the red square on the ToolBar) and restart debugging.

b. The two most important tools are: Run, Step Into (F5) to step into a method call and Run, Step Over (F6) to step over a method call.

c. Make sure to stop debugging before editing your source code. Also, you should return to the Java perspective by pressing the Java button in the upper right of your window.

d. As you step through code, you may occasionally get a message in red, “Source not found.” If so, just keep pressing F5 and it will (usually) resume. If not, stop debugging and restart.

9. Do the following:

a. Set a breakpoint on the WolfDog constructor.

b. Choose: Run, Debug (or F11). The code should stop on the call to super. Remember, when this line executes (next steps) it will call the superclass constructor.

c. Choose: Run, Step Into (or F5). Execution should advance to the Dog classes’ constructor.

d. Press F5 three times and execution should finish the Dog constructor and return to the WolfDog constructor.

e. Press F5 and you should be returned to main.

4

f. Press F5 and you should be at the call to toString.

g. Press F5. Verify that you are in the toString method of the WolfDog class.

h. Press F5. Verify that you are in the getName method of the Dog class.

i. Press F5. Verify that you are back in the toString method of the WolfDog class.

j. Press F5 twice and you should be at the call to the bark method.

k. Press F5 and execution should advance to the bark method in the Dog class.

l. Press F5 twice and you should be at the call to howl in main.

m. Press F5. Verify that you are in the howl method of the WolfDog class.n. Press F5. Verify that you are in the getName method of the Dog class.o. Press F5. Verify that you are in the howl method of the WolfDog class.p. Press F5. You should be returned to main.q. End the debugging session.

10. You probably rushed through the last step (Step 9). Repeat Step 9 paying close attention to the path the method calls follow.

11. End the debugging session and return to Java perspective.

5

Stage 2 - Polymorphism & Dynamic Binding

In this stage we will consider the concept of polymorphism.

12. (Read, no action required).

a. When we write a statement like this:

We say that we are using a WolfDog reference type to refer to a WolfDog instance. Other than the term reference type there is nothing new going on here.

b. We can also use a Dog reference type to refer to a WolfDog instance:

Dog d = new WolfDog("Leo");

The reason (rather loosely) that we can do this is that a WolfDog is-a Dog. This is called a polymorphic reference. Anytime we have an is-a (inheritance, superclass-subclass) relationship, we can refer to a subclass instance (WolfDog) with a super-type reference (Dog). Now, why we would want to do this is a bit more challenging to understand. There are tremendous benefits which we will discuss throughout the remainder of the course!

13. Add these lines to the end of main in the Driver class:

Dog d = new WolfDog("Leo");System.out.println(d.toString());System.out.println(d.bark());

14. Run the code and you should see:

...WolfDog named LeoLeo is barking

15. (Read, no action required). Note the following:

a. Both Dog and WolfDog define a toString method.

Dog WolfDog@Overridepublic String toString() {

return "Dog named " + name;}

@Overridepublic String toString() {

return "WolfDog named " + getName();}

b. We say that the WolfDog’s toString method overrides the Dog’s toString method as indicated by the “@Override” annotation in the code. Thus, the Dog’s toString is not

6

inherited, it is overridden. This is important because a subclass may want to inherit some behaviors of the superclass and others it may want to override.

c. The Dog class’s toString also uses the “@Override” annotation. All classes in Java, as shown on the right are subclasses of a class called Object which supplies a toString method (and others). Thus, the Dog class’s toString overrides the Object class’s. We will discuss this more in class.

d. The “@Override” annotation is not required but is recommended.

e. When we executed these lines:

Dog d = new WolfDog("Leo");System.out.println(d.toString());

even though we used a Dog reference, the WolfDog’s toString method was called. This is called dynamic binding. When the toString method is called, the actual toString method that runs is determined by the instance of the object (WolfDog) not the reference type (Dog).

System.out.println(d.toString());

16. Next, we will illustrate this by running the debugger. Do the following:

a. Clear all breakpoints by choosing: Run, Remove all breakpoints.

b. Set a breakpoint in the Driver class on this line:

System.out.println(d.toString());

c. Choose: Run, Debug (or F11) and execution should stop on the line above.

d. Press F5. Verify that execution has advanced to the WolfDog’s toString method.

e. Press F5. Verify that execution has advanced to the Dog’s getName method.

f. Press F5. Verify that execution has returned to the WolfDog’s toString method.

g. End the debugging session by choosing: Run, Terminate (or press the red square on the toolbar).

h. Return to the Java perspective by choosing the “Java” button in the upper-right.

7

17. Do the following:

a. Add this line to the end of main

d.howl();

b. Notice that a compile error is indicated. Hover your mouse over the red X and read the compile error:

The reference type defines what methods can be called on an instance. Here, we are using a Dog reference type to refer to a WolfDog instance and The Dog reference does not define a howl method. This is probably very confusing! We will discuss this more in class.

c. Delete the call to the howl method.

18. In the previous example, the WolfDog class inherited the Dog classes’ bark method. Now, suppose we want the WolfDog class to have a bark method that is different than the Dog classes’ bark method. As we saw earlier with the toString method, Java allows us to override the inherited bark method. Add this code to the WolfDog class:

@Overridepublic String bark() {

return getName() + " is WolfDog BARKING";}

19. Run and verify that the WolfDog’s bark method is being called.

8

Stage 3 - Casting

In this stage we will consider casting an object from one reference type to another. We may use a Dog reference to refer to a WolfDog, but there may be times we want to change the reference to a WolfDog reference so that we can call the howl method.

20. Add these lines to the end of main.

if( d instanceof WolfDog) {WolfDog wd2 = (WolfDog)d;System.out.println(wd2.howl());

}

21. Run and verify that the howl method is called.

22. (Read, no action required). Note the following:

a. Java defines the instanceof operator which returns true if the left operand is an instance of the class specified as the right operand as shown in the figure on the right.

b. Java also allows us to cast an object from one reference type to another as shown in the figure on the right. This cast will only succeed if the cast is valid, i.e. if d really is a WolfDog. The cast here will succeed because we insured that d is a WolfDog with the preceding if statement.

c. A subclass instance is always an instanceof the corresponding superclass. However, the converse is not true. For example, a Dog instance is not a WolfDog. Thus, rather loosely, you can turn a WolfDog into a Dog, but you can’t turn a Dog into a WolfDog. Next, we demonstrate this.

23. Do the following:

a. Add this code to the bottom of main:

Dog d3 = new Dog("Gigi");WolfDog wd3 = (WolfDog)d3;

b. Run and verify that a run-time error occurs (a ClassCastException is thrown).

c. Comment out those the two lines added above.

9

Stage 4 - The ArrayList Class

In this stage we introduce the ArrayList class from the API. An ArrayList is a container for holding objects just as an Array is; however, it has some added features and benefits.

24. Do the following:

a. Create a new package named array_list

b. Create a new class named ArrayListExamples in the array_list package. Check the option to generate a main.

c. Add this import above the class definition and below the package statement.

import java.util.ArrayList;

d. Add this statement inside main.

ArrayList<Integer> ints = new ArrayList<>();

The <…> are something new and indicate that we are using generics which we discuss more fully in another chapter. For now, the “Integer” inside the angle brackets is the generic type and specifies what type of data the ArrayList will hold. The generic type cannot be a primitive type, it must be a full-fledged class. In this case we use the Integer wrapper class to define an ArrayList to hold ints.

e. The ArrayList class has an add method to add objects to the end of the list. Add these statements to the end of main:

ints.add(47);ints.add(91);ints.add(16);

f. The ArrayList class has an get(i) method to obtain a reference to the object in the ith position. The index of elements is similar to an Array, starting at 0, 1, … . Add the statement below to the end of main and run. Verify that “91” is displayed.

System.out.println( ints.get(1));

g. The ArrayList class has a size method that returns the number of objects in the list. Add the statement below and run. Verify that “3” is displayed.

System.out.println( ints.size());

h. You can iterate over an ArrayList using an enhanced for loop just as you would an Array. Add the statements below and run. Verify that all three values are displayed.

for(int i : ints) {System.out.print(i + ", ");

}System.out.println();

10

i. You can also iterate over an ArrayList using an indexed for loop similar to the way you would an Array. Add the statements below and run. Verify that all three values are displayed.

for(int i=0; i<ints.size(); i++) {System.out.print(ints.get(i) + ", ");

}System.out.println();

The highlighted lines above use ArrayList methods.

j. The ArrayList class has a contains(obj) method that returns true if it contains obj. Add the statements below, run, and verify that true and false, respectively, are displayed.

System.out.println(ints.contains(91));System.out.println(ints.contains(33));

k. Add this import below the package statement at the top of the class:

import java.util.Collections;

l. The Collections class defines a number of static methods that operate on lists. Three such methods are shown highlighted below. Add the code below to the end of main, run, and verify the output.

ints.add(8); ints.add(11);Collections.sort(ints);System.out.print("Sorted: ");System.out.println(ints.toString());System.out.println("min=" + Collections.min(ints));System.out.println("max=" + Collections.max(ints));

m. The ArrayList class defines the clear method to remove all the items from the list. Add the code below, run, and verify that the list is empty:

ints.clear();System.out.println("size=" + ints.size() + ", list:" + ints.toString());

11

Stage 5 - An ArrayList of Dogs

In this stage we provide an example of using an ArrayList that contains both Dogs and WolfDogs.

25. Close all open files. Copy ver1 package and give it the new name, array_list2.

26. Open the Driver class (in the array_list2 package) and add this import below the package statement at the top:

import java.util.ArrayList;

27. Replace the code in main in the Driver class (in the array_list2 package) with the code below and run

ArrayList<Dog> dogs = new ArrayList<>();dogs.add(new Dog("Fido"));dogs.add(new Dog("Snoopy"));dogs.add(new Dog("Barley"));System.out.println(dogs);

This illustrates that we can define an ArrayList to hold custom objects (Dog).

28. We can add a WolfDog instance to an ArrayList that holds Dog objects because a WolfDog is-a Dog . Add this code to the end of main

dogs.add(new WolfDog("Juno"));

29. The code below loops through all the dogs and makes each one bark. And, if a dog is WolfDog, then we make it howl also. Add this code to the end of main, run, and verify the output.

System.out.println("\nMake the dogs bark:");for(Dog d : dogs) {

System.out.println(d.bark());if(d instanceof WolfDog) {

WolfDog wd = (WolfDog)d;System.out.println(wd.howl());

}}

30. Do the following:

a. Add this method to the Dog class (in the array_list2 package):

public void setName(String name) {this.name = name;

}

b. Add this method to the end of main in the Dog class (in the array_list2 package):

System.out.println("Change a Dog");System.out.println("Original: " + dogs);Dog d = dogs.get(2);d.setName("Moses"); // Change "Barley" to "Moses"System.out.println("Changed : " + dogs);

12

c. Run and verify that “Barley’s” name was changed to “Moses.”

31. (Read, no action required) This illustrates that an ArrayList doesn’t actually contain Dog objects, but references to Dog objects . Thus, when we obtain a reference, d to the Dog at index 2:

Dog d = dogs.get(2);

and use it to change the name:

d.setName("Moses");

then the ArrayList is “changed” also (of course!). Actually, the ArrayList is not really changed because both the ArrayList and the reference d point to the same Dog object in memory as shown in the figure below. This is exactly the same as when Arrays were discussed. It is a subtle point so we mention this again here.

13

Stage 6 - Checking Account Example

In this stage we consider another example of inheritance and introduce the protected access modifier.

32. Define a CheckingAccount class that allows clients to withdraw and deposit funds. Each withdrawal incurs a $1 transaction fee.

a. Create a new package named account_examples

b. Create a new class named CheckingAccount in the account_examples package.

c. Replace everything in the CheckingAccount class (except the package statement at the top) with the code below:

public class CheckingAccount {protected static final double TRANSACTION_FEE = 1.0;protected double balance;

public CheckingAccount(double balance) {this.balance = balance;

}

public double getBalance() {return balance;

}

public void withdraw(double amount) {balance -= (amount+TRANSACTION_FEE);

}

public void deposit(double amount) {balance += amount;

}

@Overridepublic String toString() {

return "Account balance=" + balance;}

}

d. (Read, no action required). Note above that we defined two instance variables to be protected.

protected static final double TRANSACTION_FEE = 1.0;protected double balance;

protected is an access modifier and simply means that the variables can be accessed in any subclasses (which we haven’t written yet), but not outside the CheckingAccount class. In other words, they can be inherited. Protected is “between” private and public in terms of who can access it.

Also note that the withdraw method above lowers the balance by amount and the TRANSACTION_FEE.

14

e. Create a new class named AccountDriver in the account_examples package. Check the option to generate a main .

f. Add this code inside main.

CheckingAccount a = new CheckingAccount(100.0);a.deposit(200.0);a.withdraw(50.0);System.out.println(a);

g. Run and observe the output.

33. Now, let’s add a GoldCheckingAccount subclass that waives the transaction fee for a withdrawal if the balance is at or above a threshold that is specified when the account is created. Thus, this new class only has to do two things:

Provide a constructor that accepts a threshold (and an initial balance). Override the withdraw method.

Do the following:

a. Create a new class named GoldCheckingAccount in the accounts _examples package.

b. Replace everything in the GoldCheckingAccount class (except the package statement at the top) with the code below:

public class GoldCheckingAccount extends CheckingAccount {double threshold;

public GoldCheckingAccount(double bal, double threshold) {super(bal);this.threshold = threshold;

}

public void withdraw(double amount) {if(balance >= threshold) {

balance -= amount;}else {

balance -= (amount + TRANSACTION_FEE);}

}}

Now do you see why we needed to make balance protected? Because we need to change its value in this subclass’s withdraw method. Of course we could have made balance public but we generally don’t do that. Or, we could have provided a setBalance method in the CheckingAcount class, but we wouldn’t won’t to allow users of our class to change the balance to any value they wanted to! We encapsulate instance variables to protect them by making them private or protected. There are better designs for these two classes. We use this design to keep things as simple as possible to aid in begging to understand inheritance and the protected modifier.

15

c. Add this code to the end of main in the AccountDriver class.

CheckingAccount ca = new GoldCheckingAccount(100.0, 200.0);ca.withdraw(50.0);System.out.println(ca);ca.deposit(300.0);System.out.println(ca);ca.withdraw(50.0);System.out.println(ca);

d. Run and observe the output.

You are done!

16