CS 112
Fall 2020

Old version

This is the CS 112 site as it appeared on December 31, 2020.

Lab 5: Understanding Classes and Object Oriented Programming

Using folders

We strongly encourage you to create a separate folder for each lab and each problem set, so that you can more easily keep track of your work. For example, you could create a folder called lab2 for your work on this lab, and put all of the files for this lab in that folder. If you are working on a lab machine, you will need to create the folder on the Z: drive, so that it won’t be lost when you log out.

Regrade Requests in Gradescope

This is an online video tutorial you watch at your own liesure which will guide you through the regrade request process in Gradescope.

Working with Roman Numerals

Recall that a long time ago, numbers were represented with Roman Numerals. The value of each Roman Numeral symbol is as follows:

We can represent different numbers by stringing together the symbols. Example:

Given that each symbol has an individual numeric value, we can convert from Roman Numeral to decimal using the following rules:

  1. Reading left to right, symbols with higher values will generally appear first. In this case, the overall value is given by taking the sum of values from left to right.

  2. If a single lower value is placed left of a higher value letter, it is subtracted from the total instead.

  3. Only one single lower value can be subtracted from a higher value.

  4. ”I” can only be subtract from ”V” and ”X”. ”X” can only subtract from ”L” and ”C”, ”C” can only subtract from ”D” and ”M’, and ”V”, ”L”, and ”D” can never subtract.

In this lab we will begin by studying a static class, RomanNumeralStatic which provides several static methods that allow us to perform operations on roman numerals. We refer to this class as a static class because we do not have to create an instance of this class to invoke the methods.

After we have reviewed this static API, we will then create an object based version of this same class so we can compare and contrast the differences between them.

Task 1: Static Roman Numeral Class

Begin by downloading RomanNumeralStatic.java.

Notice that several methods of this class have been written. Further note that one of the methods has been declared to be private and not public. Why do you think this is the case?

  1. The main method contains code which tests the convert method of this class. This method takes in a string representing a Roman Numeral and returns its integer equivalent value.

    The signature of this method is:

    public static int convert(String romanNum)
    

    Following are a few examples of test calls that are provided:

    System.out.println(convert("X"));
    System.out.println(convert("LXXXXIX"));
    System.out.println(convert("CDV"));
    

    Running your program, you should see the following expected output:

    10
    99
    405
    
  2. The main method also contains code to test the add method of this class. This method accepts two Roman Numerals (as String arguments) and returns the sum of the inputs as an integer. The signature of this method is:

    public static int add(String romanNum1, String romanNum2)
    

    Following are a few examples of the test calls that are provided:

    System.out.println(add("X", "X"));
    System.out.println(add("XI", "CDV"));
    System.out.println(add("LXXXXIX", "I"));
    

    Running your program, you should see the following expected output:

    20
    416
    100
    

Task 2: Roman Numeral (Object) Class

Classes made up of only static methods are useful, however, they do not take full advantage of the Object Oriented Paradigm (OOP) that Java is based on. To illustrate the difference, let’s convert our static Roman NumeralStatic API into an object version of this class.

To begin, consider the following:

Thinking through this will help us identify the data we need to store and the methods we need to write.

A Roman Numeral is a string which has a corresponding numeric value. In order to properly represent a Roman Numeral then, each object we create should contain exactly two fields or data members, namely the string that represents the Roman Numeral, and the decimal value associated with it.

