CS 112
Spring 2022

Old version

This is the CS 112 site as it appeared on May 12, 2022.

Lab 3: Static vs. object classes; writing custom/blueprint classes

Creating the necessary folder

Make sure to create a subfolder called lab3 within your cs112 folder, and put all of the files for this lab in that folder.

A couple of reminders

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. Here are some examples:

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 the individual symbol values from left to right.

  2. If a single lower-value symbol is placed directly to the left of a higher-value symbol, the lower value is subtracted from the total instead, according to the following rules:

    • I can only subtract from V and X.
    • X can only subtract from L and C.
    • C can only subtract from D and M.
    • V, L, and D can never subtract.

In this lab we will begin by studying a static class called RomanNumeralStatic that provides several static methods for performing operations on Roman numerals. We refer to this class as a static class because we do not have to create an instance/object of this class to invoke its methods.

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

Task 1: Reviewing a static class for Roman numerals

Begin by downloading this file:
RomanNumeralStatic.java

Make sure to put the file in your lab3 folder. If your browser doesn’t allow you to specify where a file should be saved, try right-clicking on its link above and choosing Save as... or Save link as..., which should produce a dialog box that allows you to choose the correct folder for the file.

In VS Code, open your lab3 folder using File->Open Folder, and then click on the name of the file in the Explorer Pane to open an editor window for it.

Notice that several methods of the 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.

    The signature of this method is:

    public static int convert(String romanNum)
    

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

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

    Running the 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 their values as an integer. The signature of this method is:

    public static int add(String romanNum1, String romanNum2)
    

    Here 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: Implementing a class for RomanNumeral objects

Classes made up of only static methods are useful, but they don’t take full advantage of the Object Oriented Programming (OOP) paradigm that Java is based on. To illustrate the difference, let’s convert our static RomanNumeralStatic class into an object-oriented 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: the string that represents the Roman numeral, and the decimal value associated with it.

In addition, the class will need to include a number of non-static or instance methods that belong to objects of the class. We make these methods non-static so that they will have access to the fields of the RomanNumeral object on which they are invoked.

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

  1. Select File->New File, which will open up an empty editor window. Then select File->Save, and give the new file the name RomanNumeral.java.

  2. Write the class header, and then declare the data members of the class as private fields (aka instance variables).

  3. Write a constructor that takes in a String that you can assume is a valid Roman numeral. Initialize the fields based on the String that is passed in.

    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 string that represents the RomanNumeral object. We discussed this type of method in lecture.

  5. Write an equals method that checks if two RomanNumeral objects are equivalent. We discussed this type of method in lecture.

  6. Write an add method that takes in another RomanNumeral object and returns an integer that is the sum of the called RomanNumeral (i.e., the object on which the method is invoked) and the other RomanNumeral that is passed in.

  7. Write a main method to test your class. Here’s one simple test that you could include:

    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. For example:

    System.out.print( "Testing for equality: the objects are " );
    if ( !r1.equals(r2) )
       System.out.print( "not " );
    System.out.println( "equal!" );
    
    // Invoke the add method to add two RomanNumeral objects.
    // Note that the output should be a decimal integer.
    System.out.println( r1.add(r2) );
    

Task 3: Creating a client program for your two classes (optional)

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 String objects for Roman numerals. Invoke the static convert and add methods of our RomanNumeralStatic class, passing the appropriate arguments to see the decimal equivalents of the two Roman numeral strings that you created and then to add them together.

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

  3. Create an array of String objects that represent Roman numerals. For each element of the array, compute and print out the corresponding integer value.

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

Task 4: Writing another custom/blueprint class

Our RomanNumeral class allowed us to create our own custom data type – i.e., our own type of object. This type of class is sometimes referred to as a blueprint class to distinguish it from a class that simply serves as a container for a program.

In this task, we will implement a blueprint class for Point objects, each of which will represent a single point with integer coordinates (x, y).

  1. Begin by downloading Point.java

    Make sure to put the file in your lab3 folder.

    In VS Code, open your lab3 folder as needed using File->Open Folder, and then click on the name of the file in the Explorer Pane to open an editor window for it.

    Read through the file to understand what is already provided.

  2. Use File->New File to create a separate test program that you can use to create and modify Point objects. Give your test class an appropriate name, and don’t forget that the name of the class must match the name of the file.

    At the start of your test program’s main method, add a line in which you fill in the blanks below to create a Point object with an x-coordinate of -2 and a y-coordinate of 5.

    ______ p1 = ________________;
    
  3. The current version of the Point class does not employ appropriate encapsulation. To see this, add the following statements to your test program’s main method after you create your object p1:

    System.out.println("x = " + p1.x);
    System.out.println("y = " + p1.y);
    

    Running your program now should produce:

    x = -2
    y = 5
    

    In a properly encapsulated class, code that is outside of a class should not be able to directly access its fields. Make the necessary changes to prevent this, and then try to rerun your test program.

    If you’ve been successful, your code should no longer compile.

  4. To provide indirect access to the fields, you must add a public accessor method and a public mutator method for each field.

    Note that each coordinate can take on any integer value, which means that the mutators don’t need to perform any error-checking, and that we also don’t need to add any error-checking to the constructor. However, keep in mind that ordinarily you do need error-checking in your mutators and constructors.

  5. Add the following statement to your main method. What do you expect to see when you execute your program?

    System.out.println(p1);
    

    To see something more descriptive, uncomment the toString() method in the Point class, and then save and rerun your program.

    Note that we don’t need to actually call the toString() method. Rather, it is called on our behalf whenever we print an object of the class.

  6. Add an accessor method called quadrant that returns the number of the quadrant (if any) in which the called Point object (this) falls. The method should return:

    • 1 if both coordinates are positive
    • 2 if the x coordinate is negative and the y coordinate is positive
    • 3 if both coordinates are negative
    • 4 if the x coordinate is positive and the y coordinate is negative
    • 0 if the point lies on the x-axis or y-axis

    Example 1:

    Point p1 = new Point(3, 4);
    System.out.println(p1.quadrant());
    

    Running this code, you should expect to see:

    1
    

    Example 2:

    Point p3 = new Point(-3, -4);
    System.out.println(p3.quadrant());
    

    You should expect to see:

    3
    

    Example 3:

    Point p5 = new Point(0, -4);
    System.out.println(p5.quadrant());
    

    You should expect to see:

    0
    
  7. Although most of the methods in a blueprint class are non-static (meaning that we can think of them as being inside each object of the class), blueprint classes can also include static methods (which belong to the class as a whole).

    A non-static method is preferred if the method needs to access to the fields of a particular called object. However, if a method does not need a called object – i.e., if it makes more sense to pass in all of the information that it needs as parameters – then we typically make it static.

    Write a static method closestToOrigin() that takes two Point objects and returns the Point that is closest to the origin.

    Hint: Make use of the distanceFromOrigin() method that every Point has!

    Because the method is static, we must prepend the class name to call it from outside the class:

    Point p1 = new Point(3, 4);
    Point p2 = new Point(2, -1);
    System.out.println(Point.closestToOrigin(p1, p2));
    

    You should expect to see:

    (2, -1)
    

Task 5: An optional 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 it 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 it fun. No one will see it but you!