OCA Exam Preparation: Inheritance - Review
OCA Exam Preparation: Inheritance - Review
INHERITANCE –
REVIEW
ABBREVIATIONS & ACRONYMS
4
INHERITANCE
A Java class may inherit a single class only, with all classes eventually extending the
class java.lang.Object, which has no parent.
While Java does not allow true multiple inheritance, its limited form is emulated
through interfaces because classes are allowed to implement multiple interfaces,
each of which, in its own turn, may also extend multiple interfaces.
Corollary: An interface extends an interface, while a class extends a class or
implements an interface.
Inheritance enables to reuse existing code by allowing a class to inherit the properties
and behavior of another class; one of the most useful aspects of inheritance is
polymorphism, which makes the code more flexible and dynamic.
5
INHERITANCE, cont’d
EXTRA: Below are some of the final core Java classes that are featured on the exam:
String;
StringBuilder;
All primitive wrappers (namely, Boolean, Character, Byte, Short, Integer, Long,
Float, and Double);
All LDTs;
java.lang.System is also final.
A Java class inherits all of its parent’s non-private members, although the package-
private members can be accessed from inside the same package only.
Known trap on the exam: private methods and private variables are not
inherited.
Be on alert for every class that defines all kinds of constructors except a no-arg one: its
child will be locked out of the inheritance (already discussed in Exam Objective 6.3).
EXTRA: Constructors as well as static and instance initers aren’t inherited.
6
7.2
POLYMORPHISM
7
POLYMORPHISM
An object may take on different forms, and its behavior will depend in part on the
reference that points to the object. Unlike methods and variables that are hidden,
overridden methods will be replaced by their overriding counterparts at the run time.
Only inheritable methods can be overridden → private methods can be only hidden.
8
POLYMORPHISM, cont’d
Corollaries: The return type of the overriding method must match exactly the return
type of the overridden method if the return type is a primitive.
If the methods return objects, the return type of the overriding method may
be a subclass of the return type of the overridden method.
EXTRA: static over non-static and vice versa between classes never compiles:
class A {
static void m1() {}
void m2(){}
}
class B extends A{
// void m1() {} // INVALID: overridden method is static
// static void m2(){} // INVALID: overriding method is static
} 9
POLYMORPHISM, cont’d
Actype vs reftype:
Actype – the type of the actual object at run time – determines which properties exist within the
object in memory.
Reftype – the type of the reference to the object – specifies which methods and vars are
accessible.
Combat version: Reftype dictates what’s available while actype says what runs.
On the exam: See a new object? Assess its reftype first: it’ll give you an idea what the object is
capable of and what is out of its reach.
EXTRA: When ancestor’s var is not hidden and gets re-assigned in the descendant
class, the var’s value changes in the ancestor class, too.
Corollary: Leads to a comperr if the var is final.
class Grandpa {
// final String s = "Grandpa"; // won’t let the code compile
String s = "Grandpa";
}
class Parent extends Grandpa { }
class Child extends Parent {
void run(){
s = "Child"; // here s isn’t hidden,
// it is simply re-assigned
System.out.println(s + " "
+ this.s + " "
+ super.s + " "
+ new Parent().s); // Child Child Child Grandpa
}
public static void main(String[] args) {
new Child().run();
}
}
Were the var s static, the output would’ve been Child Child Child Child. 11
POLYMORPHISM, cont’d
13
CASTING
Combat version:
See no cast when reftype is narrower than actype? → comperr!
14
CASTING, cont’d
Lots and lots of study guides unconditionally insist that the compiler won’t allow casts to
unrelated types, period. There’s, however, an exception that has to do with interfaces:
A cast between unrelated class types is always illegal… ← important for the exam
class Test1 {
public static void main(String[] args) {
Short sH = new Short("1");
// Integer iN = (Integer) sH; // class - class; INVALID
Number nU = sH;
}
}
EXTRA: Casting a non-final type to unrelated interface type is always valid:
interface I1 {}
interface I2 {}
class Test2 { // Test ain’t final otherwise comperr
public static void main(String[] args) {
Test2 t = new Test2(); // btw, t could be final
I1 i1 = (I1) t; // class - interface; throws CCE
I2 i2 = (I2) i1; // interface - interface; throws CCE
}
} 15
CASTING, cont’d
Corollary: A cast between two unrelated interfaces through a non-final class always compiles.
Even after an explicit and valid cast the JVM may well throw a CastClassException at run time
if the object being cast is not actually an instance of the target class:
class Parent {}
class Child extends Parent {}
class Test {
public static void main(String[] args) {
String s = "hello";
Object o = s;
StringBuilder sb = (StringBuilder) o; // VALID but throws CCE
Parent p = new Parent();
Child c = (Child) p; // VALID but throws CCE
}
}
Making both references point to the same object ensures that the downcast runs
successfully:
class Parent{}
class Child extends Parent {
public static void main(String[] args) {
Parent p = new Parent();
Child c = new Child();
p = c; // if commented out, leads to CCE 16
c = (Child) p;
}}
CASTING, cont’d
EXTRA: Casting can be used not only to make certain fields or methods available to the refvar,
but also to resolve an ambiguous reference:
interface I1{ int A = 10; }
interface I2{ int A = 20; }
17
CASTING, cont’d
EXTRA: Casting also helps to get hold of a carefully concealed member somewhere up the
hierarchical chain:
class A {
public String str = "Hello from A!";
}
class B extends A {
private String str = "Hello from B!";
}
class C extends B{}
class Test{
public static void main(String args[]){
C c = new C();
// System.out.println(c.str); // INVALID
System.out.println( ((A)c).str ); // Hello from A!
}
}
18
7.4
USING super AND this
19
USING super AND this
The keywords super and this are reference variables that are defined and initialized by
the JVM itself for every object in its memory:
this always refers to the current object…
… whereas super denotes the object’s supertype;
Known trap on the exam: Both this and super point to an instance → an attempt to
access them from a static context such as static method,
static initer or declaration of a static var throws a comperr.
EXTRA: super and this point to the same object:
class Test{
{ if ( this.hashCode() == super.hashCode() )
System.out.println("Same"); } // prints Same
public static void main(String[] args) {
new Test();
}
}
super and this can be used to access variables, methods or constructors in the parent or
child class, respectively (ctors require parentheses, though).
EXTRA: this is final → e.g., this = new Object(); throws a comperr.
20
USING super AND this, cont’d
To call a constructor from another constructor, the form this() or super() is used.
Known trap on the exam: this() or super() must be the first stat in a constructor’s
body → two such calls back to back throw a comperr.
To access a shadowed var x within the same class, we use the form this.x, which is a
typical idiom for constructors or methods that define a local var or an arg with the same
name as an instance var:
class Test {
private Object a, b;
public Test(Object a, Object b) {
this.a = b;
this.b = b;
}
public void setField_a(Object a){ this.a = a; }
}
To access a hidden var x in the superclass, we use super.x. It is also possible to invoke an
overridden method by calling super.methodName().
21
USING super AND this, cont’d
“Shadowing is distinct from hiding, which applies only to members which would otherwise be
inherited but are not because of a declaration in a subclass.”
“A simple name may occur in contexts where it may potentially be interpreted as the name of a
variable, a type, or a package. In these situations… a variable will be chosen in preference to a
type, and that a type will be chosen in preference to a package. Thus, it is may sometimes be
impossible to refer to a visible type or package declaration via its simple name. We say that
such a declaration is obscured.” (cf. Problem 1.21.)
22
USING super AND this, cont’d
This problem, though, doesn’t occur for instance variables because variables can never
be overridden: they are hidden. So in order to access an ancestor’s hidden var, we
‛unhide’ it by using a cast, such as ((A)c).var, which gives us the var defined in A
even if it is hidden by B.
Illustration:
interface Laughable {
default void getJoke(){ System.out.println("Ha-ha-ha!"); }
static void makeJoke(){ System.out.println("A man walks into a bar..."); }
}
The keyword abstract is one of the non-access modifiers that change the default
properties of a Java class and its members.
A class can be declared abstract even if it doesn’t define any abstract methods.
An abstract type cannot be instantiated and, therefore, requires a concrete subtype for the
code to be used.
An abstract method doesn’t have a body but follows all the method overriding rules
applicable to regular methods.
The first concrete class that extends an abstract class must implement all the inherited
abstract methods.
Corollary: Abstract classes and methods cannot be declared as private or final.
An abstract class can extend a concrete class, and vice versa.
An interface is an abstract type → can’t be instantiated; known trap on the exam!
26
INTERFACES AND abstract CLASSES, cont’d
In addition to abstract methods and static final variables (in other words, constants)
that were allowed in interfaces prior to Java 1.8, now interfaces can also define default
and static methods.
On the exam: As soon as you see a var declared in an interface, be on alert! The
var is final!
Important Reminder: final reftypes behave differently from final primitives: it’s only
their references that are final, not the inner states.
Alternative wording: We can call methods on final refvars to change their fields; what’s
not allowed is re-assigning the reference itself:
import java.util.ArrayList;
import java.util.List;
class Test {
public static void main(String[] args){
final List<String> list = new ArrayList<>();
list.add("Changed!");
System.out.println(list); // prints [Changed!]
// list = new ArrayList<>(); // INVALID
}
} 27
INTERFACES AND abstract CLASSES, cont’d
All members of an interface are implicitly public ← known trap on the exam!
Any interface method that is not explicitly marked as default or static is assumed to be
abstract.
An interface cannot extend a class but can extend multiple interfaces.
A subinterface inherits all the abstract and default methods declared in its
superinterface(s).
A class cannot extend an interface but can implement multiple interfaces thus achieving
benefits of multiple inheritance.
The default methods can be overridden, abstract method are meant to be overridden, and
static methods cannot be overridden: they’ll become hidden.
On the exam: Overriding method can be abstract thus making the child class abstract.
→ It’s a potential trap!
28
INTERFACES AND abstract CLASSES, cont’d
EXTRA: static interface methods are useful for providing handy tools such as sorting, null
checks, etc. → no need to define utility classes anymore.
EXTRA: A static interface method can only be invoked by using its interface name.
interface Inter { static void run(){ System.out.println("Inter"); } }
class Test implements Inter{
public void run(){ System.out.println("Test"); }
public static void main(String[] args) {
Inter t = new Test();
// t.run(); // INVALID
Inter.run(); // Inter
((Test)t).run(); // Test
}
}
So how come we can call static methods declared in classes via object references and
cannot do the same with interfaces? Answers Brian Goetz himself:
We consider the ability to invoke a static method through an instance to be a language design error,
unfortunately one which we can’t fix retroactively for classes.
EXTRA: "Classes win over interfaces" (this one is also from Brian Goetz; 29
covered in the discussion to Problem 7.34).
INTERFACES AND abstract CLASSES, cont’d
Simple answer: "Yes" for classes and "No" for interfaces as per JLS8, §8.4.8, Inheritance,
Overriding, and Hiding:
A class inherits from its direct superclass all concrete methods (both static and instance) of the
superclass;
A class inherits from its direct superclass and direct superinterfaces all abstract and default
methods;
Corollary: default methods may collide if not overridden:
interface I1 {
default void run(){}
}
interface I2 {
default void run(){}
}
// interface I3 extends I1, I2 {} // INVALID
interface I4 extends I1, I2 {
default void run(){}
EXTRA: A class does not inherit static methods from its superinterfaces. 30
INTERFACES AND abstract CLASSES, cont’d
Reminder: Just like classes, interfaces can also be inaccessible from outside their packages (it’s
only their members that are implicitly public) → so watch out if the code tries to peek
into different packages:
package one;
interface Inter{}
package two;
import one.*;
31
RECAPPING
3. ANY other override is valid EXCEPT the ‛abstract static’ combos which are illegal in
the first place.
* leaving out the stuff related to return types and exceptions
4. When it comes to vars, static may hide non-static, and vice versa.
32
RECAPPING, cont’d
Remark: Note that Rule 1 holds true even in case of an implementation via inheritance:
interface I{
// static void run() { System.out.println("Inter static"); }
// default void run(){ System.out.println("Inter default"); }
void run();
}
class Parent { // ***********************************
public static void run(){ // * the real culprit lives here *
System.out.println("Parent"); // ***********************************
}}
class Child extends Parent implements I { // INVALID: ’overriding method is static’
// public void run(){ // even commented out so that Child wouldn't
// System.out.println("Child"); // have its own run()... yet the inherited
// } // run() clashes with I’s run()
public static void main(String[] args) {
new Child().run();
}}
Simple Corollaries:
A static method can’t override an abstract method between interfaces but the opposite
is permitted (again, it merely looks like overriding; @Override will flag a comperr here).
A subinterface can re-declare a default method and also make it abstract.
A subinterface can re-declare a static method but it won’t be overriding. 33
RECAPPING, cont’d
Fields and static methods are not overridden, therefore access to fields and
static methods is determined at compile time based on the type of the
variable (while instance methods will run according to the actual type of the
object referred to by the variable).
A class cannot inherit two interfaces that both define default methods with the
same signature, unless the class implementing the interfaces overrides
those two competing methods with an abstract or concrete method.
34
RECAPPING, cont’d
Interfaces define behavior, and this behavior is inherited only through non-static methods
(shorter wording: interface static methods aren’t inherited).
interface Inter{
static void method1(){}
default void method2(){}
void method3();
}
35
RECAPPING, cont’d
Illustration: static methods defined in the Parent class run when the reftype is Parent:
36
RECAPPING, cont’d
FOR THE LAST TIME: the reftype determines which methods and fields are available to the object
while actype chooses concrete version EXCEPT fields: the fields are
ALWAYS of the reftype.
Corollary:
When a var that has been declared in an interface IS NOT HIDDEN by the implementing class, we
can’t change its value (simply because it’s implicitly final → comperr)
Suppose:
1. class Parent declares int a = 0;
2. class Childdeclares int a = 100;
Then:
Parent p = new Child();
System.out.println(p.a); // 0 Same goes for interfaces except
System.out.println(((Child)p).a); // 100
interfaces cannot be instantiated.
Child c = new Child();
System.out.println(c.a); // 100
System.out.println(((Parent)p).a); // 0