Learning Resources
What Is Inheritance?
Different kinds of objects often have a certain amount in common with each other. Mountain bikes, road bikes, and tandem bikes, for example, all share the characteristics of bicycles (current speed, current pedal cadence, current gear). Yet each also defines additional features that make them different: tandem bicycles have two seats and two sets of handlebars; road bikes have drop handlebars; some mountain bikes have an additional chain ring, giving them a lower gear ratio.
Object-oriented programming allows classes to inherit commonly used state and behavior from other classes. In this example, Bicycle
now becomes the superclass of MountainBike
, RoadBike
, and TandemBike
. In the Java programming language, each class is allowed to have one direct superclass, and each superclass has the potential for an unlimited number of subclasses:
A hierarchy of bicycle classes.
The syntax for creating a subclass is simple. At the beginning of your class declaration, use the extends
keyword, followed by the name of the class to inherit from:
class MountainBike extends Bicycle { // new fields and methods defining // a mountain bike would go here }
This gives MountainBike
all the same fields and methods as Bicycle
, yet allows its code to focus exclusively on the features that make it unique. This makes code for your subclasses easy to read. However, you must take care to properly document the state and behavior that each superclass defines, since that code will not appear in the source file of each subclass.
Inheritance
In the preceding lessons, you have seen inheritance mentioned several times. In the Java language, classes can be derived from other classes, thereby inheriting fields and methods from those classes.
Definitions: A class that is derived from another class is called a subclass (also a derived class, extended class, or child class). The class from which the subclass is derived is called a superclass (also a base class or a parent class).
Excepting
Object
, which has no superclass, every class has one and only one direct superclass (single inheritance). In the absence of any other explicit superclass, every class is implicitly a subclass of Object
.Classes can be derived from classes that are derived from classes that are derived from classes, and so on, and ultimately derived from the topmost class,
Object
. Such a class is said to be descended from all the classes in the inheritance chain stretching back to Object
.
The idea of inheritance is simple but powerful: When you want to create a new class and there is already a class that includes some of the code that you want, you can derive your new class from the existing class. In doing this, you can reuse the fields and methods of the existing class without having to write (and debug!) them yourself.
A subclass inherits all the members (fields, methods, and nested classes) from its superclass. Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.
The Java Platform Class Hierarchy
The Object
class, defined in the java.lang
package, defines and implements behavior common to all classes—including the ones that you write. In the Java platform, many classes derive directly from Object
, other classes derive from some of those classes, and so on, forming a hierarchy of classes.
All Classes in the Java Platform are Descendants of Object
At the top of the hierarchy, Object
is the most general of all classes. Classes near the bottom of the hierarchy provide more specialized behavior.
An Example of Inheritance
Here is the sample code for a possible implementation of a Bicycle
class that was presented in the Classes and Objects lesson:
public class Bicycle { // the Bicycle class has // three fields public int cadence; public int gear; public int speed; // the Bicycle class has // one constructor public Bicycle(int startCadence, int startSpeed, int startGear) { gear = startGear; cadence = startCadence; speed = startSpeed; } // the Bicycle class has // four methods public void setCadence(int newValue) { cadence = newValue; } public void setGear(int newValue) { gear = newValue; } public void applyBrake(int decrement) { speed -= decrement; } public void speedUp(int increment) { speed += increment; } }
A class declaration for a MountainBike
class that is a subclass of Bicycle
might look like this:
public class MountainBike extends Bicycle { // the MountainBike subclass adds // one field public int seatHeight; // the MountainBike subclass has one // constructor public MountainBike(int startHeight, int startCadence, int startSpeed, int startGear) { super(startCadence, startSpeed, startGear); seatHeight = startHeight; } // the MountainBike subclass adds // one method public void setHeight(int newValue) { seatHeight = newValue; } }
MountainBike
inherits all the fields and methods of Bicycle
and adds the field seatHeight
and a method to set it. Except for the constructor, it is as if you had written a new MountainBike
class entirely from scratch, with four fields and five methods. However, you didn't have to do all the work. This would be especially valuable if the methods in the Bicycle
class were complex and had taken substantial time to debug.
What You Can Do in a Subclass
A subclass inherits all of the public and protected members of its parent, no matter what package the subclass is in. If the subclass is in the same package as its parent, it also inherits the package-private members of the parent. You can use the inherited members as is, replace them, hide them, or supplement them with new members:
- The inherited fields can be used directly, just like any other fields.
- You can declare a field in the subclass with the same name as the one in the superclass, thus hiding it (not recommended).
- You can declare new fields in the subclass that are not in the superclass.
- The inherited methods can be used directly as they are.
- You can write a new instance method in the subclass that has the same signature as the one in the superclass, thus overriding it.
- You can write a new static method in the subclass that has the same signature as the one in the superclass, thus hiding it.
- You can declare new methods in the subclass that are not in the superclass.
-
You can write a subclass constructor that invokes the constructor of the superclass, either implicitly or by using the keyword
super
.
The following sections in this lesson will expand on these topics.
Private Members in a Superclass
A subclass does not inherit the private
members of its parent class. However, if the superclass has public or protected methods for accessing its private fields, these can also be used by the subclass.
A nested class has access to all the private members of its enclosing class—both fields and methods. Therefore, a public or protected nested class inherited by a subclass has indirect access to all of the private members of the superclass.
Casting Objects
We have seen that an object is of the data type of the class from which it was instantiated. For example, if we write
public MountainBike myBike = new MountainBike();
then myBike
is of type MountainBike
.
MountainBike
is descended from Bicycle
and Object
. Therefore, a MountainBike
is a Bicycle
and is also an Object
, and it can be used wherever Bicycle
or Object
objects are called for.
The reverse is not necessarily true: a Bicycle
may be a MountainBike
, but it isn't necessarily. Similarly, an Object
may be a Bicycle
or a MountainBike
, but it isn't necessarily.
Casting shows the use of an object of one type in place of another type, among the objects permitted by inheritance and implementations. For example, if we write
Object obj = new MountainBike();
then obj
is both an Object
and a Mountainbike
(until such time as obj
is assigned another object that is not a Mountainbike
). This is called implicit casting.
If, on the other hand, we write
MountainBike myBike = obj;
we would get a compile-time error because obj
is not known to the compiler to be a MountainBike
. However, we can tell the compiler that we promise to assign a MountainBike
to obj
by explicit casting:
MountainBike myBike = (MountainBike)obj;
This cast inserts a runtime check that obj
is assigned a MountainBike
so that the compiler can safely assume that obj
is a MountainBike
. If obj
is not a Mountainbike
at runtime, an exception will be thrown.
Note: You can make a logical test as to the type of a particular object using the
instanceof
operator. This can save you from a runtime error owing to an improper cast. For example:
if (obj instanceof MountainBike) { MountainBike myBike = (MountainBike)obj; }
Here the instanceof
operator verifies that obj
refers to a MountainBike
so that we can make the cast with knowledge that there will be no runtime exception thrown.
Overriding and Hiding Methods
Instance Methods
An instance method in a subclass with the same signature (name, plus the number and the type of its parameters) and return type as an instance method in the superclass overrides the superclass's method.
The ability of a subclass to override a method allows a class to inherit from a superclass whose behavior is "close enough" and then to modify behavior as needed. The overriding method has the same name, number and type of parameters, and return type as the method it overrides. An overriding method can also return a subtype of the type returned by the overridden method. This is called a covariant return type.
When overriding a method, you might want to use the @Override
annotation that instructs the compiler that you intend to override a method in the superclass. If, for some reason, the compiler detects that the method does not exist in one of the superclasses, it will generate an error. For more information on @Override
, see Annotations
.
Class Methods
If a subclass defines a class method with the same signature as a class method in the superclass, the method in the subclass hides the one in the superclass.
The distinction between hiding and overriding has important implications. The version of the overridden method that gets invoked is the one in the subclass. The version of the hidden method that gets invoked depends on whether it is invoked from the superclass or the subclass. Let's look at an example that contains two classes. The first is Animal
, which contains one instance method and one class method:
public class Animal { public static void testClassMethod() { System.out.println("The class" + " method in Animal."); } public void testInstanceMethod() { System.out.println("The instance " + " method in Animal."); } }
The second class, a subclass of Animal
, is called Cat
:
public class Cat extends Animal { public static void testClassMethod() { System.out.println("The class method" + " in Cat."); } public void testInstanceMethod() { System.out.println("The instance method" + " in Cat."); } public static void main(String[] args) { Cat myCat = new Cat(); Animal myAnimal = myCat; Animal.testClassMethod(); myAnimal.testInstanceMethod(); } }
The Cat
class overrides the instance method in Animal
and hides the class method in Animal
. The main
method in this class creates an instance of Cat
and calls testClassMethod()
on the class and testInstanceMethod()
on the instance.
The output from this program is as follows:
The class method in Animal. The instance method in Cat.
As promised, the version of the hidden method that gets invoked is the one in the superclass, and the version of the overridden method that gets invoked is the one in the subclass.
Modifiers
The access specifier for an overriding method can allow more, but not less, access than the overridden method. For example, a protected instance method in the superclass can be made public, but not private, in the subclass.
You will get a compile-time error if you attempt to change an instance method in the superclass to a class method in the subclass, and vice versa.
Summary
The following table summarizes what happens when you define a method with the same signature as a method in a superclass.
Superclass Instance Method | Superclass Static Method | |
---|---|---|
Subclass Instance Method | Overrides | Generates a compile-time error |
Subclass Static Method | Generates a compile-time error | Hides |
Note: In a subclass, you can overload the methods inherited from the superclass. Such overloaded methods neither hide nor override the superclass methods—they are new methods, unique to the subclass.
Polymorphism
The dictionary definition of polymorphism refers to a principle in biology in which an organism or species can have many different forms or stages. This principle can also be applied to object-oriented programming and languages like the Java language. Subclasses of a class can define their own unique behaviors and yet share some of the same functionality of the parent class.
Polymorphism can be demonstrated with a minor modification to the Bicycle
class. For example, a printDescription
method could be added to the class that displays all the data currently stored in an instance.
public void printDescription(){ System.out.println("\nBike is " + "in gear " + this.gear + " with a cadence of " + this.cadence + " and travelling at a speed of " + this.speed + ". "); }
To demonstrate polymorphic features in the Java language, extend the Bicycle
class with a MountainBike
and a RoadBike
class. For MountainBike
, add a field for suspension
, which is a String
value that indicates if the bike has a front shock absorber, Front
. Or, the bike has a front and back shock absorber, Dual
.
Here is the updated class:
public class MountainBike extends Bicycle { private String suspension; public MountainBike( int startCadence, int startSpeed, int startGear, String suspensionType){ super(startCadence, startSpeed, startGear); this.setSuspension(suspensionType); } public String getSuspension(){ return this.suspension; } public void setSuspension(String suspensionType) { this.suspension = suspensionType; } public void printDescription() { super.printDescription(); System.out.println("The " + "MountainBike has a" + getSuspension() + " suspension."); } }
Note the overridden printDescription
method. In addition to the information provided before, additional data about the suspension is included to the output.
Next, create the RoadBike
class. Because road or racing bikes have skinny tires, add an attribute to track the tire width. Here is the RoadBike
class:
public class RoadBike extends Bicycle{ // In millimeters (mm) private int tireWidth; public RoadBike(int startCadence, int startSpeed, int startGear, int newTireWidth){ super(startCadence, startSpeed, startGear); this.setTireWidth(newTireWidth); } public int getTireWidth(){ return this.tireWidth; } public void setTireWidth(int newTireWidth){ this.tireWidth = newTireWidth; } public void printDescription(){ super.printDescription(); System.out.println("The RoadBike" " has " + getTireWidth() + " MM tires."); } }
Note that once again, the printDescription
method has been overridden. This time, information about the tire width is displayed.
To summarize, there are three classes: Bicycle
, MountainBike
, and RoadBike
. The two subclasses override the printDescription
method and print unique information.
Here is a test program that creates three Bicycle
variables. Each variable is assigned to one of the three bicycle classes. Each variable is then printed.
public class TestBikes { public static void main(String[] args){ Bicycle bike01, bike02, bike03; bike01 = new Bicycle(20, 10, 1); bike02 = new MountainBike(20, 10, 5, "Dual"); bike03 = new RoadBike(40, 20, 8, 23); bike01.printDescription(); bike02.printDescription(); bike03.printDescription(); } }
The following is the output from the test program:
Bike is in gear 1 with a cadence of 20 and travelling at a speed of 10. Bike is in gear 5 with a cadence of 20 and travelling at a speed of 10. The MountainBike has a Dual suspension. Bike is in gear 8 with a cadence of 40 and travelling at a speed of 20. The RoadBike has 23 MM tires.
The Java virtual machine (JVM) calls the appropriate method for the object that is referred to in each variable. It does not call the method that is defined by the variable's type. This behavior is referred to as virtual method invocation and demonstrates an aspect of the important polymorphism features in the Java language.
Hiding Fields
Within a class, a field that has the same name as a field in the superclass hides the superclass's field, even if their types are different. Within the subclass, the field in the superclass cannot be referenced by its simple name. Instead, the field must be accessed through super, which is covered in the next section. Generally speaking, we don't recommend hiding fields as it makes code difficult to read.
Using the Keyword super
Accessing Superclass Members
If your method overrides one of its superclass's methods, you can invoke the overridden method through the use of the keyword super
. You can also use super
to refer to a hidden field (although hiding fields is discouraged). Consider this class, Superclass
:
public class Superclass { public void printMethod() { System.out.println("Printed in Superclass."); } }
Here is a subclass, called Subclass
, that overrides printMethod()
:
public class Subclass extends Superclass { // overrides printMethod in Superclass public void printMethod() { super.printMethod(); System.out.println("Printed in Subclass"); } public static void main(String[] args) { Subclass s = new Subclass(); s.printMethod(); } }
Within Subclass
, the simple name printMethod()
refers to the one declared in Subclass
, which overrides the one in Superclass
. So, to refer to printMethod()
inherited from Superclass
, Subclass
must use a qualified name, using super
as shown. Compiling and executing Subclass
prints the following:
Printed in Superclass. Printed in Subclass
Subclass Constructors
The following example illustrates how to use the super
keyword to invoke a superclass's constructor. Recall from the Bicycle
example that MountainBike
is a subclass of Bicycle
. Here is the MountainBike
(subclass) constructor that calls the superclass constructor and then adds initialization code of its own:
public MountainBike(int startHeight, int startCadence, int startSpeed, int startGear) { super(startCadence, startSpeed, startGear); seatHeight = startHeight; }
Invocation of a superclass constructor must be the first line in the subclass constructor.
The syntax for calling a superclass constructor is
super();
super(parameter list);
With super()
, the superclass no-argument constructor is called. With super(parameter list)
, the superclass constructor with a matching parameter list is called.
Note: If a constructor does not explicitly invoke a superclass constructor, the Java compiler automatically inserts a call to the no-argument constructor of the superclass. If the super class does not have a no-argument constructor, you will get a compile-time error.
Object
does have such a constructor, so if Object
is the only superclass, there is no problem.
If a subclass constructor invokes a constructor of its superclass, either explicitly or implicitly, you might think that there will be a whole chain of constructors called, all the way back to the constructor of Object
. In fact, this is the case. It is called constructor chaining, and you need to be aware of it when there is a long line of class descent.
Object as a Superclass
The Object
class, in the java.lang
package, sits at the top of the class hierarchy tree. Every class is a descendant, direct or indirect, of the Object
class. Every class you use or write inherits the instance methods of Object
. You need not use any of these methods, but, if you choose to do so, you may need to override them with code that is specific to your class. The methods inherited from Object
that are discussed in this section are:
-
protected Object clone() throws CloneNotSupportedException
Creates and returns a copy of this object. -
public boolean equals(Object obj)
Indicates whether some other object is "equal to" this one. -
protected void finalize() throws Throwable
Called by the garbage collector on an object when garbage
collection determines that there are no more references to the object -
public final Class getClass()
Returns the runtime class of an object. -
public int hashCode()
Returns a hash code value for the object. -
public String toString()
Returns a string representation of the object.
The notify
, notifyAll
, and wait
methods of Object
all play a part in synchronizing the activities of independently running threads in a program, which is discussed in a later lesson and won't be covered here. There are five of these methods:
-
public final void notify()
-
public final void notifyAll()
-
public final void wait()
-
public final void wait(long timeout)
-
public final void wait(long timeout, int nanos)
Note: There are some subtle aspects to a number of these methods, especially the
clone
method.
The clone() Method
If a class, or one of its superclasses, implements the Cloneable
interface, you can use the clone()
method to create a copy from an existing object. To create a clone, you write:
aCloneableObject.clone();
Object
's implementation of this method checks to see whether the object on which clone()
was invoked implements the Cloneable
interface. If the object does not, the method throws a CloneNotSupportedException
exception. Exception handling will be covered in a later lesson. For the moment, you need to know that clone()
must be declared as
protected Object clone() throws CloneNotSupportedException
or:
public Object clone() throws CloneNotSupportedException
if you are going to write a clone()
method to override the one in Object
.
If the object on which clone()
was invoked does implement the Cloneable
interface, Object
's implementation of the clone()
method creates an object of the same class as the original object and initializes the new object's member variables to have the same values as the original object's corresponding member variables.
The simplest way to make your class cloneable is to add implements Cloneable
to your class's declaration. then your objects can invoke the clone()
method.
For some classes, the default behavior of Object
's clone()
method works just fine. If, however, an object contains a reference to an external object, say ObjExternal
, you may need to override clone()
to get correct behavior. Otherwise, a change in ObjExternal
made by one object will be visible in its clone also. This means that the original object and its clone are not independent—to decouple them, you must override clone()
so that it clones the object and ObjExternal
. Then the original object references ObjExternal
and the clone references a clone of ObjExternal
, so that the object and its clone are truly independent.
The equals() Method
The equals()
method compares two objects for equality and returns true
if they are equal. The equals()
method provided in the Object
class uses the identity operator (==
) to determine whether two objects are equal. For primitive data types, this gives the correct result. For objects, however, it does not. The equals()
method provided by Object
tests whether the object references are equal—that is, if the objects compared are the exact same object.
To test whether two objects are equal in the sense of equivalency (containing the same information), you must override the equals()
method. Here is an example of a Book
class that overrides equals()
:
public class Book { ... public boolean equals(Object obj) { if (obj instanceof Book) return ISBN.equals((Book)obj.getISBN()); else return false; } }
Consider this code that tests two instances of the Book
class for equality:
// Swing Tutorial, 2nd edition Book firstBook = new Book("0201914670"); Book secondBook = new Book("0201914670"); if (firstBook.equals(secondBook)) { System.out.println("objects are equal"); } else { System.out.println("objects are not equal"); }
This program displays objects are equal
even though firstBook
and secondBook
reference two distinct objects. They are considered equal because the objects compared contain the same ISBN number.
You should always override the equals()
method if the identity operator is not appropriate for your class.
Note: If you override
equals()
, you must override hashCode()
as well.
The finalize() Method
The Object
class provides a callback method, finalize()
, that may be invoked on an object when it becomes garbage. Object
's implementation of finalize()
does nothing—you can override finalize()
to do cleanup, such as freeing resources.
The finalize()
method may be called automatically by the system, but when it is called, or even if it is called, is uncertain. Therefore, you should not rely on this method to do your cleanup for you. For example, if you don't close file descriptors in your code after performing I/O and you expect finalize()
to close them for you, you may run out of file descriptors.
The getClass() Method
You cannot override getClass
.
The getClass()
method returns a Class
object, which has methods you can use to get information about the class, such as its name (getSimpleName()
), its superclass (getSuperclass()
), and the interfaces it implements (getInterfaces()
). For example, the following method gets and displays the class name of an object:
void printClassName(Object obj) { System.out.println("The object's" + " class is " + obj.getClass().getSimpleName()); }
The Class
class, in the java.lang
package, has a large number of methods (more than 50). For example, you can test to see if the class is an annotation (isAnnotation()
), an interface (isInterface()
), or an enumeration (isEnum()
). You can see what the object's fields are (getFields()
) or what its methods are (getMethods()
), and so on.
The hashCode() Method
The value returned by hashCode()
is the object's hash code, which is the object's memory address in hexadecimal.
By definition, if two objects are equal, their hash code must also be equal. If you override the equals()
method, you change the way two objects are equated and Object
's implementation of hashCode()
is no longer valid. Therefore, if you override the equals()
method, you must also override the hashCode()
method as well.
The toString() Method
You should always consider overriding the toString()
method in your classes.
The Object
's toString()
method returns a String
representation of the object, which is very useful for debugging. The String
representation for an object depends entirely on the object, which is why you need to override toString()
in your classes.
You can use toString()
along with System.out.println()
to display a text representation of an object, such as an instance of Book
:
System.out.println(firstBook.toString());
which would, for a properly overridden toString()
method, print something useful, like this:
ISBN: 0201914670; The Swing Tutorial; A Guide to Constructing GUIs, 2nd Edition
Writing Final Classes and Methods
You can declare some or all of a class's methods final. You use the final
keyword in a method declaration to indicate that the method cannot be overridden by subclasses. The Object
class does this—a number of its methods are final
.
You might wish to make a method final if it has an implementation that should not be changed and it is critical to the consistent state of the object. For example, you might want to make the getFirstPlayer
method in this ChessAlgorithm
class final:
class ChessAlgorithm { enum ChessPlayer { WHITE, BLACK } ... final ChessPlayer getFirstPlayer() { return ChessPlayer.WHITE; } ... }
Methods called from constructors should generally be declared final. If a constructor calls a non-final method, a subclass may redefine that method with surprising or undesirable results.
Note that you can also declare an entire class final. A class that is declared final cannot be subclassed. This is particularly useful, for example, when creating an immutable class like the String
class.
Abstract Methods and Classes
An abstract class is a class that is declared abstract
—it may or may not include abstract methods. Abstract classes cannot be instantiated, but they can be subclassed.
An abstract method is a method that is declared without an implementation (without braces, and followed by a semicolon), like this:
abstract void moveTo(double deltaX, double deltaY);
If a class includes abstract methods, the class itself must be declared abstract
, as in:
public abstract class GraphicObject { // declare fields // declare non-abstract methods abstract void draw(); }
When an abstract class is subclassed, the subclass usually provides implementations for all of the abstract methods in its parent class. However, if it does not, the subclass must also be declared abstract
.
Note: All of the methods in an interface (see the
Interfaces
section) are implicitly abstract, so the abstract
modifier is not used with interface methods (it could be—it's just not necessary).
Abstract Classes versus Interfaces
Unlike interfaces, abstract classes can contain fields that are not static
and final
, and they can contain implemented methods. Such abstract classes are similar to interfaces, except that they provide a partial implementation, leaving it to subclasses to complete the implementation. If an abstract class contains only abstract method declarations, it should be declared as an interface instead.
Multiple interfaces can be implemented by classes anywhere in the class hierarchy, whether or not they are related to one another in any way. Think of Comparable
or Cloneable
, for example.
By comparison, abstract classes are most commonly subclassed to share pieces of implementation. A single abstract class is subclassed by similar classes that have a lot in common (the implemented parts of the abstract class), but also have some differences (the abstract methods).
An Abstract Class Example
In an object-oriented drawing application, you can draw circles, rectangles, lines, Bezier curves, and many other graphic objects. These objects all have certain states (for example: position, orientation, line color, fill color) and behaviors (for example: moveTo, rotate, resize, draw) in common. Some of these states and behaviors are the same for all graphic objects—for example: position, fill color, and moveTo. Others require different implementations—for example, resize or draw. All GraphicObject
s must know how to draw or resize themselves; they just differ in how they do it. This is a perfect situation for an abstract superclass. You can take advantage of the similarities and declare all the graphic objects to inherit from the same abstract parent object—for example, GraphicObject
, as shown in the following figure.
Classes Rectangle, Line, Bezier, and Circle inherit from GraphicObject
First, you declare an abstract class, GraphicObject
, to provide member variables and methods that are wholly shared by all subclasses, such as the current position and the moveTo
method. GraphicObject
also declares abstract methods for methods, such as draw
or resize
, that need to be implemented by all subclasses but must be implemented in different ways. The GraphicObject
class can look something like this:
abstract class GraphicObject { int x, y; ... void moveTo(int newX, int newY) { ... } abstract void draw(); abstract void resize(); }
Each non-abstract subclass of GraphicObject
, such as Circle
and Rectangle
, must provide implementations for the draw
and resize
methods:
class Circle extends GraphicObject { void draw() { ... } void resize() { ... } } class Rectangle extends GraphicObject { void draw() { ... } void resize() { ... } }
When an Abstract Class Implements an Interface
In the section on Interfaces
, it was noted that a class that implements an interface must implement all of the interface's methods. It is possible, however, to define a class that does not implement all of the interface methods, provided that the class is declared to be abstract
. For example,
abstract class X implements Y { // implements all but one method of Y } class XX extends X { // implements the remaining method in Y }
In this case, class X
must be abstract
because it does not fully implement Y
, but class XX
does, in fact, implement Y
.
Class Members
An abstract class may have static
fields and static
methods. You can use these static members with a class reference—for example, AbstractClass.staticMethod()
—as you would with any other class.