0% found this document useful (0 votes)
35 views

OCA Exam Preparation: Inheritance - Review

Inheritance allows a class to inherit properties and behavior from another class. Polymorphism allows an object to take on different forms depending on the reference used, with the actual type determining which version of an overridden method is executed. The reference type specifies accessible methods and variables, while the actual type dictates inherited behavior. Constructors can exhibit polymorphism as well, with the actual type's version of a called method executing over the reference type's version.

Uploaded by

carutsa
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views

OCA Exam Preparation: Inheritance - Review

Inheritance allows a class to inherit properties and behavior from another class. Polymorphism allows an object to take on different forms depending on the reference used, with the actual type determining which version of an overridden method is executed. The reference type specifies accessible methods and variables, while the actual type dictates inherited behavior. Constructors can exhibit polymorphism as well, with the actual type's version of a called method executing over the reference type's version.

Uploaded by

carutsa
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 38

OCA Exam Preparation

INHERITANCE –
REVIEW
ABBREVIATIONS & ACRONYMS

actype – actual object’s type at run time AIOOBE – ArrayIndexOutOfBoundsException


assop – assignment operator CCE – ClassCastException
castype – data type specified inside the parens for an explicit cast ChE – checked exception
comperr – compilation error CSR – the Catch-or-Specify Requirement
ctor – constructor DTPE – DateTimeParseException
dim – dimension E – an exception (regardless of the type)
initer – initializer IAE – IllegalArgumentException
op – operator IOE – IOException
paramlist – list of formal parameters in a lambda expression IOOBE – IndexOutOfBoundsException
preditype – data type specified inside the angle brackets LDT – any of the new date/time classes in Java 8
reftype – reference type LOC – line of code
refvar – reference variable NFE – NumberFormatException
sout – any printing statement such as System.out.println(), etc. NPE – NullPointerException
stat – statement RTE – RuntimeException
ternop – ternary operator SIOOBE – StringIndexOutOfBoundsException
var – variable TCF – try-catch-finally construct

Igor Soudakevitch  2019 2


[email protected]
EXAM OBJECTIVE’S STRUCTURE

The 1Z0-808 topics within this group:


1. Describe inheritance and its benefits;
2. Develop code that demonstrates the use of polymorphism, including overriding
and object type versus reference type;
3. Determine when casting is necessary;
4. Use super and this to access objects and constructors;
5. Use abstract classes and interfaces.

Source: Oracle Univ. 3


7.1
INHERITANCE

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.

 A final class cannot be extended.

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.

Different wording: Parent’s private methods aren’t polymorphic and bind


at the compile time → the actype’s version won’t fire.
 EXTRA: Abstract methods can be overridden.

 Essence of polymorphism through the exam taker’s eyes:


 The version to be executed is selected according to the class of the actual object;
 In Java, all non-private and non-final instance method calls are virtual and
potentially polymorphic;
 Polymorphism in Java is realized when child’s method has the same name as in
the parent class but its business logic differs from the overridden method.

8
POLYMORPHISM, cont’d

 Rules for method overriding:


 both methods must have the same signature,
 the overriding method mustn’t have a weaker access privilege,
 the overriding method may not declare any new or wider ChE,
 the return types must be at least covariant.

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.

 Unlike methods, variables can’t be overridden – but they can be HIDDEN:


 Which variable will be used depends on the reftype (RT), that is, the LHS of RT obj = new AT();
where AT is a descendant of RT;
 Which method will run depends on the actype (AT, right-hand side):
class A { int i = 10; int m1() { return i; } }
class B extends A { int i = 20; int m1() { return i; } }
class C extends B { int i = 30; int m1() { return i; } }
class Test{
public static void main(String[] args){
C o1 = new C();
B o2 = o1; // reftype is B, actype is C
System.out.println( o1.m1() ); // prints 30
10
System.out.println( o2.i ); // prints 20
POLYMORPHISM, cont’d

 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

 EXTRA: Polymorphism extends to constructors, as well:


class Parent {
Parent() { greet(); }
void greet() {
System.out.println(" Hello! "); // prints null because greet() binds
// to class Child and str at this
// point still holds default value...
}}
class Child extends Parent {
String str = " Hi! ";
void greet() { System.out.print(str); }
public static void main(String[] args){
Parent obj = new Child();
obj.greet(); // ...and only now it prints Hi!
}}
In this example, the Parent’s no-arg constructor invokes the method greet(), which is virtual and,
therefore, gets executed according to the overriding version. Since the obj’s actype is Child, the
first call to greet() prints the default value of the var str and not the String " Hello! ", and only
after the object has been fully initialized, the second call to the same method outputs " Hi! ".
Note that calling an overridable method from a constructor (like Parent’s ctor does) is bad coding
practice as it invokes a method on an object that hasn’t been properly constructed yet.
12
7.3
CASTING

13
CASTING

 We use casting to make refvars behave as a variables of another type when it


becomes necessary to access certain members that are not available by default:
class Casting{
public static void main(String[] args) {
Object obj = new StringBuilder("Cast me plz... ");
// obj.append("Done!"); // INVALID
( (StringBuilder)obj ).append("Done!");
System.out.println(obj);
}}

 Rules of casting between reference types:


■ Widening (or upcast: subclass → superclass) doesn’t need an explicit cast.
■ Narrowing (or downcast: subclass ← superclass) requires an explicit cast.

Combat version:
See no cast when reftype is narrower than actype? → comperr!

14
CASTING, cont’d

 Casting to unrelated types:

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; }

class Test implements I1, I2{

public static void main(String[] args) {


// System.out.println( new Test().A ); // INVALID: "reference
// to A is ambiguous"
System.out.println( I1.A ); // prints 10
System.out.println( ( (I1)new Test() ).A ); // prints 10
System.out.println( ( (I2)new Test() ).A ); // prints 20
}
}

Reminder: Another way to resolve an ambiguous reference is, of course, by


specifying the fully qualified name (as illustrated by the first valid
sout in the above example).

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

