Interfaces and Inheritance Subtyping ​
Inheritance
Inheritance is a mechanism where you derive a class from another class for a hierarchy of classes that share a set of attributes and methods.
Benefits of Inheritance ​
Inheritance minimizes the amount of duplicate code in software by sharing common code amongst classes: When the same code exists in two classes, The common code can be added to a superclass that the two classes inherit from. This also makes software easier to maintain since when a change is necessary it only has to be applied in one class instead of two.
Inheritance can also make application code more flexible to change because classes that inherit from a common superclass can be used interchangeably.
Class Hierarchy ​
Example ​
If the game code uses variables of type Item, say, for keeping an Inventory, then it is easy to add more types of “Items” later on, without changing the code for the Inventory.
Because classes that inherit from Item can be used interchangeably with Item.
JavaFX Class Hierarchy ​
Libraries like JavaFX contain many classes. Typically, the “base code” is shared: held in common base classes like Node, or Shape. For example
- A
Nodehas alocation,rotation,scale - A
Shapeisfilledor notfilledAs a result of this class hierarchy all classes have alocation,rotationandscalesince they extendNode.
Inheritance ​
Syntax ​
// Extends Syntax
class MountainBike extends Bicycle {
// new fields and methods for MountainBike.
// MountainBike is the subclass
// Bicycle is the superclass.
}Diagram for the entire structure:
Extending Classes ​
When extending from a class you can access the fields and methods of that class and override methods to change their implementation.
When writing the code for a superclass, you can control whether the classes that extend the superclass will be able to access it's methods and fields:
- methods of the child class cannot access
privatefields/methods of the parent class. - If a method of the superclass you are writing should not be overridden you can mark it as
final. - If the class should not be extended at all you can mark the entire class as
final.
If there are two fields with the same name in a subclass and superclass you can differentiate the two with this.fieldName and super.fieldName respectively.
If there are two methods with the same signature in a subclass and superclass, the method in the subclass is overriding the method in the superclass which means that you are giving the method a different implementation from the superclass. You can access both implementations in the subclass with this.methodName() and super.methodName() respectively.
thisfor the current object andsuperfor fields/methods of the parent classsuper()in the constructor invokes the constructor of the parent class.
Inheritance Example ​
// Inheritance Example
public class Item {
private Room place;
public Item(Room place) {
this.place = place;
}
public Room getPlace() {
return this.place;
}
public boolean isPortable() {
return false;
}
}
public class Key extends Item {
private Door door;
public Key(Room place, Door door) {
super(place); // calls the constructor of the superclass: Item
this.door = door; // sets a value for door.
}
@Override
public boolean isPortable() {
return true; // overriding a method of a superclass to have a different result.
}
public boolean opens(Door door) {
return this.door.equals(door);
}
}Polymorphism ​
Polymorphism
Polymorphism is an OOP feature that allows sub-classes to have methods of the same name (of the same superclass) but with a different implementation.
Example ​
// Polymorphism Example
class Animal {
public void animalSound() {
System.out.println("The animal makes a sound"); // original implementation.
}
}
class Pig extends Animal {
@Override
public void animalSound() {
System.out.println("The pig says: wee wee"); // different implementation for Pig
}
}
class Dog extends Animal {
@Override
public void animalSound() {
System.out.println("The dog says: bow wow"); // different implementation for Dog.
}
}Overloading vs Overriding ​
Overloading (also called static polymorphism)
- Methods in a class with the same name but different signature (different number or type of parameters)
- Use sparingly, and only if confusion is unlikely
Overriding (also called dynamic polymorphism)
- Methods of a subclass with the same signature of a method of the parent class
- Use
@Overridetags in front of overriding method (not when overloading) - Improves maintainability: fewer mistakes! Good practice
Constructors ​
Method without a return type, with the same name as the class that creates a new object of the class.
- Constructors can be overloaded but are not inherited
- All classes have a constructor
- When omitted, the class has an implicit (default) empty-argument constructor
- Every constructor calls another constructor
- When omitted, implicitly calls super() (most frequent case)
- Only valid if superclass has a constructor without parameters!
Good practices
- Constructor initializes all fields that need a non-default value
- Constructor does not perform extensive computation
Example ​
public class Point2D {
protected Point2D() {
// empty
}
public Point2D(int x, int y) {
this(); // calls empty constructor.
move(x, y);
}
}
public class Point3D extends Point2D {
public Point3D() {
// empty
}
public Point3D(int x, int y, int z) {
super(x, y); // calls Point2D constructor
this.z = z;
}
}Abstract Methods And Classes ​
An abstract method is a method without a body
public abstract class SomeAbstractClass {
public abstract void doSomething(int someNumber);
protected abstract double computeSomething(int numberOne, double numberTwo);
}abstractmethods must be in anabstractclass.abstractclasses are “incomplete”/“partial” and typically contain “base” functionality- Subclasses must either implement the
abstractmethods or also beabstract - You cannot instantiate an
abstractclass but anabstractclass has a constructor
Interfaces ​
An interface is a special type: only has specification, no implementation.
- All variables are
public final static - Methods in interfaces do not have a body, unless they are marked with
default. - Classes can
extendone class andimplementmultiple interfaces - Interfaces can
extendmultiple interfaces
Syntax ​
interface MyInterface {
public static int myInt = 0;
/**
* Specification 1 ...
*/
public void myMethod1(); // <-- a semicolon instead of the method body
/**
* Specification 2 ...
*/
public int myMethod2(int i, int j);
}Implementation Example ​
interface Item {
Room getPlace();
boolean isPortable();
}
public class Key implements Item {
private Room place;
private Door door;
public Key(Door door) {
this.door = door;
}
public Room getPlace() {
return place;
}
public boolean isPortable() {
return true;
}
public boolean opens(Door door) {
return this.door.equals(door);
}
}implemented methods have the same signature(method names, result, parameter types) and visibility
implementation has additional methods, fields and constructor.
Java Data-structures implementation ​
Java has no native list data structures, only arrays. The Collections library includes many useful data structures including Lists.
- All list types have a common interface
List Listdefines many methods. Some of them are:
interface List {
boolean add(Object e);
boolean remove(Object e);
boolean contains(Object e);
Object get(int index);
Object set(int index, Object e);
Object remove(int index);
int size();
}UML Notation ​
For implementing an interface a dotted arrow is drawn instead of the solid arrow for extending.
Combining Interfaces And Inheritance ​
Since interfaces extend from each other the correct notation in uml is a solid arrow from the subinterface to the superinterface.
Multiple Inheritance ​
- In many programming languages, a class can extend multiple classes (ex. C++)
- This leads to the diamond problem:
In the Werewolf class the speak() method is ambiguous.
Inheritance vs Interface. ​
| Feature | Interface | Class | abstract Class |
|---|---|---|---|
| method implementation necessary | no | yes | no if abstract method. |
| method implementation possible | limited; with default. | yes | yes |
| non-final fields | no | yes | yes |
| private/protected members | no | yes | yes |
Options for extending/implenting:
- Classes can implement multiple interfaces
- Extending interfaces can be combined with extending one class.
Purpose of abstract classes vs interfaces:
- Abstract classes are a basis for subclasses with shared behaviour
- Interfaces are specifications, describing the behavior an implementing class will have.
Inheritance vs Composition ​
Also called the OOP composite reuse principle.
composite reuse principle
is the principle that classes should achieve polymorphic behavior and code reuse by their composition (by containing instances of other classes that implement the desired functionality) rather than inheritance from a base or parent class. (Wikipedia)
| Composition | Inheritance |
|---|---|
| rely on other object(s) to provide (some) functionality | inherit fields and methods from another class |
| Composition is often more appropriate | Use when there is a clear parent-child relationship of concepts (is-a relationship) |
| Use when only using parts of the functionality of another class (has-a or uses-a relationship) | Use to alter the behavior of a class |
| Use when you want to reuse the entire interface of the superclass |
Object Class ​
All classes are derived from the base class Object. All classes inherit its methods:
public boolean equals(Object o) // check for “equality”
public int hashCode() // compute unique representation.
public String toString() // create a String representation- These methods have a default implementation.
- Often it is a good idea to override these inherited methods
- The default
toString()method returns a String that represents the internal reference to the instance:SomeObjectClassname@hashcodenumber.
Types ​
Reference types (Objects) carry a subtyping relation: one type can be a subtype of another.
Subtyping is a partial order (meaning reflexive and transitive)
- Reflexive: every type is a subtype of itself:
Ais a subtype ofA - Transitive: if
Ais a subtype ofBandBis a subtype ofC, thenAis a subtype ofC. - if
Sis a subtype ofT, then we useSwhenTis expected (Whenever a value of a given type is expected, a subtype can be used)
Subtyping in Java ​
In each of these cases the class B is a subtype of A. And therefore B can be used when A is expected.
class B implements A ​
implements keyword should be used.
class B extends A ​
extends keyword should be used.
interface B extends A ​
extends keyword should be used.
Static vs Dynamic Type Of an Expression ​
Static type: that which the compiler can infer during “compile-time”, Also called declared type.
The Java compiler will not do (potentially complicated) type inferencing: it will treat variables as Type when they are declared as Type even if at runtime it will contain a different type.
Dynamic type: that which the value actually has during “run-time”, Also called actual type or run-time type.
- The dynamic type is always a subtype of the static type
- What the compiler considers correct is based on the static type.
Dynamic Type Test ​
How to find out the dynamic type of an expression expr during run time?
- Type test:
instanceof Type - This yields true if the dynamic type of an expression is a subtype of
Type null instanceof Typeis alwaysfalse.
Key k1 = new Key();
Item i1 = k1;
Item i2 = new FireArm();
Item i3 = null;
System.out.println(k1 instanceof Key); // true
System.out.println(k1 instanceof Item); // true
System.out.println(i1 instanceof Key); // true
System.out.println(i1 instanceof Item); // true
System.out.println(i2 instanceof Key); // false
System.out.println(i2 instanceof Item); // true
System.out.println(i3 instanceof Item); // falseCasting ​
How to tell the compiler the actual type of an expression expr? To be able to call methods that are not available for the super type.
Type cast: (Type) expr changes the static type to Type
- Only correct if dynamic type of expr is a subtype of Type
- You cannot change the dynamic type of an expression
Watch the parentheses
((Key) i1).opens(door)is correct:((Key) i1)has type “Key”.(Key) i1.opens(door)is wrong: tries to cast the result from the method.
Example ​
public class Player {
private Item item;
// other code
/** Tests if item is a Key. */
public boolean hasKey() {
return item instanceof Key;
}
/** Returns the item if it is a key, otherwise null. */
public Key getKey() {
return item instanceof Key ? (Key) item : null;
}
/** Fires the firearm, if item is a firearm. */
public boolean fire() {
return item instanceof FireArm && ((FireArm) item).fire();
}
}