return to lecture notes index

15-100 Lecture 12 (Wednesday, February 9, 2005)

Announcements

Exam1 is on Monday, bring pen pencil, the exam is going to be on paper and not online.

What's fair game? Basically what's fair game is everything that we've done so far. One thing that you will definitely be asked to do is to code on paper. A couple of things we can ask you to do is to implement a class specification or test driver. The class could contain several types of primitives, and object types. For primitive types, anything that you've learned so far is fair game, but you have only learned one object class so far, the String class. Since you only know one type of Object class it's a good bet that Strings will appear somewhere on the class, although which of the primitive data types that appear is more up in the air. A couple of methods that you could see are constructors, accessors, and mutators. More complex mutators are fair game as well, mutators where we ask you to make a decision based on criteria. For example if something is greater then something else then set a property to a certain value, if not set it to a different value. You may also have to write a class skeleton for another class, and then use the class.

So what are we looking for? We aren't looking for perfect compilable code, we know that you are doing this on paper. We are looking for something that looks like a program, with an argument and code that looks like it could work. We won't take off to many points for syntax errors, but we do want code that looks like java and not English. Specific things that we aren't so worried about are, perfect semicolons, forgetting some squggily brackets or maybe accidentally misspelling BufferedReader. Errors more severely penalized are things about the structure of the program. For example not having a return type, or a constructor in the method that you are writing are both punished more severely.

JavaDoc

As a side note we also took a quick peek at the Java.api. sun.Java.com maintains a web site that holds information on all of the classes that come as part of Java v1.4.2 (which is the version we are using). The JavaDoc is a formal write up of all methods you can call in a class...including a brief description, the parameters it takes, and what type of return should be expected. If you are ever trying to remember a method call or trying to find a method which does something specific, the Java.api is a very good place to look. You can find it at

http://java.sun.com/j2se/1.4.2/docs/api/
However, you should hopefully not need to use it much for this course. It may, however, come in handy for later courses and on those days when you feel especially forgetful.
Virtual Binding Table

When Java is looking for a method to use in a class, it searches in what's called a virtual binding table (vtab). A vtab lists all the methods that are available to a class. The table is formed from the top down where the upper most methods come from the original super class (Object) and as you go down it lists each of the successive subclasses of Object (and super classes of your class) and their methods until it reaches the methods for the class you just created. Therefore the most recent definitions for a method are at the bottom of the vtab. When Java searches for a method it has to search the vtab from the bottom up and it will use the first method it encounters with the correct signature and use that entry. Since all classes are subclasses of Object all classes have the same methods at the top of their vtab as a result of inheriting Object's methods. Thus in the example below when you make a Wallet (and all wallets extend Object)the Wallet automatically gets the object methods. If we don't completely override the Object method (say you use the signature public in compareTo(Wallet w) and we accidentally pass in a cat instead of a wallet then it will skip our compareTo() method and end up using the compareTo(Object o) that is further up on the vtab. Thus this method will compare the cat to a wallet and return false, possibly creating bogus results which the program will continue to run on. This makes code very hard to debug.

Use type casting to stop this error from happening. GIGO (garbage in garbage out) used to be acceptable. You didn't have to produce accurate results if the user didn't give you a valid input. However, now a days, the programmer is expected to make the program usable for any type of input the user my create (even if the program's response is to politely "blow up" in the user's face). GIGO is no longer a valid excuse.

A Review Example
//note this new class is automatically a subclass of Object
class Wallet{
  private double amountInDollars;
  private int numberOfCreditCards;

  public Wallet(double amountInDollars, int numberOfCreditCards){
    this.amountInDollars = amountInDollars;
    this.numberOfCreditCards = numberOfCreditCards;
  }

  public Wallet(double amountInDollars){
    this.amountInDollars = amountInDollars;
    numberOfCreditCards = 0;
  }

  //note the use multiple constructors (all with the same name but different 
  //signatures.  When you do this for any method it is called overloading
  public Wallet(int numberOfCreditCards){
    amountInDollars = 0.0;
    this.numberOfCreditCards = numberOfCreditCards;
  }

  //an example of an accessor...you should have one for each instance variable
  public int getAmountOfCashInDollars(){
    return amountInDollars;
  }

  public int getNumberOfCreditCards(){
    return numberOfCreditCards;
  }

  //a mutator method to allow us to spend cash
  public void spendCash(amountToSpend){
    amountInDollars -= amountToSpend;
  }

  // we are overriding the inherited toString() from the Object class
  public String toString(){
    return "$" + amountInDollars + ", " + numberOfCreditCards + " cards";
  }

  public boolean equals(Object o){
    //a laundry list of the different cases and what it should return in each
    //note that there is a more elegant way of doing this (using ||)...can you think
    //of how to do this (similar to comparison in compareTo().
    if this.amountInDollars != w.amountInDollars 
      return false;
    if this.numberOfCreditCards != w.numberOfCreditCards
      return false;

    return true;
  }

  public int compareTo(Object o){
    Wallet w = (Wallet)o;
		
    //one wallet can affect another wallet, so this method can call private variables
    //of other wallets
    double differenceMoney = this.amountInDollars - w.amountInDollars;

    //note the use of || found above your Enter key 
    if ((differenceMoney < 0 )|| differenceMoney > 0))
      return (int)(differanceMoney*100);

