Advanced Class Design

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

  1. Abstract classes cannot be instantiated.
  2. All top-level types, including abstract classes, cannot be marked protected or private.
  3. Abstract classes cannot be marked final.
  4. Abstract classes may include zero or more abstract and nonabstract methods.
  5. An abstract class that extends another abstract class inherits all of its abstract methods.
  6. The first concrete class that extends an abstract class must provide an implementation for all of the inherited abstract methods.
  7. 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

  1. Abstract methods can be defined only in abstract classes or interfaces.
  2. Abstract methods cannot be declared private or final.
  3. Abstract methods must not provide a method body/implementation in the abstract class in which they’re declared.
  4. 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, and final.
  • Interface methods without a body are assumed to be abstract, and public.

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

  1. Interfaces cannot be instantiated.
  2. All top-level types, including interfaces, cannot be marked protected or private.
  3. Interfaces are assumed to be abstract and cannot be marked final.
  4. Interfaces may include zero or more abstract methods.
  5. An interface can extend any number of interfaces.
  6. 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.
  7. 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 a final class that does not inherit the interface.
  8. An interface method with a body must be marked default, private, static or private static.

Abstract Interface Method Rules

  1. Abstract methods can be defined only in abstract classes or interfaces.
  2. Abstract methods cannot be declared private or final.
  3. Abstract methods must not provide a method body/implementation in the abstract class in which it’s declared.
  4. Implementing an abstract method in a subclass follows the same rules for overriding a method, including covariant return types, exception declaretions, etc.
  5. Interface methods without a body are assumed to be abstract and public.

The first 4 rules for abstract methods, whether they be defined in abstract classes or interfaces are exactly the same.

Interface Variable Rules

  1. Interface variables are assumed to be public, static, and final
  2. 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.