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. I showed this main method
that can be used to control the GuitarLite object that is provided to you as a
sample musical instrument:
public class GuitarHero {
public static void main(String[] args) {
GuitarLite g = new GuitarLite();
for (;;) {
if (StdDraw.hasNextKeyTyped()) {
char key = Character.toLowerCase(StdDraw.nextKeyTyped());
if (g.hasString(key)) {
g.pluck(key);
} else {
System.out.println("bad key: " + key);
}
}
g.play();
g.tic();
}
}
}
The GuitarLite object has just two guitar strings, so it is a very primitive
instrument. In the assignment, 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:
public class GuitarLite {
public boolean hasString(char string)
public void pluck(char string)
public void play()
public void tic()
}
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 boolean hasString(char string);
public void pluck(char string);
public void play();
public void tic();
}
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.So I went back to our code and changed the line of code that constructed our GuitarLite object to use the interface instead:
Guitar g = new GuitarLite();
Unfortunately, this led to an error:
GuitarHero.java:3: incompatible types
found : GuitarLite
required: Guitar
Guitar g = new GuitarLite();
^
That seems a bit odd, because GuitarLite has the methods mentioned in the
Guitar interface. The explanation is that Java requires classes to explicitly
state what interfaces they implement. So we had to modify the GuitarLite class
to include this notation:
public class GuitarLite implements Guitar {
...
}
With this change, the code compiled and executed properly.I then tried creating an instance of the Guitar interface:
Guitar g = new Guitar();
This produced an error. Interfaces cannot be instantiated because they are
incomplete.I spent the rest of the time discussing details about the homework that are included in the assignment writeup.