Creating Abstract Classes
Introducing Abstract Classes
A subclass can override an inherited method defined in a parent class. Overriding a method potentially changes the behavior of a method in the parent class.
We use abstract classes when we want to define a class that other developers can extend and use, but you want them to specify particular types.
We can ensure that every class that extends it is required to provide its own overriden version.
An abstract class is one that cannot be instantiated and may contain abstract methods.
An abstract method is one that does not define an implementation when it’s declared.
Both of them are denoted with the abstract
keyword.
abstract class Bird {
public abstract String getName();
public void printName() {
sout(getName());
}
}
public class Stork extends Bird {
public String getName() { return "Stork!"; }
public static void main(String[] args) {
new Stork().printName();
}
}
The class Stork
must now override abstract getName()
. The following implementation without it, wouldn’t compile.
public class Stork extends Bird {} // DOES NOT COMPILE
An abstract class is most commonly used when you want another class to inherit properties of a particular class, but you want the subclass to fill in some of the implementation details.
Defining Abstract Methods
An abstract class may include non-abstract methods. It may also include all kind of members such as variables, static, inner classes, constructors…
An abstract class it not required to include abstract methods. The following code compiles.
public abstract class Llama {
public void chet() {}
}
// this is also fine
abstract public class Llama {
public void chet() {}
}
abstract public class Llama {
// this is also fine
abstract public void chet() {}
}
The modifier cannot be placed after the class
keyword nor after the return type in a method.
public class abstract Jackal { // DOES NOT COMPILE
public int abstract howl(); // DOES NOT COMPILE
}
An abstract method can only be defined in an abstract class, or an interface.
public class Egret { // DOES NOT COMPILE
public abstract void peck();
}
(!) If you see a class that contains an abstract method, make sure the class is marked as abstract
. This modifier can be placed before or after the access modifier. (!)
It’s also not possible to define an abstract
method that has a body, or default implementation. As long as you don’t mark the method as final
, the subclass has the option to override an inherited method.
Constructors in Abstract Classes
abstract
classes cannot be instantiated, but they’re still able to be initialized through constructors by their subclasses.
abstract class Bear {
abstract CharSequence chew();
public Bear() {
sout(chew());
}
}
public class Panda extends Bear {
String chew() { return "yummy!"; }
public static void main(String... args) {
new Panda();
}
}
This will print yummy!
on runtime.
The main difference between constructors in an abstract
class and a non-abstract one, is that the constructor in the abstract class can be called only when it’s being initialized by a non-abstract subclass. This makes sense, as abstract classes cannot be instantiated.
Invalid Abstract Method Declarations
Why don’t compile any of the following methods?
public abstract class Turtle {
public abstract long eat()
public abstract void swim() {};
public abstract int getAge() { return 10; }
public void sleep;
public void goInShell();
}
eat()
does not compile because it’s missing ;
swim()
and getAge()
are marked as abstract
but implement a body for the method. An abstract
method cannot contain an implementation.
sleep
is missing parentheses.
goInShell()
is not marked as abstract
, but then it has to implement a body.
(!) If you come across a question on the exam in which a class or method is marked as abstract
, make sure the class is properly implemented before attempting to solve the problem. (!)
Invalid Modifiers
We review the abstract
modifier and which modifiers it’s not compatible with.
abstract and final Modifiers
If you mark something abstract, you’re intending for someone else to extend or implement it.
If you mark something final, you’re preventing anyone from extending or implementing it. These concepts are in direct conflict with each other.
public abstract final class Tortoise { // DOES NOT COMPILE
public abstract final void walk(); // DOES NOT COMPILE
}
abstract and private Modifiers
A method cannot be marked both as abstract and private. How would you define a subclass that implements a required method if the method is not inherited by the subclass? You can’t.
public abstract class Whale {
private abstract void sing(); // DOES NOT COMPILE
}
public class HumpbackWhale extends Whale {
private void sing() {
sout("X");
}
}
The sing()
method defined in the parent is not visible to the subclass.
abstract and static Modifiers
A static
method cannot be overriden. It’s defined as belonging to the class, not an instance of the class. If it cannot be overriden, then it also cannot be marked abstract
since it could never be implemented.
abstract class Hippopotamus {
abstract static void swim(); // DOES NOT COMPILE
}
Creating a Concrete Class
A concrete class is a non-abstract class. The first concrete subclass that extends an abstract class is required to implement all inherited abstract
methods.
(!) When you see a concrete class extending an abstract class on the exam, check to make sure that it implements all of the required abstract methods. (!)
public abstract class Animal {
public abstract String getName();
}
public class Walrus extends Animal { // DOES NOT COMPILE
}
We highlight the first concrete subclass for a reason. An abstract class can extend a non-abstract class, and vice versa. Any time a concrete class is extending an abstract class, it must implement all of the methods that are inherited as abstract.
This compiles fine.
abstract class Mammal {
abstract void showHorn();
abstract void eatLeaf();
}
abstract class Rhino extends Mammal {
void showHorn() {}
}
public class BlackRhino extends Rhino {
void eatLeaf() {}
}
Reviewing Abstract Class Rules
For the exam you should know the following rules.
#### Abstract Class Definition Rules
- Abstract classes cannot be instantiated.
- All top-level types, including abstract classes, cannot be marked protected or private.
- Abstract classes cannot be marked final.
- Abstract classes may include zero or more abstract and nonabstract methods.
- An abstract class that extends another abstract class inherits all of its abstract methods.
- The first concrete class that extends an abstract class must provide an implementation for all of the inherited abstract methods.
- Abstract class constructors follow the same rules for initialization as regular constructors, except they can be called only as part of the initialization of a subclass.
Abstract Method Definition Rules
- Abstract methods can be defined only in abstract classes or interfaces.
- Abstract methods cannot be declared private or final.
- Abstract methods must not provide a method body/implementation in the abstract class in which they’re declared.
- Implementing an abstract method in a subclass follows the same ruels for overriding a method, including covariant return types, exception declarations etc.
Implementing Interfaces
Java allows a class to implement any number of interfaces. An interface is an abstract data type that declares a list of abstract methods that any class implementing the interface must provide. It can also include constant variables.
Both abstract methods and constant variables included with an interface are implicitly assumed to be public.
With Java 8, interfaces were updated to include static
and default
methods. A default method is one in which the interface method has a body and is not marked abstract
. It was added for backward compatibility.
In Java 9, interfaces were updated to support private
and private static
methods. Both of these types were added for code reusability.
Defining an interface
Interface variables are referred to as constants, because they’re assumed to be public
, static
, and final
. They’re initialized with a constant value when they’re declared.
Interfaces are not required to define any method.
public abstract interface WalksOnTwoLegs {}
An interface cannot be instantiated.
var e = new WalksOnTwoLegs(); // DOES NOT COMPILE
Interfaces cannot be marked as final
.
public final interface WalksOnEightLegs {} // DOES NOT COMPILE
How do you use an interface?
interface Climb {
Number getSpeed(int age);
}
public class FieldMouse implements Climb {
public Float getSpeed(int age) {
return 11f;
}
}
The access modifier of the interface method is assumed to be public, although the concrete class must explicitly declare it.
If any of the interfaces implemented define abstract
methods, then the concrete class is required to override them.
Like a class, an interface can extend another interface using extends
. An interface, unlike a class, may extend multiple interfaces at once (a class may only extend one class).
interface Nocturnal {
public int hunt();
}
interface CanFly {
public void flap();
}
interface HasBigEyes extends Nocturnal, CanFly {}
Interfaces, unlike abstract classes, do not contain constructors and are not part of instance initialization. Interfaces simply define a set of rules that a class implementing them must follow.
- A Java file may have at most one public top-level class or interface
- A top-level class or interface can only be declared with
public
or package-private access.
What about Enums?
An enum is a specialized type that defines a set of fixed values. It’s declared with enum
.
public enum Color {
RED, YELOW, BLUE, GREEN;
}
Like classes and interfaces, enums can have more complex formations including methods, private constructors and instance variables.
Inserting Implicit Modifiers
An implicit modifier is one that the compiler will automatically insert.
- Interfaces are assumed to be
abstract
. - Interface variables are assumed to be
public
,static
, andfinal
. - Interface methods without a body are assumed to be
abstract
, andpublic
.
The following two interface definitions are equivalent, as the compiler will convert them both.
public interface Soar {
int MAX_HEIGHT = 10;
final static boolean UNDERWATER = true;
void fly(int speed);
abstract void takeoff();
public abstract double dive();
}
public interface Soar {
public static final int MAX_HEIGHT = 10;
public final static boolean UNDERWATER = true;
public abstract void fly(int speed);
public abstract void takeoff();
public abstract double dive();
}
Conflicting Modifiers
If a developer wants to mark a method or variable with an invalid modifier such as private
or protected
, it won’t compile, as the compiler will apply the public
modifier to both.
public interface Dance {
private int count = 4; // DOES NOT COMPILE
protected void step(); // DOES NOT COMPILE
}
What about package-private? When working with interface members, the lack of access modifier always indicates public access, unlike classes.
(!) Spot compiler errors here (!)
1: private final interface Crawl {
2: String distance;
3: private int MAXIMUM_DEPTH = 100;
4: protected abstract boolean UNDERWATER = false;
5: private void dig(int depth);
6: protected abstract double depth();
7: public final void surface();
}
Line 1 doesn’t compile because it’s marked final
, which cannot be applied to an interface. It’s also marked as private
, which conflicts for top-level interfaces.
Line 2, because distance
is not initialized.
Line 3 and 4, because they’re assumed to be public
. Line 4 also again because variables cannot be marked abstract.
Line 5 and 6, because all interface abstract methods are assumed to be public
.
Line 7, because it’s marked as final
and methods without a body are assumed to be abstract, so it cannot be final.
Differences between Interfaces and Abstract Classes
Even though both are considered abstract types, only interfaces make use of implicit modifiers.
abstract class Husky {
abstract void play();
}
interface Poodle {
void play();
}
Both of these definitions are considered abstract, although Husky
will not compile if play()
is not marked as abstract
, whereas the method in Poodle
will compile fine either way.
They also don’t have the same access level. play()
from Husky
is considered package-private, whereas play()
from Poodle
is assumed to be public
.
class Webby extends Husky {
void play() {}
}
class Georgette implements Poodle {
void play() {}
}
The class Webby
compiles fine, but Georgette
does not as it breaks overriding rules. It reduces the visibility from the method play()
from public
in Poodle
to package-private in Georgette
.
Inheriting an Interface
An interface can be inherited as
- An interface can extend another interface.
- A class can implement an interface.
- A class can extend another class whose ancestor implements an interface.
When an interface is inherited, all of the abstract methods are inherited. If the type inheriting the interface is also abstract, then it’s not required to implement the interface methods. The first concrete subclass that inherits the interface must implement all of the inherited abstract methods.
Mixing Class and Interface Keywords
(!) The exam creators are fond of questions that mix class and interface terminology. (!)
Altough a class can implement
an interface, a class cannot extend
an interface. Likewise, an interface cannot implement
another interface.
public interface CanRun{}
public class Cheetah extends CanRun {} // DOES NOT COMPILE
public class Hyena {}
public interface HasFur extends Hyena {} // DOES NOT COMPILE
Duplicate Interface Method Declarations
A class may inherit from two interfaces that contain the same abstract
method.
public interface Herbivore {
public void eatPlants();
}
public interface Omnivore {
public void eatPlants();
public void eatMeat();
}
public class Bear implements Herbivore, Omnivore {
public void eatMeat() {
sout("eating meat");
}
public void eatPlants() {
sout("eating plants");
}
}
You just need to be able to create a single method that overrides both inherited abstract methods at the same time.
If they have different method signatures it’s also not a problem, as it’s considered method overloading.
The methods are duplicates, but they’re also considered compatible. The compiler can resolve the differences between the two declarations without finding any conflicts.
If the duplicate methods have the same signature, but different return types, you need to implement it with return types that are covariant.
interface Dances {
String swingArms();
}
interface EatsFish {
CharSequence swingArms();
}
public class Penguin implements Dances, EatsFish {
// covariant return type
public String swingArms() {
return "Swing!";
}
}
If the return type is not covariant it won’t compile.
interface Dances {
int countMoves();
}
interface EatsFish {
boolean countMoves();
}
public class Penguin implements Dances, EatFish { // DOES NOT COMPILE
}
The same happens for a class or an interface.
interface LongEars {
int softSkin();
}
interface LongNose {
void softSkin();
}
// DOES NOT COMPILE
interface Donkey extends LongEars, LongNose {}
// DOES NOT COMPILE
abstract class Aardvark implements LongEars, LongNose {}
Polymorphism and Interfaces
Abstract Reference Types
When working with abstract types, you may prefer to work with the abstract reference type, rather than the concrete class.
import java.util.*;
public class Zoo {
public void sortAndPrintZooAnimals(List<String> animals) {
Collections.sort(animals);
for(String a : animals) {
sout(a);
}
}
}
The input may be any type of list, such as ArrayList
or LinkedList
instead of List
.
Casting Interfaces
If you need access to a method that’s only declared in the concrete subclass, then you will need to cast the interface reference to that type, assuming the cast is supported at runtime.
The following is not permitted as the compiler detects they’re not related.
String l = "Bert";
Long t = (Long) l; // DOES NOT COMPILE
With interfaces, there’re limitations as to what the compiler can validate
interface Canine {}
class Dog implements Canine {}
class Wolf implements Canine {}
public class BadCasts {
public static void main(String... args) {
Canine canine = new Wolf();
Canine badDog = (Dog) canine;
}
}
This compiles, but it will throw a ClassCastException
at runtime.
Interfaces and the instanceof Operator
The compiler has limited ability to report an error if two interfaces are related, because even though a reference type may not implement an interface, one of its subclasses could.
// COMPILES
Number tickets = 5;
if(tickets instanceof List) {}
A subclass that fits
public class MyNumber extends Number implements List {}
That said, the compiler can check for unrelated interfaces if this reference is a class that’s marked final
.
Integer tickets = 6;
if(tickets instanceof List) {} // DOES NOT COMPILE
Reviewing Interface Rules
Interface Definition Rules
- Interfaces cannot be instantiated.
- All top-level types, including interfaces, cannot be marked
protected
orprivate
. - Interfaces are assumed to be
abstract
and cannot be markedfinal
. - Interfaces may include zero or more
abstract
methods. - An interface can extend any number of interfaces.
- An interface reference may be cast to any reference that inherits the interface, although this may produce an exception at runtime if the classes aren’t related.
- The compiler will only report an unrelated type error for an
instanceof
operation with an interface on the right side if the reference on the left side is afinal
class that does not inherit the interface. - An interface method with a body must be marked
default
,private
,static
orprivate static
.
Abstract Interface Method Rules
- Abstract methods can be defined only in abstract classes or interfaces.
- Abstract methods cannot be declared
private
orfinal
. - Abstract methods must not provide a method body/implementation in the abstract class in which it’s declared.
- Implementing an abstract method in a subclass follows the same rules for overriding a method, including covariant return types, exception declaretions, etc.
- Interface methods without a body are assumed to be
abstract
andpublic
.
The first 4 rules for abstract methods, whether they be defined in abstract classes or interfaces are exactly the same.
Interface Variable Rules
- Interface variables are assumed to be
public
,static
, andfinal
- Because interface variables are marked
final
, they must be initialized with a value when they’re declared.
The primary difference between interfaces and abstract classes are that interfaces
- Include implicit modifiers
- Do not contain constructors
- Do not participate in the instance initialization process
- Support multiple inheritance
Introducing Inner Classes
Defining a Member Inner Class
A member inner class is a class defined at the member level of a class, or the same level as methods, instance variables, and constructors.
It is the opposite of a top-level class, in that it cannot be declared unless it’s inside another class.
This is useful is the relationship between the two classes is very close.
public class Zoo {
private interface Paper {}
public class Ticket implements Paper {}
}
While top-level classes and interfaces can only be set with public
or package-private access, member inner classes don’t have the same restriction.
A member inner class can be declared with all access modifiers. Some members are disallowed in them though, such as static
members.
public class Zoo {
private interface Paper {
public String getId();
}
public class Ticket implements Paper {
private String serialNumber;
public String getId() { return serialNumber; }
}
}
Using a Member Inner Class
They can be used by calling it in the outer class.
public class Zoo {
private interface Paper {
public String getId();
}
public class Ticket implements Paper {
private String serialNumber;
public String getId() { return serialNumber; }
}
public Ticket sellTicket(String serialNumber) {
var t = new Ticket();
t.serialNumber = serialNumber;
return t;
}
}
The advantage here, is that Zoo
completely manages the lifecycle of Ticket
.
Summary
In this chapter, we presented advanced topics in class design, starting with abstract classes. An abstract class is just like a regular class except that it cannot be instantiated and may contain abstract methods. An abstract class can extend a nonabstract class, and vice versa. Abstract classes can be used to define a framework that other developers write subclasses against.
An abstract method is one that does not include a body when it is declared. An abstract method may be placed inside an abstract class or interface. Next, an abstract method can be overridden with another abstract declaration or a concrete implementation, provided the rules for overriding methods are followed. The first concrete class must implement all of the inherited abstract methods, whether they are inherited from an abstract class or interface.
An interface is a special type of abstract structure that primarily contains abstract methods and constant variables. Interfaces include implicit modifiers, which are modifiers that the compiler will automatically apply to the interface declaration. For the 1Z0-815 exam, you should know which modifiers are assumed in interfaces and be able to spot potential conflicts. When you prepare for the 1Z0-816 exam, you will study the four additional nonabstract methods that interfaces now support. Finally, while the compiler can often prevent casting to unrelated types, it has limited ability to prevent invalid casts when working with interfaces.
We concluded this chapter with a brief presentation of member inner classes. For the exam, you should be able to recognize member inner classes and know which access modifiers are allowed. Member inner classes, along with the other types of nested classes, will be covered in much more detail when you study for the 1Z0-816 exam.
Exam Essentials
Be able to write code that creates and extends abstract classes. In Java, classes and methodscan be declared as abstract. An abstract class cannot be instantiated. An instance of an abstract class can be obtained only through a concrete subclass. Abstract classes can include any number, including zero, of abstract and nonabstract methods. Abstract methods follow all the method override rules and may be defined only within abstract classes. The first concrete subclass of an abstract class must implement all the inherited methods. Abstract classes and methods may not be marked as final.
Be able to write code that creates, extends, and implements interfaces. Interfaces are specialized abstract types that focus on abstract methods and constant variables. An interface may extend any number of interfaces and, in doing so, inherits their abstract methods. An interface cannot extend a class, nor can a class extend an interface. A class may implement any number of interfaces.
Know the implicit modifiers that the compiler will automatically apply to an interface. All interfaces are assumed to be abstract. An interface method without a body is assumed to be public and abstract. An interface variable is assumed to be public, static, and final and initialized with a value when it is declared. Using a modifier that conflicts with one of these implicit modifiers will result in a compiler error.
Distinguish between top-level and inner classes/interfaces and know which access modifiers are allowed. A top-level class or interface is one that is not defined within another class declaration, while an inner class or interface is one defined within another class. Inner classes can be marked public, protected, package-private, or private.