Radford University ITEC

ITEC120-ibarland (incl. office hrs)infolectureslabshws

references as values

int myFavoriteInt   = 5;
int yourFavoriteInt = 3*myFavoriteInt;
myFavoriteInt = 7;
Math.Sqrt(yourFavoriteInt);  // Doesn't change the value of yourFavoriteInt.

/* ...more code which never assigns to "yourFavoriteInt"... */

(yourFavoriteInt == 15)  // Will be true.
You can't change the value of n without having it be on the left hand side of the assignment statement. 1 But compare with:
Dog myFavoriteDog   = new Dog( "fido", 3 );
Dog yourFavoriteDog = myFavoriteDog;

/* ...code which never *assigns* to "yourFavoriteDog", 
      and moreover, never even *mentions* it. */

(myFavoriteDog == yourFavoriteDog)    // Will be true.
(yourFavoriteDog.getAge() == 3)       // Might be false?!?
But, what if that intervening code called yourFavoriteDog.setAge()? Then the age of my favorite dog has changed, even though I never even mentioned myFavoriteDog.

(Pictures on board; see also pp.116-8 in the book, “aliasing”.)

There is still a parallel between the two examples above (m,n vs. my,yourFavoriteDog): The value of myFavoriteDog never changed -- it is still a reference to the same dog. Agreed, the state of the Dog has been modified over time, but it's still the same Dog!.

So: you can't change the value of a variable without assigning to it, whether its a primitive type or an object reference. But, the contents of an object might change, even if your variable which references the object isn't explicitly mentioned in the code.

Multiple References can be intended

Note that the code for the Library class might contain a reference to a book, even if that book has been checked out: they might have a field booksOnShelf, as well as a field booksCheckedOut.

Similarly, there might be only one Student object corresponding to you, but several different places of code might all refer to you. Your Dorm has a list of students which will include a reference to you; the Registrar has a list of all-students, as well as a list of honor-students and a list of students-with-outstanding-fines; you might be on all of these lists, or perhaps only one!

It turns out, that the library-program doesn't use the objects made by the Registrar's computer; I had to fill out a separate card (and re-list my name, phone-number, etc.). If I ever change my phone number, I'll need to tell both the library and the registrar to change it. This is poor data representation; there should be a single instance of (the object representing) me which all the different functions access.

On the other hand, for social reasons, perhaps it's good not to have a single central object with all the information:

The drawback of a lack of centralized database is that a criminal might be wanted by the police, yet still able to apply for parking permits and credit cards because there is no central database. (Or even, I might be wanted by the FBI, and yet when I'm pulled over by a local sherrif for speeding, the FBI information may not be shared with other systems, and I go free.) The costs and benefits of implementing the data the “correct” way (from a programmer's point of view) must be weighed with social realities.

== vs. .equals()

(m == n)
(someChar == 'a')
(myFavoriteDog == yourFavoriteDog)
But what about:
myFavoriteDog   = new Dog( "fifi", 3);
yourFavoriteDog = new Dog( "fifi", 3);

(myFavoriteDog == yourFavoriteDog)
Are these two the same dog, or are they two different dogs which each happen to have the same name and age? The latter! Every time you say new, a new object is created, distinct from all other objects. Thus Java is able to handle both situations: when our favorite dogs are the same dog, vs. when then are simply two dogs which look alike but are different.

Often, when our java objects correspond to physical objects in the world, we have a distinction between two objects which look alike but are different. The mantra “one new, one instance” gets you through that. Sometimes, though, it's not what we want. Consider:

 import java.math.BigInteger;
 BigInteger num1 = new BigInteger("7");
 BigInteger num2 = new BigInteger("7");
 (num1.add(num2)).toString();  // Returns "14", as expected.
 (num1 == num2)    // False?!

 num1.toString()   // "7"
 num2.toString()   // "7"
 // Can we compare BigIntegers by sneakily/hackily comparing their String versions?
 (num1.toString() == num2.toString())   // Nope, still false!
This problem never arose with primitive int, because unlike Dogs, there was no notion of “two sevens which look alike, but are really different”.

Just as the authors of the BigInteger class provided useful methods like add, it turns out they also provide a handy method so that you can compare one BigInteger to another:

num1.equals(num2)    // Aha, finally, true!
This method name equals(·) is actually used in all classes (just as toString() is in all classes):
myFavoriteDog.equals(yourFavoriteDog)  // true.
myFavoriteDog.equals( new Dog("fido", 4) ) // False, even though they look alike?!

  1. Use == to compare primitive types.
  2. Use == to check if two objects are identically the same object.
    (More precisely: use == to see if two object-references are both referring to the same Object.)
  3. Use equals(·) to check if two objects are conceptually equal, whether or not they are identically equal. For example, two Date objects should be compared using .equals(·), since there might be multiple instances which really do refer to the same date.
    class MyDate {  /* Gregorian calendar */
      int year;     /* AD */
      String month; /* 3-letter abbrev: e.g. "Mar", "Oct".
      int day;      /* The day-of-month, always in 1..31. */
    
      // ...other methods omitted...
    
      /** @param otherDate Another MyDate instance, to compare with this one.
       *  @return true if these two Dates look alike.
       */
      public boolean equals(MyDate otherDate) {
        // WARNING: after we talk about inheritance,
        // we'll see that this parameter should be of type Object, not MyDate.
    
        return (   (this.getYear() == otherDate.getYear())          // Same year?
                && (this.getMonth()).equals( otherDate.getMonth() ) // Same month?
                && (this.getDay() == otherDate.getDay()) // Same day-of-month?
                   );
        }
    
      }
    
  4. If you don't write a public boolean equals(Dog) method for your own classes, then Java automatically provides one for you: however, what Java writes for you is a method which simply declares two things equal iff they are ==:
    class Dog {
      // ...other fields and methods omitted...
    
      /** @param otherDog Another Dog instance, to compare with this one.
       *  @return true if these two Dogs are identically the same
       *      (This is the default version of equals which Java provides,
       *       if we don't write our own version for Dogs.)
       */
      public boolean equals(Dog otherDog) {
        // WARNING: after we talk about inheritance,
        // we'll see that this parameter should be of type Object, not Dog.
    
        return (this == otherDog);
        }
      }
    
Be aware: When comparing Strings, you almost always want to use equals rather than ==.

Notes on defining equals:

While these are all obviously desirable traits, be aware that you may (wittingly or not) violate them, if you write your own equals(·). This danger is much more plausible once we've talked about polymorphism and inheritance.

passing and returning objects

An object can be passed in, just like any other parameter.

class Kennel {
  Dog dog1;
  Dog dog2;

  /**
   * @param d The dog to replace the old dog1 with.
   */
  void setDog1( Dog d ) {
    dog1 = d;
    }

  /**
   * @return The first dog in this kennel.
   */
  Dog getDog1() {
    return dog1;
    }  
  }

Note that while the parameter itself cannot be changed, the fields of that parameter can be.

class Dog {
  // ...fields and other methods omitted...

 /** Have this and otherDog exchange their names.
   * A very useful method for sitcoms and Disney movies.
   * @param otherDog the dog who we'll swap names with.
   */
  void swapName(Dog otherDog) {
    // ... WRITE THIS METHOD ...
    }
  }
By the way, it's poor practice to assign to your parameters. Note that assigning to the parameters doesn't modify what value was passed in:
void addSevenTo( int n ) { n += 7; }

int addSevenTo_v2( int n ) { n += 7; return n; }  // Still bad practice.


int zz = 5;
addSevenTo(zz);    // zz is still 5.
addSevenTo(zz+3);  // zz+3 is still 8.
addSevenTo(19);    // 19 is still 19.

We can return objects as well:

class Dog {

  /**
   * @return a new dog which looks just like this one.
   */
  public Dog clone() {
    // ... WRITE THIS METHOD ...
    }

  /**
   * @param partner the dog who is breeding with this.
   * @return a new puppy dog, whose age is 0 and whose name is a blend 
   *    of this's and partner's names.
   *    For example:
   *    (new Dog("rex",4)).breed( new Dog("fifi", 6) ) = new Dog( "refi", 0 );
   */
  public Dog breed( Dog partner ) {
    // ... WRITE THIS METHOD ...
    }

  }

Btw, note that some people feel there are good reasons to avoid implementing clone, due to the danger of an object and its clone sharing state. For instance, if we clone a kennel, will the clone contain identically the same Dogs as the original? Or, will it contain clones of those Dogs?


1By the way, notice that Java variables work differently than spreadsheet cells: in a spreadsheet, if Y1 = 3 * M1, then any changes to M1 do cause Y1 to change. Java variables are not so sophisticated. But there are some programming languages for the web which do use such “reactive” variables.      back

ITEC120-ibarland (incl. office hrs)infolectureslabshws


©2006, Ian Barland, Radford University
Last modified 2006.Nov.02 (Thu)
Please mail any suggestions
(incl. typos, broken links)
to ibarlandradford.edu
Powered by PLT Scheme