CLASSES IN JAVA

constructor inheritance casting polymorphism abstract interface Exercises

Making Your Own Class

NOTE: The code samples you see here may not match what your group created in our class exercise. Each group did things in a slightly different way.

Suppose you are going to make an applet for playing chess. To make use of the object oriented paradigm, you need to ask yourself: what are the objects in this problem? The chess board and the chess pieces come immediately to mind. Often, we think of the most physical parts of the problem first. Let's start with the chess pieces. If you go to the API Specification, you will not find a class named ChessPiece. It offers lots of other handy things like String, Label, Button, and Polygon-- but no ChessPiece. So, you can just make your own class and name it ChessPiece. Once you describe what it means to be a ChessPiece, you can create objects of type ChessPiece. Java is an infinitely expandable language; if a method or a class doesn't already exist, you just make it yourself.

To get started on making a class, you need to determine what the fields and methods of the class will be. What does a chess piece have? This question helps you figure out what the fields are. A chess piece has a name, like "pawn" or "king." So, one of the fields could be a string variable for holding the piece's name. A chess piece also has a location on the board. We can make two integer fields to hold that location. We'll think of more fields as we get further into the developing the program. We might also change our mind several times.

What can a chess piece do? This question leads you to the methods of the class. A chess piece can move; we will want to have a method that allows that to happen. We will need to provide a signature for the method when we declare it, so we must figure out what arguments it needs and what the return type will be. To move a piece, someone would have to tell us where it is moving to, so the method should require two integer arguments describing the new location. Once the piece is moved, there is probably not any information to send back to the calling program, so the return type can be void. Come to think of it, though, first we have to be sure that the move is allowed under the rules of chess. It makes sense to have a method which will check for a legal move and return a boolean indicating if the piece can be moved to that location or not.

Defining your own class simply consists of identifying fields and methods. Just open up a new file and declare the name of the class. Then list the fields and methods:

keyhole logo
class
public class ChessPiece{

String pieceName;

int row;

int column;


public void moveTo(int newRow, int newColumn){

}


public boolean moveIsLegal(int newRow, int newColumn){

}

}

and save it in a file called "ChessPiece.java." Of course, you have already been making your own classes with applets and applications. All Java code is contained inside some sort of class declaration. But you might not have realized that you can make your own type of object, something that can be used as a variable type just like String or Color.

Once you have identified the fields and methods of your class, you should think about how this type of object will be constructed. The class is only a description of what it means to be a chess piece. To get an actual object of type ChessPiece, you need to call a constructor to create an instance of the class. Every class in Java must have a class constructor. In fact, a default constructor is created for you if you don't specify one of your own. The default constructor has no arguments. It will allow you to say something like this:

ChessPiece myPiece = new ChessPiece();

and thereby create an instance of class ChessPiece. If you think about the actual game, however, you might decide that it's meaningless to create a chess piece without establishing its location and its name right from the beginning. In that case, you should declare a class constructor that requires a string and two integers as arguments:

public class ChessPiece{

String pieceName;

int row;

int column;


//class constructor

public ChessPiece(String s, int r, int c){

pieceName = s;

row = r;

column = c;

}


public void moveTo(int newRow, int newColumn){

}


public boolean moveIsLegal(int newRow, int newColumn){

}

}

You can see that writing a constructor looks a lot like writing a method. The key difference is that the constructor is written without a return type. Also, the name of the constructor must be the same as the name of the class itself. (Those two features allow the compiler to figure out that it is a constructor.)

The body of this constructor may look a little odd to you. Does it seem pointless to say that row = r? In fact it's very important. The variable r is sent in as an argument of the constructor and the scope of r lasts only until the constructor's closing brace. After the pawn object is made, the memory for r will be destroyed. The variable row, on the other hand, is one of the fields of the class and it stays alive as long as the chess piece itself is alive. So, by using the assignment operator, we take the value sent in as an argument and store it permanently in the field row. By the way, the names for those arguments--"r" and "c" were completely arbitrary. I could have called them something else and used them in the same way in my definition of the class constructor.