    return (this.numberOfCreditCards - w.numberOfCreditCards);
  }

  //tester
  public static void main (String [] args){
    //Cash and credit cards test
    System.out.println("Cash and cards.  Expect...$10.45, 6 cards");
    Wallet fullWallet= new Wallet(10.45, 6);
    System.out.println(fullWallet);

    //cash only
    System.out.println("Cash only.  Expect...$12.46, 0 cards");
    Wallet cashOnly= new Wallet(12.46);
    System.out.println(cashOnly);

    //credit cards only
    System.out.println("Cards only.  Expect...$0.0, 2 cards");
    Wallet creditOnly= new Wallet(2);
    System.out.println(creditOnly);
  }
}
More on the Equals method and Casting (a review of last Friday's class)

First lets take a look at what we played with last class, operations defined over the set of primitives.

<   - Greater then
>   - Less then 
=   - equals (sets one side equal to the other side)
==  - equals equals (logically equal, returns true if equal false otherwise)
!   - bang (makes a true statement false and a false statement true)

Now lets list a couple quick examples of what these operations return

Example:
  int x = 4;
  int y = 5;
  int z = 4;
  (x > y)   //false                  
  !(x > y)  //true
  (x == z)  //true
  (x != z) //false
  (x=y)   //sets x equal to y, in this case 5
  

One thing that's always important to remember in java is order of operations. The '!' operator will be applied before just about any other operator, so if you had !x > y instead of !(x> y) it would not run, because java can't apply the ! operator to an int. When in doubt just add some parenthesis.

String Equality

So far we've talked about how to test if primitive data types are equal. There is one data type thought that we've talked a lot about that isn't a primitive, but is an Object instead. So if you didn't know anything about java and someone asked you how to compare two strings you might first think that you could just code "string1 == string2" THIS IS WRONG. Since the "==" operator is defined only on the primitive data types, and string is an object what the line :"string1 == string2" is actually evaluating is whether or not the reference variable "string1" is pointing to the same place in memory as the reference variable "string2". Below is an example.

 String s1 = new String("Hello");
 String s2 = new String("Hello");
 
 System.out.println(s1 == s2); //returns false

 //sets the reference variable s1 to point to the same point in memory as
 //the reference variable s2
 s1 = s2;

 System.out.println(s1 == s2); //returns true,since both now point to the
                              //same point in memory 
 

There is a peculiarity about the java compiler though. Even though you may expect that the example below would return false, it returns true because since we pass the Strings as literals, and they are the same string, the java compiler will actually set the two reference variables pointing to the same string, in order to save space.

 String s1 = "Hello";
 String s2 = "Hello";

 System.out.println(s1 == s2); //returns true

Now that we know that we can't use the "==" operator to compare strings we're going to have to find some other way to be able to discover if two strings are equivalent. In order to do this we can use something called the equals method inside the String class.

To use the equals method all you have to do is type "string1.equals(string2)" and it will return true if the two strings are the same and false if they aren't.

Implementing the equals method

Now that we can see if to Strings are equal we want to have a way of seeing if other types of objects are equal to each other. For example if we look back on our textbook class, we may want to know of a way to decide if two textbooks are equal. In order to do this we are going to take advantage of the "equals" method in the Object class.

The class of Objects in java contains several methods that are applied across all the different types of Objects in java. When you write a class, since your class is an Object, it automatically inherits all the methods in Object. You can override these methods though. If you remember back to when you were learning the toString method you may remember that even before you wrote a toString method in your class you could call that method, and it would just return funny input to you. You then would over ride that method by specifying your own toString method for your class that returned the information you wanted in a more meaningful way then the default method. You do exactly the same thing for the equals class. Below are the method definitions in the object class for toString and equals.


class Object{
  ...
  public String toString(){
      ...
  }
  public boolean equals(Object o){
      ...
  }
}

In order to write our equals method we're going to have to write exactly the same method definition that's above. However before we can even do that we need to decide how two textbooks are equals, because we're going to ask a textbook to look at itself an then look at another object of the same type and decide if they are equal. In this case lets decide that two textbooks are equal if they have the same title and the same number of pages.


//We have to have the method declaration copied down so that it is
//exactly the same as the Object method declaration, or it
//won't override the Object method
public boolean equal (Object o) {
    
  //we had to pass the method an object, unfortunately we can't
  //access any of the textbook properties or methods from an object
  //so we have to tell the java  compiler that the type of object thats
  //being passed in is a SimpleTextbook.  We do this by casting o as a
  //SimpleTextbook and then setting SimpleTextbook t equal to it.
  SimpleTextbook t = (SimpleTextbook) o;

    //if the titles of the two textbooks aren't equal returns false and
    //exits the method
    if (!this.title.equals (t.title))
      return false;

    //if the number of pages in the two textbooks aren't equal returns false and
    //exits the method
      if (this.pages != t.pages)
        return false;

    //If we haven't exited the method yet then the titles are equal and
    //and the pages are equal, and thus the textbooks are equal.
    return true;
}
You may of noticed while we were writing the program we did something called "casting". When we cast Object o to being a SimpleTextbook we were assuming that o was a SimpleTextbook, and we told java to work off that assumption as well. If someone passes in an Object that isn't the same as the class being cast java will throw a ClassCastException while it was running.

Below is a quick example of how to call the equals method.


System.out.println(textbook1.equals(textbook2)
At the bottom is the completed SimpleTextbook class.

class SimpleTextbook {
  private String title;
  private int pages;

  public SimpleTextbook (String title, int pages) {
    this.title=title;
    this.pages = pages;
  }

  public String toString () {
    return title + ": " + pages + " pgs.";
  }

  public boolean equal (Object o) {
    SimpleTextbook t = (SimpleTextbook) o;

    if (!this.title.equals (t.title))
      return false;


    if (this.pages != t.pages)
      return false;

    return true;
  }
}