 Reminders: What is the difference between shadowing, hiding and obscuring?


“Some declarations may be shadowed in part of their scope by another declaration of the same
name, in which case a simple name cannot be used to refer to the declared entity.”
class Test {
String str; // line t1
public static void main(String[] args) { LOCAL VARS
String str; // t2
CAN’T BE
// for (String str : args) // t3; INVALID
System.out.println(str); SHADOWED
// { String str; } // t4; INVALID
}
}

“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

 EXTRA: Can a grandson C call his grandpa A’s method? Is this.super.run()


possible?
No, there is no way to go more than one level up for methods because this refers to
an instance and not to a data type.

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.

super.methodName() is a valid way to invoke a superclass’ method from a


subclass’s instance method, instance initializer, constructor, or initializer of an instance
var. But it works only for classes.

To invoke a superinterface’s default method, we need to use the name of that


interface, as well. This, however, is possible only when the interface is directly
implemented – or extended – by the caller.
23
USING super AND this, cont’d

Illustration:
interface Laughable {
default void getJoke(){ System.out.println("Ha-ha-ha!"); }
static void makeJoke(){ System.out.println("A man walks into a bar..."); }
}

interface Coder extends Laughable {


default void sayHello() {
// super.getJoke(); // works for classes only
Laughable.super.getJoke();
Laughable.makeJoke();
}
}

class JavaCoder implements Coder {


public void sayHello() {
// super.getJoke(); // JavaCoder's supertype isn't a class
Laughable.makeJoke();
Coder.super.sayHello();
}
public static void main(String[] args) {
new JavaCoder().sayHello();
}
} 24
7.5
INTERFACES AND
abstract CLASSES
25
INTERFACES AND abstract CLASSES

 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

 EXTRA: Question: Are static methods inherited?

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.*;

class Test implements Inter{} // INVALID: "Inter is not public in one,


// cannot be accessed from outside package"

31
RECAPPING

Rules of Inheritance Revisited*


(be especially attentive whenever overriding involves a static method)
1. A static method attempting to override a non-static one is ALWAYS a comperr, which
includes ALL cases: interface-interface, class-class or interface-class AND throughout the
entire inheritance chain (see Remark on the next slide).
2. When it comes to overriding between classes, static and non-static methods are NEVER
compatible.
Reminder: On the other hand, a non-static interface method may "override" a
superinterface’s static method. (It’s just an appearance because static
interface methods aren’t inherited.)

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

AGAIN, from a different angle:

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

More on difference between static and default / abstract methods in interfaces:

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();
}

abstract class Parent implements Inter{


public void method3(){}
}
class Child extends Parent{
public static void main(String[] args) {
// method1(); // won't compile but Inter.method1() will
new Child().method2(); // VALID
new Child().method3(); // VALID
}
}

35
RECAPPING, cont’d

Illustration: static methods defined in the Parent class run when the reftype is Parent:

class Parent { // p2.one();


void one() { System.out.println("parent: one"); } // p2.two();
static void two() { System.out.println("parent: two"); }
Child c2 = new Child();
public static void main(String[] args) { c2.one(); // child: one
Parent p1 = new Parent(); c2.two(); // child: two
p1.one(); // parent: one c2.three(); // child: three
p1.two(); // parent: two }
// p1.three(); //<-- can’t see it }
class Child extends Parent {
Parent c1 = new Child(); void one() { System.out.println("child: one"); }
c1.one(); // child: one static void two() { System.out.println("child: two");}
c1.two(); // parent: two void three() {System.out.println("child: three");}
// c1.three(); //<-- can’t see it }
// Child p2 = new Parent(); // needs cast

36
RECAPPING, cont’d

SAME STORY WITH INTERFACES:


interface Parent {
void zero();
default void one() { System.out.println("parent: one"); }
static void two() { System.out.println("parent: two"); }
}
class Child implements Parent {
public void zero(){System.out.println("child: zero");}
public void one() { System.out.println("child: one"); }
static void two() { System.out.println("child: two"); }

public static void main(String[] args) {

Parent c1 = new Child();


c1.zero(); // child: zero
c1.one(); // child: one
// c1.two(); // won’t compile but next one does
Parent.two(); // parent: two

Child c2 = new Child();


c1.zero(); // child: zero
c2.one(); // child: one
c2.two(); // child: two
} 37
}
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

Parent obj = new Parent();


System.out.println(obj.a); // 0
System.out.println(((Child)obj).a); // CCE at RT 38

You might also like