CSE143 Notes for Friday, 9/30/05

I started by pointing out that in the first two assignments we will be reviewing arrays and classes. This should give everyone some time to remember how these things work and to fill in any gaps in your knowledge. It's important to understand typical array processing and the basic use of classes. It's particularly important to understand the IntList class we will be using as a case study.

On Wednesday I had mentioned an idea that I got from Arthur Riel who has written a book on object-oriented design heuristics. Following Riel's advice, I asked everyone in the audience who knows how to use a radio to raise their hand. All the hands went up. Then I asked people to raise their hand if they know how to build a radio from low-level electronic parts. Fewer than 10% of the class raised their hand. This is an important distinction to understand. We all know how to use a radio, but only some of us know how to build one.

In programming, we refer to this as the "client" view and the "implementation" or "implementor" view. This is going to be an important concept to understand as we progress through cse143.

I had also mentioned on Wednesday that the following pithy statement summarizes several of the key concepts of object oriented programming: "An object encapsulates state and behavior." We had discussed what state and behavior are. For a radio, the states included on/off, volume setting, station setting, am/fm and so on. The behavior is that it plays music and that it allows us to change these settings.

In programming, state usually means variables (data) and behavior usually means methods (what are called functions or procedures or subprograms in other languages). I showed a slide of a classic book from 1976 by Klaus Wirth, the inventor of Pascal. The book was titled, "Algorithms + Data Structures = Programs." In the older style, we didn't attempt to directly combine our data and our algorithms (our state and our behavior). With objects, we try to put the two together.

Then I asked people why they thought that cassette tapes so quickly replaced reel-to-reel technology in the last 1960's. People had many answers. I argued that cassettes won because they took an inconvenient collection of stuff (tape, reels, etc) and turned it into an "it", a "thing", an "object". Several students gave this answer as well talking about cassettes as "more convenient", "self contained", "encapsulated". I said that this is a good analogy for understanding what Java classes get us. A Java class is a way of packaging a set of variables along with some code into a self-contained, encapsulated, convenient object.

I then turned to the solution key for the section problems from Tuesday. You were asked to consider two variables that were used to store a list of integers (an array for the integers that had a high capacity and a separate integer variable that kept track of the current number of elements in use in the array). There were five coding problems to manipulate the pair of variables. I pointed out that this combination (the two variables and the five pieces of code) could be turned into a class that we can call IntList.

We then looked at turning the code fragments into methods. These two lines of code append a new value at the end of the list:

        elementData[size] = value;
        size++;
We could just put a method body around these lines of code:

        public static void add(int value) {
            elementData[size] = value;
            size++;
        }
But how does this method get access to the array and size? These can be parameters:

        public static void add(int value, int[] elementData, int size) {
            elementData[size] = value;
            size++;
        }
So you might imagine using the method like this:

        int[] list1 = new int[50];
        int size1 = 0;
        int[] list2 = new int[100];
        int size2 = 0;

        add(3, list1, size1);
        add(18, list2, size2);
The intent here is to set up two lists and to add 3 to the end of the first list and to add 18 to the end of the second list. There are all sorts of problems with this. First of all, the code doesn't work. The "size" variables passed to the method are never changed. That's because the parameter passing mechanism in Java is a value parameter mechanism. Someone mentioned that C++ allows you to add the special character "&" in the header to fix this, but I said that Java doesn't have that ability.

But even worse is the fact that this is so clumsy. If you want two lists, you have to declare four different variables and you have to pass the variables to the add method. Objects provide a better alternative. Someone had suggested using global variables. The problem with global variables is that you'd have just one array and one size. How do you get two lists if you're always manipulating the same global array and size variables? Objects give us the best of these two solutions: a kind of global variable, but one that allows us to create many different lists.

The two variables (the array called elementData and the variable called size) become the data fields or instance variables of the class. By defining them at the outer level of the class:

        public class IntList {
            int[] elementData = new int[100];
            int size = 0;

            ...
        }
These variables become part of what we call the "state" of the object. Once the object is constructed, these variables have a permanence to them that local variables don't have. They stay around indefinitely. These variables are like the tape and reels inside the cassette. They are the "innards" that allow this object to do what it has to do.

So that means that we can rewrite our client code as follows:

        int[] list1 = new Intlist(50);
        int[] list2 = new IntList(100);

        list1.add(3);
        list2.add(18);
We only need a single variable for each list because the state variables are encapsulated into a single object. In other words, each of these lists has its own array and its own size variable. We also can have much simpler parameter passing. Now we just tell one list or the other to add a value to the end of the list. The add method itself is written as follows:

        public void add(int value) {
            this.elementData[this.size] = value;
            this.size++;
        }
There are several things to notice about this method. First, it doesn't have the keyword "static". We won't be using static very much any more now that we are defining objects. In this case, we want to write a method that will manipulate the internal state of the object (the array and size). To do so, we have to have a dynamic rather than a static method. Static methods are associated with the class (one method for the whole class). Dynamic methods (which are the default in Java) are associated with the individual instances of the class.

Also notice the use of the keyword "this". When using the object notation, we call the method in a different way. We say:

        list1.add(3);
or we say:
        list2.add(18);
In the first case, we talk to list1. In the second case, we talk to list2. We are always talking to a particular object. That object is known as the "implicit parameter" and we use the keyword "this" to refer to it. So when the first call on add is executed, we are talking to list1, which means that "this" refers to "list1". So when we say "this.size" we are referring to "list1.size". In the second case, we are talking to list2, so "this" refers to "list2" and references like "this.size" refer to "list2.size".

The use of "this" is optional in Java classes. We could, for example, write the add method as follows:

        public void add(int value) {
            elementData[size] = value;
            size++;
        }
In this case, when Java comes across the references to "elementData" and "size", it looks to see if there are any local variables of that name. There aren't any local variables of that name, so it looks to see if there are any data fields of that name. There are, so it treats these references as if they were "this.elementData" and "this.size". I find that the "this" notation is useful when people are learning about objects, but most people get lazy quickly and tend to use it only when they have to.

I mentioned that the other four code fragments had been turned into methods as well in turning this into a class. Handout #3 has the complete class definition.

We then talked about the fact that the variables should not be initialized in this clumsy way:

        public class IntList {
            int[] elementData = new int[100];
            int size = 0;
It is considered bad style to initialize data fields in the declaration. We usually have just the declaration in the class:

        public class IntList {
            int[] elementData;
            int size;

            ...
        }
And we move the actual initialization into a separate method called a constructor:

    public IntList(int capacity) {
        this.elementData = new int[capacity];
        this.size = 0;
    }
Constructors are special methods that are only called in conjunction with "new". They have the same name as the class and have no return type. Their only purpose is to initialize the data fields of the object.

At the very end of the lecture, I mentioned the idea of encapsulating the object. As it stands, someone on the outside would be able to reach into this object and directly manipulate the array and its size. We can prevent this by declaring the data fields to be private:

        public class IntList {
            private int[] elementData;
            private int size;

            ...
        }
If you need to review arrays or classes, you might want to use the chapters on the CSE142 site:
Stuart Reges
Last modified: Sun Oct 9 13:15:02 PDT 2005