Lab 7

Polymorphic Classes

Today we are going to implement some related classes that use polymorphism.

You may have noticed that people use multiple units of measurement for different things. For example, most Americans use miles per hour to measure the speed of their cars, while most Europeans use kilometers per hour. Both groups of people use knots for boat speed. Vertical speed is usually measured in meters per second or feet per second.

We do a similar thing with temperature - most Americans describe the outdoors temperature in Fahrenheit, while most Europeans use Celsius. Most scientists and engineers (even the Americans) use Celsius and Kelvin. It is not unusual for a scientific software application to use more than one temperature scale. This can get confusing if temperatures are simply stored in primitive variables. If you have dozens of floats named 'temp', do you think you are likely to remember which units you are using in each instance? It is very easy to forget the units of a data element and use the wrong number in an equation. Errors like that happen frequently, even for professionals. Take a moment to read about a few here.

One way to solve this problem is to use a different class for each unit of measurement. So Fahrenheit and Celsius can be stored as different classes. If the variable 'temp' is of type 'Fahrenheit' then it is a lot more difficult to forget its units than if it is simply a 'float'. Also, the Fahrenheit class can contain methods for easily converting to Celsius or Kelvin if needed. This is what we are going to build today.

Requirements

Everything in this section must be implemented exactly as described. Do not take shortcuts or leave a method out if you think you can solve the problem a different way.

You are going to build four data classes: Temperature, Fahrenheit, Celsius, and Kelvin. The diagram to the right explains their relationship. Note the italics in the classname 'Temperature'. In UML, a classname in italics indicates an abstract class. The three 'concrete' classes each inherit from the abstract class Temperature. The three concrete classes should each be final.

Temperature has a private double data member named 'degrees'. This value holds the recorded temperature, regardless of the units being used. So for "32 degrees Fahrenheit", degrees==32. For "0 degrees Celsius", degrees==0.

Temperature has two accessor methods for manipulating 'degrees'. These set the numeric value in degrees directly, without converting from one scale to another:

Temperature has two constructors:

Temperature has several abstract methods that must be overridden in the child classes:

Temperature has three public methods for converting each unit to its sibling units. These must be overridden in the child classes. Note that it is not possible to prevent all three from being required of each child, so class Celsius will have to have a method named toCelsius(). They should each return an appropriate value:

The three child classes must each implement all of the abstract methods so they can be instantiated.

Each child class should have two public constructors that perform the same as the Temperature constructors. The version with no arguments should always default to the freezing temperature of water. (Hint - let the parent's constructor do the work for you here.)

You should create main() methods in each of the child classes for your own testing. Create tests for each separate method to make sure they are running correctly.

You must use javadoc notation to document all four of these classes. You will be scored on the usefulness and correctness of the documentation. The goal is to create documentation that would allow a new developer to use your temperature libraries without having to look through the code to figure out how it works.

Error-Checking

NOTE - there is no error-checking of user input required in this lab. Normally, a data class like this would want to ensure that values are within a legal range. In the case of temperature, none of them should be allowed below absolute zero (0.0 K). We will revisit that topic in one week, after we have covered Exception handling. For today's lab, we will accept temps below absolute zero.

What to hand in

Four files:

All of the above must be in a directory named 'Lab07'

Delete any *.class or other unneeded files prior to submitting

Delete all of your javadoc files. We will recreate them ourselves.

When you run the submit script, the directory and its contents get zipped and sent to your instructor. Ask for help early if you are having problems running the submit script.

Make sure that you run the correct submit script for your instructor:

Extra Credit

For some extra points, we are going to create a method to test whether any two Temperature objects are equal to each other. We have previously seen that you can do this with String.equals(). In a more general sense, you can do this comparison with Object.equals(Object obj) for any class. But to do this, you need to override Object.equals() for yourself in any custom class you write. Try googling for 'object.equals java example'.

Override the equals(Object obj) method in Temperature. The method must work for any two Temperature objects, even if they are different subclasses. For example:

   Celsius c = new Celsius();
   Fahrenheit f = new Fahrenheit();
   boolean b = c.equals(f);  // <-- True, since both are set to the freezing point

   Celsius c = new Celsius(100);
   Fahrenheit f = new Fahrenheit(100);
   boolean b = c.equals(f);  // <-- False, since 100 degrees C != 100 degrees F
   

Sample test program

We will grade your labs using a test program we write. Our program will import your classes and run their various methods. The test code will look something like the program below.

public class Lab07Test {
    public static void main(String[] args) {
        Fahrenheit f = new Fahrenheit(32);
        System.out.println(f);

        Celsius c = new Celsius(100);
        System.out.println(c);
        System.out.println(c.toKelvin());
        System.out.println(c.get() == c.boilingPoint());

        Kelvin k = new Kelvin(273.15);
        System.out.println(k);
        System.out.println(k.toFahrenheit());
        System.out.println(k.get());
        k.set(0);
        System.out.println(k);
        System.out.println(k.toCelsius());

        Temperature[] tarray = new Temperature[3];
        tarray[0] = new Fahrenheit();
        tarray[1] = new Celsius();
        tarray[2] = new Kelvin();
        for(int i = 0; i < tarray.length; i++){
            System.out.println(tarray[i].toCelsius());
        }

        // Extra Credit
        Celsius cc = new Celsius();
        Fahrenheit ff = new Fahrenheit();
        Kelvin kk = new Kelvin();
        Kelvin kk2 = new Kelvin(100);
        System.out.println(cc.equals(ff));    // True
        System.out.println(cc.equals(kk));    // True
        System.out.println(ff.equals(kk));    // True
        System.out.println(kk.equals(kk2));   // False
    }
}