Once you create your own constructor, the default constructor no longer exists. It won't be possible to say new ChessPiece() with no arguments. Now, the only way to create an instance of class ChessPiece would be to say:

ChessPiece myPiece = new ChessPiece("rook",0,7);

Notice that in this example the value of String s is "rook", while int r is 0 and int c is 7. The person calling the class constructor just sends in the actual values desired and is not aware of what I named those arguments in my constructor definition.

Read more about constructors: David Eck's textbook.
Sun tutorial
Bruce Eckel's textbook

Now our ChessPiece.java file is following a general syntax of:

public class ClassName{

fields of the class

class constructor(s)

methods of the class

}

Use this structure for all of the classes that you create. In the fields section, be careful that you don't try to make any method calls. The only thing that belongs up there is declaration of variables. You can initialize the variables at the time of declaration, but that would need to be all in one line of code like this:

int numDice = 2;

You would not be allowed to do this in the fields section:

int numDice;

numDice = 2;

In Java, all code must be contained inside of a method definition, except for field declarations and constructor declarations.

Once you create a class, you can make use of it just like any class you find in the API Specification. Suppose you decide to make another class called ChessSquare. You might want to have a boolean field to indicate if that square is occupied or not, and then another field to represent the particular chess piece that is currently sitting in that square:

public class ChessSquare{

boolean isOccupied = false;

ChessPiece currentPiece;

}


See--ChessPiece is just as legitimate for a variable type as boolean is. And now that ChessSquare has been defined, that is usable as a variable type also. In fact, you can even refer to class ChessSquare inside of the definition of the class itself! You might decide that you want to keep track of all of the squares surrounding a chess square. Then the beginning of your class might look like this:

public class ChessSquare{

boolean isOccupied = false;

ChessPiece currentPiece;

ChessSquare[] neighbor;

...

I was surprised to learn that you could do that, because it seems like you're using the word to define the word. It was very useful for me when I wrote the Game of Life applet, because I created a class called Cell, and each cell has a set of neighbors that I needed to keep track of. The neighbors are also cells. It took me a lot of work back and forth to figure out how to set up that class. The same will be true of class ChessPiece and class ChessSquare.

We've thought of a few fields that a chess square might have, but we're certainly not done. We might decide later that we also need to keep track of the square's row and column, as we did with class ChessPiece. Or maybe we'll even change our minds and decide that the square will hold row and column, while the chess piece doesn't need to. That would be something to stop and debate. Does it make more sense for the square to know where it is, or for the piece to know where it is? Or should both of them know? You can do it either way, but one way might make for a smoother design. That's hard to know in advance, and frequently you change your mind in the middle of designing something.

We might also want to give the chess square a field of type Color, so we know if it's black or white. I'm sure you can think of other fields that might be important as we further develop the chess applet.

In the code for a class ChessBoard, we might use both class ChessPiece and class ChessSquare as variable types:

public class ChessBoard {

ChessPiece[] whiteTeam = new ChessPiece[16];

ChessPiece[] blackTeam = new ChessPiece[16];

ChessSquare[][] boardSquare = new ChessSquare[8][8];

}


You don't even need to import these new classes in order to use them in an applet. As long as ChessPiece.java, ChessSquare.java and ChessBoard.java are stored in the same directory as ChessApplet.java, the compiler will find them all.




Inheritance

So far, we just have a skeleton of the ChessPiece class. We haven't actually spelled out how to move the piece; we've merely stated that a moveTo() method needs to exist. Of course, different types of chess pieces move in different ways. The fact that there are different types of chess pieces suggests that we might want to establish subclasses of class ChessPiece. We could have class Pawn, class Bishop, etc.. Then each subclass could contain methods tailored for that type of piece. To set up a subclass requires the "extends" keyword:

keyhole logo
extends
public class Pawn extends ChessPiece{
}

This is put into a separate file which is saved as "Pawn.java." The subclass/superclass relationship can be described this way: a Pawn is a ChessPiece. You can see this relationship in the existing classes listed in the API Specification. A Label is a Component. An Applet is a Panel, which is a Container, which is a Component. And everything is an Object! When you are considering establishing a subclass, just think in terms of the "is a" phrase. (Note: in some readings, the superclass is referred to as the "base" class, while the subclass is called a "derived" class.)

To make class Pawn a meaningful subclass, we need to identify the fields and methods that are unique to a pawn. For one thing, in the game of chess it makes a difference if the pawn is being moved for the first time or not. We would need a boolean to signify if the pawn has already been moved or not. None of the other chess pieces need that boolean, so it doesn't belong as one of the fields of class ChessPiece. Now the subclass would look like this:

public class Pawn extends ChessPiece{

boolean isFirstMove = true;

}

Notice that when the boolean field is declared here, it is also initialized. This makes sense because when a pawn is first created, it hasn't moved yet. The value of that field can be changed after the pawn moves, but at the time of creation it is definitely true. If you know for sure that all instances of a class will start out with the same value for a field, go ahead and initialize the field with the declaration statement. We didn't initialize the fields row and column in class ChessPiece because we each instance of the class will have a unique location when it is created.

For the fields that can't be initialized in advance, be sure that the constructor sets their values. To do that, our Pawn constructor will need two arguments:

public class Pawn extends ChessPiece{

boolean firstMove = true;


//class constructor

public Pawn(int originalRow, int originalColumn){

row = originalRow;

column = originalColumn;

pieceName = "pawn";

}

}

With this setup, each instance of class Pawn will have all of its fields initialized by the time it is created. If someone calls the constructor by typing new Pawn(1,5), the value of row will be set to 1 and column will be 5, while firstMove will equal true and pieceName will equal "pawn." For any class that you make, you don't want to leave your fields hanging around uninitialized. That could lead to some unexpected behavior in your program.

In our Pawn constructor, the value of pieceName doesn't need to be sent in as an argument, since we already know that we are creating a pawn. You might wonder why the assignment of pieceName appears inside the constructor body instead of up in the fields section with boolean firstMove. This is because pieceName is already declared in class ChessPiece, and thereby automatically inherited by class Pawn. It doesn't need to be declared; it just needs to be assigned a value. That assignment must take place inside the constructor body, because assignment statements can't be placed up in the field declaration section unless they are part of a declaration.

Now let me show you a nice feature of the inheritance built into Java. Remember above that we created an array of chess pieces to constitute a player's team. When we go to fill that array, we can call the constructor from a subclass:

public class ChessBoard {

ChessPiece[] whiteTeam = new ChessPiece[16];

...

public void setUpTeams(){

whiteTeam[0] = new Pawn(0,1);

whiteTeam[1] = new Bishop(2,0);

whiteTeam[2] = new Rook(0,0);

...

}

}

Even though the array was to declared to be a group of ChessPiece objects, we can fill it with subclass objects. Why? Because a Pawn is a ChessPiece!

Our Pawn subclass now has a field and a class constructor. It also needs some methods. Determining the legality of moving a chess piece depends on what kind of piece it is. All chess pieces should have a method called moveIsLegal(), but that method needs to be implemented differently in each subclass. So class Pawn will override the method from class ChessPiece, by using the same signature as in class ChessPiece.

public class Pawn extends ChessPiece{

boolean firstMove = true;


public Pawn(int originalRow, int originalColumn){

row = originalRow;

column = originalColumn;

pieceName = "pawn";

}


public boolean moveIsLegal(int newRow, int newColumn){

if (firstMove==true){

//for first move, allow two steps forward

if (newRow == row + 2 && newColumn == column){

firstMove = false;

return true;

}else{

return false;

}

}else{

//after first move, only one step forward

if (newRow == row + 1 && newColumn == column){

return true;

}else{

return false;

}

}

}

}

The moveIsLegal() method is definitely not finished! You can imagine that there will be a lot more things to take care of. For one thing, it matters if the pawn is moving down the board or up the board. We might need a field to hold on to that piece of information. Would the field belong in class Pawn or class ChessPiece? You have to think about the rules of chess in order to make that decision. All of the other pieces can move forward and backward, while pawns go in just one direction. So it only matters for a pawn and therefore the field belongs in class Pawn. Also, pawns move diagonally when they are capturing a piece, and we would have to incorporate that feature too, along with what happens when a pawn crosses all the way to the end of the board. As you start fleshing out your methods, you will discover the need for new fields and methods within your class.

Notice that the moveIsLegal() method includes return statements. These statements serve two purposes. First they make the program flow leave the method and go back to the calling program. Also, they send back the proper boolean value to the calling program. You must provide return values unless the method's return type is void. The program won't compile without them. In fact, if you attempted to compile class ChessPiece as written above you probably got an error message telling you that you needed a return statement for the moveIsLegal() method in that class. It doesn't care about the moveTo() method because that has a return type of void. (We will fix the compile-time error below.)

Why does it help to make subclasses? Why not just do class Pawn, class Bishop, etc. all separately and forget about the superclass ChessPiece? Is anything gained by having a superclass? Well, think about the fact that both a Label and an Applet are a Component. This means that they inherit all of the fields and methods of the superclass Component. Check the API Specification and you will see a huge number of methods in class Component. They include things like the setBackground() method. Programmers will want to be able to control the background color of all of the various kinds of components. With inheritance, the method can be written once in class Component, instead of separately for each subclass. Anyone who wants to extend class Component can also take advantage of those existing methods. In fact, it might make sense to make our ChessSquare class become a subclass of Component. Then we would be able to set the background color of the chess square without having to write that method ourselves. We would also be able to add a mouse listener to the chess square, using the method with this signature:

public void addMouseListener(MouseListener m)

That method is found in class Component, so if a chess square is a component, we can call that method for our chess squares. Then we'll know if the user has clicked on one of the chess squares.

Perhaps the best choice would be to make our class ChessSquare extend class Panel. A panel is a container, which means it can hold other components, such as labels and buttons. And a container is a component. If a chess square is a panel, it can hold labels too. We might need that, along with all of the methods we will get from the superclass Component. This is all we need to do:

import java.awt.Panel;


public class ChessSquare extends Panel{

boolean isOccupied = false;

ChessPiece currentPiece;

ChessSquare[] neighbor;

}

Let's see how class ChessSquare's inherited features can be incorporated into the applet's code. We should make the applet implement the MouseListener interface, by providing all of the five required methods for a mouse listener. Then we can add that mouse listener to each chess square, when we create the squares and add them to our chess board:

import java.awt.GridLayout;


public class ChessBoard extends Panel{

int numRows;

int numColumns;

ChessSquare[][] boardSquare = new ChessSquare[numRows][numColumns];


public ChessBoard(MouseListener theListener){

setLayout(new GridLayout(numRows, numColumns));

for (int i=0; i<numRows; i++){

for (int j=0; j<numColumns; j++){

boardSquare[i][j]= new ChessSquare();

add(boardSquare[i][j]);

boardSquare[i][j].addMouseListener(theListener);

}

}

}


...

}


Inside of the class constructor for ChessBoard, you can see a chess square being treated like a component. Each element of the boardSquare array is created using the ChessSquare constructor. Then the panel's add method is called, using a chess square as an argument, in the same way it would accept any object of type Component as an argument. The addMouseListener() method is being called in the object.method() syntax, where the object is a chess square. Only components can call the addMouseListener method(), but a chess square is a component.

Read more about inheritance: Sun tutorial
Bruce Eckel's textbook



Casting

Let's now add a mouseClicked method to the applet in order to respond to the event of the user clicking on a chess square:

import java.applet.Applet;

import java.awt.event.*;

import java.awt.GridLayout;


public class ChessApplet extends Applet implements MouseListener{

ChessPiece selectedPiece;


...


public void mouseClicked(MouseEvent e){

//identify clicked square

//highlight clicked square

//look for chess piece there

//if a piece is there, make it selected

}


...

}


I've put comments into the body of the mouseClicked method to give us an idea of what we need to do when a square is clicked. Just from the comments, we can see already that our ChessSquare class is incomplete. We haven't written any methods for drawing the chess piece onto the chess square, or for highlighting a square. But now we realize we need that and we can go back and take care of it. A more pressing issue is: how do we know which square got clicked? All of the squares have mouse listeners-- it could be anyone of those squares that caused the event. Well, if we consult the API Specification for class MouseEvent, we find a method getSource() which will tell us the source of the click event. Here is the signature of that method:

public Object getSource()

No arguments are required to call the method, but look at the return type. It will send us back something of type Object. That's fine-- a chess square is an object. We might write the body of the method like this:

public void mouseClicked(MouseEvent e){

Object clickedSquare = e.getSource();   //identify clicked square

clickedSquare.highlight();              //highlight clicked square

if (clickedSquare.isOccupied == true){    //look for chess piece there

selectedPiece = clickedSquare.currentPiece;   //make it selected

}

}


but we get errors from the compiler. It's that famous "cannot resolve symbol" error, because the variable clickedSquare is of type Object and class Object doesn't have a method called "highlight" or fields called "occupied" and "currentPiece." Only class ChessSquare has those. We need clickedSquare to be an instance of class ChessSquare, so that it can gain access to the fields and methods of that class. You might think this would work:

public void mouseClicked(MouseEvent e){

...

ChessSquare clickedSquare = e.getSource();

...

}


but now the compiler thinks you're crazy. The getSource() method does not return something of type ChessSquare. It returns something of type Object. The signature told you that! This kind of thing can really infuriate you until you learn about class casting. We need a way to turn an object into a chess square. Here's how you do that:

public void mouseClicked(MouseEvent e){

...

ChessSquare clickedSquare = (ChessSquare)e.getSource();

...

}


Simply by putting the name of the class in parentheses right next to thing that needs transforming, you can change it into an instance of another class. You are casting an object to a chess square. The compiler will let you cast anything you want. You could also cast that clicked object to a button. But during run-time, you would get an error called Class Cast Exception, because the clicked object is not in fact a button and it has no business trying to call methods from class Button. You do have to make sure that the cast makes sense, and only do it if you know for sure what kind of thing the object will be.

With the casting in place, the code will now compile. It will also behave the way we want it to, once the method is fleshed out. We don't even need to know where that clicked square is located on the chess board. The mouse event is sending us the one that got clicked. Whichever one that is, its highlight() method will be called. Its occupied field will be checked for true or false, and its chess piece will become the selectedPiece of the applet. It might feel a little scary not knowing which square it is, but all of that is taken care of for us at runtime.

Come to think of it, once the value of selectedPiece gets assigned by that mouseClicked method, we also don't know what kind of chess piece it is. It's whatever piece happened to be sitting on that clicked square. If we then want to check for a legal move of the selected piece, we wouldn't know which subclass to check in. Should we call the moveIsLegal() method for class Pawn or class Bishop or class Rook...? The methods are implemented differently in each subclass. This situation helps us to appreciate the importance of a superclass. We can just call the method from class ChessPiece. In fact, if we say

selectedPiece.moveIsLegal(5,4);

then it will have to be called from class ChessPiece because that's the variable type of our field selectedPiece. The amazing thing about inheritance in Java is that the smart little computer will go and find the right method for us during runtime, when the actual piece gets clicked. This feature is known as dynamic binding or polymorphism. If for some reason we ever do want to know which subclass the selectedPiece actually belongs to, there is a keyword "instanceof" which will help us out. The syntax looks like this:

keyhole logo
instanceof
if (selectedPiece instanceof King){

and that will return a boolean value of true or false to tell us if the selectedPiece is an object of type King. There is also a method in class Object called "getClass()." Since a chess piece is an object, we could always call selectedPiece.getClass() to find out that information. For most of your programming, however, you won't need to know that information because polymorphism is just so handy.

Read more about polymorphism: David Eck's textbook.
Sun tutorial
Bruce Eckel's textbook



Abstract Classes

We noted above a problem with class ChessPiece. We have a method called moveIsLegal() and we left it empty in the superclass so that we could allow the various subclasses to define it individually. But since the return type of moveIsLegal() is not void, we are obligated to return a value from the method, even if the rest of it is empty. The ChessPiece class file won't compile without such a return statement. But we don't know what value to return (true or false) because only the subclasses can determine if the move is legal for a particular type of chess piece. We could just stick a fake "return true" statement in there, simply to satisfy the compiler so we can move on. But that is not very good programming practice. We realize that implementing the method only makes sense in the context of the subclasses. You need to know what kind of piece you have in order to know what the return value should be.

If the method moveIsLegal() makes sense only for subclasses, maybe we should remove it from the class ChessPiece. We don't want to do that either. Remember our mouseClicked() method in the applet above? The user will click on a square--we have no idea which one, or what kind of chess piece is on that square. We go ahead and highlight the square and mark its current piece as selected, but in code we won't know in advance what kind of piece that is. We have to be able to call moveIsLegal() for a generic chess piece, whatever kind of thing got clicked.

So we do need to keep the method listed in class ChessPiece, but we don't really want to define it there. The solution is to make the method abstract. You use the "abstract" keyword right before the return type of a method:

keyhole logo
abstract
public abstract boolean moveIsLegal(int newRow, int newColumn);

and at the end of the line you put a semi-colon instead of a curly bracket. This is because abstract methods have no body. We are not defining what the method does. We are only saying that it exists for objects of type ChessPiece. The subclasses of ChessPiece will define what the method does-- and that's exactly what we want!

Now what about our other method, the one that moves the pieces? Should that also be abstract? We have to decide if that method will act the same way for every type of chess piece, or if it needs to be defined separately by the individual subclasses. Well, if moving a piece just means reassigning the value of the row and column fields, we can do that in class ChessPiece. Since it works the same way for all subclasses, we might as well leave it in the superclass so we don't have to write it over again for each subclass. That is the power of inheritance in Java.

If you tried to compile class ChessPiece with the abstract method included, you would get an error message telling you that the class itself needs to be declared abstract. This actually makes sense for our situation. On a chess board, I can pick up a pawn, a king, a rook, and so forth. Every piece I pick up belongs to one of the subclasses. There is no such thing as a chess piece that is only a chess piece and nothing else. To declare a class abstract, you just insert the keyword right before the word "class":

public abstract class ChessPiece{

In our chess game applet, we won't be making instances of class ChessPiece. Abstract classes cannot be instantiated. The compiler will tell you that as an error message if you try to say something like:

ChessPiece specialPiece = new ChessPiece();

Instead, we will make instances of class Pawn, class Rook, class Bishop, etc.. When we assign a variable with the constructor "new Pawn," the compiler will be looking for the constructor in class Pawn. Of course, as soon as it gets to the information about class Pawn, it finds out that Pawn is a subclass of ChessPiece, which is itself a subclass of Object. So it will also call constructors for those superclasses. If the superclasses don't have specified constructors, it will call a default constructor. Do we want to specify a constructor for class ChessPiece? Would there be anything to gain? Let's look again at the constructor we made for class Pawn:

public Pawn(int originalRow, originalColumn){

row = originalRow;

column = originalColumn;

pieceName = "pawn";

}

Wouldn't a constructor for class Bishop look very similar?

public Bishop(int originalRow, int originalColumn){

row = originalRow;

column = originalColumn;

pieceName = "bishop";

}

Maybe we can be a little more efficent. The fields of row and column are part of class ChessPiece and we know that every type of chess piece will need to have those values initialized by the constructor. We could do that initializing in the constructor for class ChessPiece:

//class constructor

public ChessPiece(int originalRow, int originalColumn){

row = originalRow;

column = originalColumn;

}

and then leave the initializing of pieceName in the subclasses, since it will be different for each subclass. The subclass constructor will look like this:

//class constructor

public Bishop(int originalRow, int originalColumn){

super(originalRow, originalColumn);

pieceName = "bishop";

}

You saw the keyword "super" once before in the context of overriding methods. It refers to the superclass of whatever subclass you are in. The way it is written here, it will call the constructor for that superclass and it will send in the values of originalRow and originalColumn as arguments for the superclass constructor. If we didn't have the super statement, a default constructor would be called, and that doesn't have any arguments. Our row and column fields would not get initialized. Calls to the superclass constructor must always be the very first statement in your subclass constructor.

We aren't allowed to call the ChessPiece constructor directly with the "new" keyword, because then we would be making something that is just a ChessPiece and not an instance of any subclass. The whole reason we made class ChessPiece abstract was because it didn't make any sense to create a plain chess piece. However, we can call the ChessPiece constructor by way of the super keyword, because it is in the context of creating an instance of one of the subclasses.

We are also allowed to use the abstract class ChessPiece as a variable type for our whiteTeam array and for our selectedPiece field. This is because the array gets filled by calls to subclass constructors. The elements of the array are particular types of chess pieces and we will use those pieces when we assign fields like currentPiece and selectedPiece. Even though we don't know in advance what kind of chess piece will be assigned, we do know that it is some kind of piece. I agree that it feels pretty strange, like we're not really in control of our own program. But eventually you begin to see a beauty in the object-oriented paradigm.

Okay, here is our updated version of the abstract class:

public abstract class ChessPiece{

String pieceName;

int row;

int column;


public ChessPiece(int originalRow, int originalColumn){

row = originalRow;

column = originalColumn;

}


public void moveTo(int newRow, int newColumn){

row = newRow;

column = newColumn;

}


public abstract boolean moveIsLegal(int newRow, int newColumn);


}

As you see, an abstract class can still have a constructor and it can contain methods that are not abstract. In fact, if you look closely at the API Spec, you will find that quite a few of those classes are abstract, even though you never noticed it before. Class Graphics is abstract, as is class Component.

Read more about abstract classes: David Eck's textbook.
Sun tutorial
Bruce Eckel's textbook



Interfaces

Now that you've seen the value of an abstract class, you might be ready to understand why the Java API also includes these things called interfaces. You have been using interfaces like MouseListener in your applets, but if you're like me when I first started programming, you're using them without really understanding what they are. It took me quite a while to figure it out.

You can think of an interface as a class that's really abstract. Above in our abstract class ChessPiece, we have an abstract method called moveIsLegal(), along with a regular old method called moveTo(). Remember that abstract methods are declared, but they lack a method body and are not defined. We wanted to leave the moveIsLegal() method to be defined by the subclasses. In fact, the subclasses are obligated to define that method if they want to extend class ChessPiece. If we wrote class Bishop like this:

public class Bishop extends ChessPiece{

//class constructor

public Bishop(int originalRow, originalColumn){

super(originalRow, originalColumn);

pieceName = "bishop";

}

}

and didn't bother defining the moveIsLegal() method, we would get an error message from the compiler:

Bishop should be declared abstract; it does not define moveIsLegal(int,int) in ChessPiece

It's telling us that the only way we can avoid defining that method is to make Bishop also an abstract class, but of course we don't want to do that. So we must include a moveIsLegal() method with a body. It won't check to see if anything is in the body, but the body does have to be there:

public boolean moveIsLegal(int r, int c){

}

It's the braces that establish the presence of a method body. So there's an agreement between abstract classes and their subclasses: you can extend me if you give my abstract methods a body. The non-abstract methods like moveTo() don't need to be defined by the subclass.

The interface structure takes this even further. None of its methods have a body. All of them are waiting to be defined. We say that the methods lack an implementation. That's why you use the keyword "implements" when you are using an interface:

keyhole logo
implements
public class MyApplet extends Applet implements MouseListener{
}

You're saying: "Okay, I am willing to take those five undefined methods in interface MouseListener and give them a body. I will implement them." If you fail to include one of those five methods (and I'm sure you've made this mistake several times), you get the same message from the compiler that we got about class Bishop:

MyApplet should be declared abstract; it does not define mouseEntered(MouseEvent) in MouseListener

You'll even get that message if you make a mistake in how you type your mouseEntered() method. The signature must match what you find listed in the Interface. In fact, that's all the interface really consists of, is a bunch of method signatures. You could make your own interface just by listing signatures. It might look like this:

keyhole logo
interface
public interface Fireplace{

public void buildFire(Wood w);

public void ignite();

}

You use semi-colons at the end of each signature, since the methods cannot have a body. (It is also possible for interfaces to have fields, but only if the fields are initialized at the same time they are declared. Most of the interfaces you will find listed in the API Specification do not have fields.)

Now anyone who wants to implement Fireplace must provide a definition for your two methods. Since there's no limitation on how many methods a class can include, there's no limitation on how many different interfaces a class can implement. Frequently you want multiple listener interfaces in your applet and that's fine.

In contrast, you can only extend one superclass. That's a good thing, because otherwise the keyword "super" would be ambiguous. Now you can see why interfaces don't have a class constructor. There would be no way to call it for one thing. It can't be called by "super" as you could with an abstract class. And it certainly can't be called by the new keyword, because then you would be trying to instantiate an abstract class.

I could also use an interface to write a programming assignment for you. I could say:

public interface DeckOfCards{

public void shuffle();

}

This is my way of telling you that a deck of cards will need to be shuffled. You will be in charge of figuring out how to shuffle it. That's the implementation--how to do something. When you implement an interface, you are providing the instructions on how to do the task. The interface only lists what the tasks are. Of course, the deck probably needs to do more than just shuffle--we could figure out a more complete interface together.

Now, while I am waiting for you to figure out the implementation, I will go ahead and make use of the interface in my own code. I will pretend that your code is already finished and working. I'll write a card game like this:

public class PokerGame{

DeckOfCards theDeck;

public void startGame(){

theDeck.shuffle()

}

}

This will compile, even though you are not done building your implementation yet. The compiler goes to the interface found in DeckOfCards.java and sees that it includes a shuffle method. That's good enough. I can write out my whole game. The only thing that is missing is to initialize the value of myDeck. That would have to wait until you send me your class file, because I need to know the name of your particular implementation, and the signature of your class constructor. Once I have it, I could make my own constructor like this:

public class PokerGame{

DeckOfCards theDeck;

public PokerGame(int numPlayers){

theDeck = new EricDeck();

}

}

This would allow me to run my whole game program using Eric's implementation of a deck of cards. Then I could substitute somebody else's implementation, simply by changing the class name in the line of code where I initialize the deck. Let's say a different student made a deck of cards implementation where you can change how many suits there are in the deck. Calling that constructor would look different:

public class PokerGame{

DeckOfCards theDeck;

public PokerGame(int numPlayers){

theDeck = new LauraDeck(4, 13);

}

}

But nothing else in the whole program needs to change! That's the value of making your own interface.


Exercises

  1. Here is another class, Zucchini. I made it up-there isn't really a package called "food" available from Sun, but pretend there is.

    import java.food.*;


    public class Zucchini extends Vegetable{


    public void prepare(){

    if (testFreshness()== true){

    wash();

    peel();

    slice("Julienne");

    }else {

    System.out.println("Cannot prepare");

    }

    }


    public void saute(FryingPan thePan){

    Utensil theSpatula = Utensil.getSpatula();

    thePan.setHeat("medium");

    thePan.addOil(.5);

    for (int i=0; i<12; i++){

    theSpatula.flip(this);

    }

    }


    public void serve(){

    Cheese topping = new Cheese("romano");

    topping.grate();

    topping.sprinkle(this);

    }

    }

    This code compiles and runs without exceptions, so there are no errors. If you were to look up class Vegetable in the API Specification, what methods should appear there? What would be the signature of those methods?



  2. The Zucchini class refers to several other classes. How many classes do you see? List them. How do you know that they are classes?



  3. Identify all of the methods that belong to the other classes.



  4. Where is a class constructor being called? How can you tell?



  5. Which method is static? How can you tell?



Previous Topic Java Home Page Syntax Sheet Next Topic