Java inheritance |
Inheritance in Java is very similar to that in Grace, and in fact we have been using it from the beginning with our main programs (classes that extend WindowController and in the last section with classes that extend Thread).
Before we get into the nitty gritty of inheritance, let me remind you about the visibility declarations in Java. If a variable or method is declared to be public, then it is accessible to anyone who has a reference to the object. If it is declared to be private then it is only accessible inside the class that defines it. If it is declared to be protected, then it is accessible inside the class that defines it or in any subclass.1 Thus protected is very similar to Grace's confidential.
To this point we have insisted that you declare all of the instance variables in Java to be private. However, you should declare them to be protected if you wish them to be accessible to subclasses. Never make an instance variable public. Instead if you want read or write access to a variable to be public, declare a getter or setter method to access them. (This is what Grace does for you automatically when you declare a variable to be readable or writable.)
In Java we write extends rather than inherits and the initialization code for the superclass is invoked in the constructor of the subclass. Here is a simple example in Java. First the superclass:
// A FramedDisplay is a composite graphic intended to serve // as a frame in which a program can display various forms // of information. public class FramedDisplay { private static final int BORDER = 3; // Border thickness private static final int ROUNDNESS = 5; // Corner roundess // Colors used for the frame private static final Color BORDERCOLOR = Color.GRAY; private static final Color HIGHLIGHTCOLOR = Color.RED; private FilledRoundedRect body; // The background private FilledRoundedRect border; // The border // Create the display areaÕs background and border and set // their colors appropriately public FramedDisplay( double x, double y, double width, double height, DrawingCanvas canvas ) { border = new FilledRoundedRect( x, y, width, height, ROUNDNESS, ROUNDNESS, canvas ); body = new FilledRoundedRect( x + BORDER, y + BORDER, width - 2*BORDER, height - 2*BORDER, ROUNDNESS, ROUNDNESS, canvas ); border.setColor( BORDERCOLOR ); } // Change the border's color to make it stand out public void highlight( ) { border.setColor( HIGHLIGHTCOLOR ); } // Restore the standard border color public void unHighlight( ) { border.setColor( BORDERCOLOR ); } }
Here is the code for the subclass:
// A FramedText object displays a specified text message on a // background framed by a distinct border. public class FramedText extends FramedDisplay { // Color used for the text private static final Color TEXTCOLOR = Color.WHITE; private Text message; // The message displayed private Location frameCenter; // Where message belongs // Create a FramedText object displaying the text ÕcontentsÕ // at the position and with the dimensions specified. public FramedText( String contents, double x, double y, double width, double height, DrawingCanvas canvas ) { // construct the frame super( x, y, width, height, canvas ); // Construct and appropriately position the message frameCenter = new Location( x + width/2, y + height/2 ); message = new Text ( contents, x, y, canvas ); message.setColor( TEXTCOLOR ); positionContents(); } // Position the message to center it in the frame private void positionContents() { message.moveTo( frameCenter ); message.move( -message.getWidth()/2, -message.getHeight()/2 ); } // Change the font size used public void setTextSize( int size ) { message.setFontSize( size ); positionContents(); } }
In Grace we would have started the body of the method with inherits framedDisplay..... However in Java we add extends FrameDisplay to the class header. We invoke the initialization code in Java by inserting a super constructor as the first statement in the constructor of the subclass. The statement super( x, y, width, height, canvas ); invokes the superclass (FramedDisplay) constructor. The rest of the body of the constructor initializes the new instance variables introduced in the subclass, though it could have also modified the initialization of instance variables of the superclass, if they were declared to be protected. In this case we did not need to access them, so all the variables in the superclass were declared to be private.
Java requires that the first statement of every constructor be a call to a super constructor, aside from one very important exception. If a class has a parameterless constructor, and the first line of its subclass constructor does not contain a call to a super constructor, then Java will automatically insert a call to the parameterless super constructor. This exception explains why we did not need to insert a call to a super constructor in classes extending Thread or WindowController. In the case of WindowController we do not even need to write the constructor at all. The system automatically runs the parameterless constructor for WindowController, which initializes the canvas and other instance variables from the superclass.
As in Grace, if you wish to request a method definition of some m from the superclass, you can just write super.m(...).
As mentioned earlier, a Java class can only extend a single class, but it may implement as many interfaces as desired.
Just as in Grace, an interface may extend one or more existing interfaces. Where in Grace we wrote
type T = U & type { n(..) -> V }
In Java we would write the same expression as
public interface T extends U { V n(...) }
Examples in the objectdraw library in Java include Drawable2DInterface and Drawable1DInterface both extending DrawableInterface. Also Resizable2DInterface extends Drawable2DInterface. As in Grace, if an expression has an interface type then it also has all of its super interface types. Thus if an object has type Drawable2DInterface then it also has type DrawableInterface. Like classes, one interface is only a subinterface of another if it declared to be an extension of it.
That is we use the same extends clause to extend both classes and interfaces. However we use implements to indicate that a class provides elements of an interface.
Because classes can also be used as types in Java, if an expression has a type corresponding to a class then it also has the type of any superclass. Thus in our example above, if obj represents a FramedText then it also has type FramedDisplay
maketopicSummary
Here is a brief summary of what must be done to convert a Grace program to Java:
class C { ... private String strVal; ... public C(String strVal) { this.strVal = strVal; // right side is the parameter, left side is instance variable } ... }
Java inheritance |