CSE143 Notes for Wednesday, 1/15/14

I used most of the lecture time to describe and demonstrate the next programming assignment. I mentioned that it would involve using objects, classes, and interfaces in a more significant way than any of the assignments in cse142.

I first gave an example that involved constructing an array of ojects. I used the example of constructing an array of Point objects. The Point class is part of the java.awt package that is used for graphics. It was also a primary example in the CSE142 class.

I began with this code:

        import java.awt.*;
        
        public class Fri {
            public static void main(String[] args) {
                Point[] points;
            }
        }
I asked what kind of objects this program creates. The answer is that it doesn't create any objects. It defines a variable called points that is of type Point[], which means that it is capable of storing a reference to an array of Point objects. But if we want an actual array or some actual Point objects, we have to explicitely construct them.

So I added code to construct the array:

        Point[] points = new Point[5];
We used jGRASP to see what the program is doing and we saw that it constructs the array, but not any Point objects. When you work with an array of objects, you have to construct not just the array, but also every individual object.

Java initializes the array to the zero-equivalent for the type, which in the case of an array of objects means that Java initializes each array element to null:

           +--+     +---------+---------+---------+---------+---------+
    points | -+-->  |    /    |    /    |    /    |    /    |    /    |
           +--+     +---------+---------+---------+---------+---------+
                        [0]     [1]       [2]       [3]       [4]
It is a common convention to use a slash ("/") to represent null. To fill up this array, we had to write a loop that constructed individual Point objects that were stored in the array:

        for (int i = 0; i < points.length; i++) {
            points[i] = new Point(i, i);
        }
This constructed 5 different Point objects:
                     +-------+ +-------+ +-------+ +-------+ +-------+
                     | x = 0 | | x = 1 | | x = 2 | | x = 3 | | x = 4 |
                     | y = 0 | | y = 1 | | y = 2 | | y = 3 | | y = 4 |
                     +-------+ +-------+ +-------+ +-------+ +-------+
                         ^         ^         ^         ^         ^
                         |         |         |         |         |
           +--+     +----+----+----+----+----+----+----+----+----+----+
    points | -+-->  |    *    |    *    |    *    |    *    |    *    |
           +--+     +---------+---------+---------+---------+---------+
                        [0]       [1]       [2]       [3]       [4]
I then asked how we could print each of the Point objects with a println. Someone mentioned that we could use a foreach loop:

        for (Point p : points) {
            System.out.println(p);
        }
This produced the following output:

        java.awt.Point[x=0,y=0]
        java.awt.Point[x=1,y=1]
        java.awt.Point[x=2,y=2]
        java.awt.Point[x=3,y=3]
        java.awt.Point[x=4,y=4]
Then I started discussing the programming assignment. The assignment involves simulating guitar strings and musical instruments built from those strings. Each instrument will be stored in its own class. You will be provided with a class called GuitarLite that has just two guitar strings and you will define a class called Guitar37 that has 37 strings.

I asked people to consider the issue of making our code more general. If we know that we are going to want to use different objects as our guitar, then how do we structure our code so that we can make minimal changes to the code? We don't want to write all of our code for one kind of guitar and then find that it doesn't work for another kind of guitar.

Someone mentioned that this is a good place to introduce an interface. To do so, we have to think about what are the behaviors we expect of a guitar object. As in the code above, we expect the guitar to have these methods:

In an interface, we want to specify that certain behaviors exist without saying how they are implemented. We want to specify the "what" part without specifying the "how" part. So I went to the GuitarLite class and deleted all of its comments and all of the method bodies. That left me with this:

        public class GuitarLite {
            public void playNote(int pitch)
            public boolean hasString(char string)
            public void pluck(char string)
            public double sample()
            public void tic()
            public int time()
        }
To turn this into an interface, I had to change the name to something new. I decided to call it Guitar. I also had to change the word "class" to "interface". The methods have no curly braces because I deleted those lines of code. I replaced each of those with a semicolon. That left me with:

        public interface Guitar {
            public void playNote(int pitch);
            public boolean hasString(char string);
            public void pluck(char string);
            public double sample();
            public void tic();
            public int time();
        }
This is how you define an interface. In place of the method bodies, we have a semicolon to indicate, "The implementation isn't given." You can think of an interface as being like a hollow radio. It has all of the knobs and buttons that are used to control the radio, but it has none of the "innards" that make it work.

I mentioned that the first part of the assignment involves implement a GuitarString class that simulates a string that can be plucked. The writeup gives you all of the information about how the simlulation works.

You are provided with a GuitarLite object that has two strings. Once you have a working GuitarString class, you can use the GuitarLite object to write some client code. I said it would be instructive for us to write some client code. I began by constructing a GuitarLite object:

        Guitar g = new GuitarLite();
Notice that the variable is of type Guitar using the interface. We only need to specify the specific type of Guitar object when we construct it.

The Guitar interface has methods for playing the guitar. You can play a note using the playNote method. The notes are specified using a chromatic scale where concert-A has the value 12. But you have to give more instructions to the guitar object than just to play the note. Telling it to play the note will pluck an appropriate string. But there are two important methods called sample and tic. The sample method returns the current sound information that we send to the sound card and the tic method advances the simulation. Most often you'll do these two things together, calling sample to play the sound and then calling tic. But they are distinct operations, so it's best to have them in separate methods. For example, you might want to mute the program so you might call tic to move time forward without calling sample.

This simulation involves sending information to the sound card at a rapid rate. We are using a class called StdAudio that has a constant called SAMPLE_RATE. It indicates that we are sampling 44,100 times a second. So that means we have to call sample and tic thousands of times just to get a fraction of a second of audio.

Then I mentioned that these Guitar objects also have a different interface for playing notes. Each Guitar object is allowed to introduce a mapping of characters to notes. For the GuitarLite object, the mapping is from "a" and "c" to concert-A and concert-C. The Guitar37 class has a more complex mapping that allows you to use the computer's keyboard to play it like a piano. I opened up a client program that is called GuitarHero. This is being provided to you. It is set up to play the GuitarLite guitar. I compiled it and ran it and I showed that it was able to play those two notes as I would hit the keys "a" and "c". Then I changed this line of code:

        Guitar g = new GuitarLite();
to be:
        Guitar g = new Guitar37();
and then I was able to play 37 different keys. I played a bad version of "Three Blind Mice." I spent the rest of the time discussing details about the homework that are included in the assignment writeup.


Stuart Reges
Last modified: Wed Jan 15 16:23:41 PST 2014