Once you have discussed the features of Roman Numberal objects, build your class as follows:

  1. Create a new file called RomanNumeral.java.

  2. Declare the data members of the class as private instance variables.

  3. Write a constructor that takes in a String which is expected to be a Roman Numeral. Initialize the data members of the class based on the string passed.

    To help you complete this method, consider what methods you have available to you in the RomanNumeralStatic class. Can one of those methods help here? If so, how would you call it?

  4. Write a toString method that returns the Roman Numeral representation of the decimal number.

  5. Write an equals method that checks if two RomanNumerals are equivalent.

  6. Write an add method that takes in another RomanNumeral object, and returns an integer that is the sum of the invoked RomanNumeral, and the other RomanNumeral.

  7. Write a main method to test your class. A simple test could look as follows

    RomanNumeral r1 = new RomanNumeral("X");
    RomanNumeral r2 = new RomanNumeral("IX");
    
    System.out.println( r1 );
    System.out.println( r2 );
    

    Expand your main method to test each of the methods you have written. Example:

    System.out.print( "Testing for equality: object are " );
    if ( !r1.equals(r2) )
       System.out.print( "not " );
    System.out.println( "equal!" );
    
    // Invoke the add method to add two roman numeral objects
    // Note the decimal output
    System.out.println( r1.add(ry) );
    

Task 3: Interacting with Objects and Static APIs

Create a new program TestRomanNumerals.java. This program only needs a main method that we are going to use to write and test the code we have written in our two previous classes.

Much of the code that we will write in this method we have already written in the main methods local to each class. However depending on whether or not we are using the static API or creating instance objects, the methods may not be called the same way as they were in the main method of their own class file.

Add code in the main method of this class to accomplish each of the following:

  1. Create two strings of roman numerals. Invoke the add and convert static methods of our static Roman Numeral class, passing the appropriate arguments to add the two roman numeral strings you created and then to see their decimal equivalents.

  2. Create at least two instances of RomanNumeral objects, add them together and print out the results.

  3. Create an array of Roman numeral strings. For each element of the array, print out the corresponding integer value.

  4. Create an array of RomanNumeral objects. For each element of the array, print out the corresponding integer value.

Task 4: Challenge

Create a new program RomanNumeralGame.java that creates a game that allows you to play with roman numerals objects.

The game is of your design (feel free to adlib) but, can be played something like this:

Sample Run #1:

Welcome to my Roman Numeral Game, what is your name? Christine
Hello Christine, would you like to play (yes/no)? no
You are a chicken ...@&FHF!!!!

Sample Run #2:

Welcome to my Roman Numeral Game, what is your name? Sarah
Hello Sarah, would you like to play (yes/no)? maybe
Well, Sarah if you cannot follow simple instructions .... &D*&HS%!!!

Sample Run #3:

Welcome to my Roman Numeral Game, what is your name? Amaeli
Hello Amaeli, would you like to play (yes/no)? Yes
You are brave Amaeli!!!

Continue to enter roman numerals, when are done, enter quit.

Enter a roman numeral and be amazed: X
Oh that was easy! X has a decimal value of 10!

Enter a roman numeral and be amazed XX
ha! Is that supposed to challeng me? XX has a decimal value of 20!

Enter a roman numeral and be amazed: XXVVXCCIIXXV
Thinking, Thinking, Thinking aha 235!!!

Enter a roman numeral and be amazed: 10
DAA!

Enter a roman numeral and be amazed: This!!!game!!!stinks!!
AUTOMATIC DESTRUCTION SEQUENCE ACTIVATED. YOUR DEVICE WILL 
IMPLODE IN TWO MINUTES!!!

Just kidding...

Enter a roman numeral and be amazed: quit
Quitting after only 3 tries! loser!

You guys get the idea... Make it your own, make if fun. No one will see it, but you who write it!

Moving On with OOP

Task 5: Understand and use Inheritance

As we discussed in lecture, a class can extend another class. Consider the Animal and Cat classes in the following Java source code files:

Save these classes, open them in your IDE along with a simple progam testDriver.java that we can use to test the methods of our classes. Review each file and take note of how they are related.

Note: The Cat class will not compile until we first make some changes to it, so don’t try to compile anything yet!

  1. Which class is the superclass? Which is the subclass? What does it mean that the Cat class extends the Animal class?

  2. The Cat class cannot directly access the fields it inherits from Animal. Why not?

  3. The subclass constructor typically calls the superclass constructor to initialize the inherited fields. Write a constructor for the Cat class above. It should take as parameters the cat’s name and a boolean indicating whether it is short-haired, and it should call the superclass constructor to initialize the inherited fields.

    Update the test program to create an instance of class Cat.

    Cat c = new Cat("Kitty", false);
    
  4. To manipulate the inherited fields, the subclass can use the inherited accessor and mutator methods. Write a toString method for the Cat class above. It should return a string consisting of the cat’s name followed by either " (short-haired)" or " (long-haired)".

    Update the test program to test your method.

    System.out.println( c );
    
  5. The subclass can override an inherited method, replacing it with a version that is more appropriate. Write an isSleeping method for the Cat class. It should reflect the fact that cats seem to sleep all of the time!

    Update the test program to test your method.

  6. Let’s say that we now want to define a class called Abyssinian for cats that belong to that particular breed of short-haired cat. Which class should it extend?

  7. Go ahead and create a class named Abyssinian, defining it so that it extends the correct class. It should not have any new fields of its own. However, it should include:

    • a constructor that takes only a name, and that calls the superclass constructor to initialize the inherited fields. When making that call, make sure that it reflects the fact that Abyssinians are short-haired.

    • an isExtroverted() method that overrides the inherited version and replaces it with one that reflects the fact that Abyssinian cats are known to be extroverted.

    Once your class is created, go ahead and test out the methods in your main program.

  8. Another possible class for this hierarchy of animals is the Dog class, which you should examine now, although you don’t need to open it in your IDE. In addition to its inherited fields and methods, it has a boolean field isSmall, and methods isSmall() and bark().

  9. Let’s say that we have created an Abyssinian object and assigned it to the variable a:

    Abyssinian a = new Abyssinian("Abby");
    

    For each of the following method calls:

    • Indicate whether it will compile. Because the variable a is declared to be of type Abyssinian, a method call using a will compile if there is a corresponding method inside Abyssinian objects – either defined in the Abyssinian class itself or inherited from a superclass. A method call will not compile if there is no corresponding method in objects of that class.

    • If the method call will compile, specify which version of the method will be called. In other words, in which class can we find the version of the method that will be called?

    Here are the calls to test:

    1.     a.getNumLegs()

    2.     a.isExtroverted()

    3.     a.isSleeping(12, 30)

    4.     a.isSmall()

    5.     a.toString()

    6.     a.equals(a)

  10. You will notice a static method defined in the Animal class named printAnimalName. How can you call this method in your main program to print out the names of all the animal objects you have created? Note that it is a static method. How does this differ from the other methods of the class?

    Update the test program to test this method.

Task 6: Understand polymorphism

Your work for this task should go on the piece of paper that we give you. Please show your paper to a staff member before you leave the lab.

Thanks to a feature of Java called polymorphism, we can do something like this:

ClassA myObject = new ClassB(...);

Where ClassB extends ClassA, or equivalently, ClassB is a subclass of ClassA. Specifying a more general type for myObject than the actual type of the object can be useful when writing a method that needs to take more than one type of object as a parameter, or when creating an array of objects of different but related types.

For example, if we wanted to have an array containing different types of animal objects, we could define the array as follows:

Animal[] zoo = new Animal[10];

Then, any element of the array could be of type Animal or any subclass of Animal. In other words, this would be allowed:

zoo[0] = new Dog(...);
zoo[1] = new Cat(...);
zoo[2] = new Abyssinian(...);

Consider the following class headers:

public class A extends B {
    ...
}

public class B extends C {
    ...
}

public class C {
    ...
}

public class D extends C {
    ...
}
  1. Draw an inheritance hierarchy for these classes.

  2. Which of these assignments would be allowed, taking into account the rules of polymorphism?

    1.       B myObj = new A();
    2.       B myObj = new C();
    3.       C myObj = new A();
    4.       A myObj = new B();
    5.       D myObj = new B();