Chapter 8 Class Design Notes1 Flashcards

Class Design

1
Q

Understanding Inheritance

A
  • Inheritance is the process by which a subclass automatically includes any public or protected members of the class, including primitives, objects, or methods, defined in the parent class.
  • subclass or child class
  • superclass or parent class
  • inheritance is transitive
    Z class –> Y class –> X class
  • Package-private members are available if the child class is in the same package as the parent class.
  • private members are restricted to the class they are defined in and are never available via inheritance. This doesn’t mean the parent class doesn’t have private members that can hold data or modify an object; it just means the child class has no direct reference to them.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

SINGLE VS. MULTIPLE INHERITANCE

A
  • Java supports single inheritance, by which a class may inherit from only one direct parent class. (only can extends one class)
  • Java also supports multiple levels of inheritance, by which one class may extend another class, which in turn extends another class. You can have any number of levels of inheritance, allowing each descendant to gain access to its ancestor’s members.
  • To truly understand single inheritance, it may helpful to contrast it with multiple inheritance, by which a class may have multiple direct parents. By design, Java doesn’t support multiple inheritance in the language because multiple inheritance can lead to complex, often difficult-to-maintain data models.
  • Java does allow one exception to the single inheritance rule that you’ll see in Chapter 9—a class may implement multiple interfaces.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

How to prevent a class from being extended?

A
  • It is possible in Java to prevent a class from being extended by marking the class with the final modifier.
  • If you try to define a class that inherits from a final class, then the class will fail to compile.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

INHERITING OBJECT

A
  • In Java, all classes inherit from a single class: java.lang.Object, or Object for short.
  • Furthermore, Object is the only class that doesn’t have a parent class.
  • You might be wondering, “None of the classes I’ve written so far extend Object, so how do all classes inherit from it?” The answer is that the compiler has been automatically inserting code into any class you write that doesn’t extend a specific class.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Creating Classes

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

EXTENDING A CLASS

A
public abstract class ElephantSeal extends Seal {
    // Methods and Variables defined here
}
  1. public or default (package-private) access modifier
  2. abstract or final keyword (optional)
  3. class keyword (required)
  4. Class name (required)
  5. extends parent class (optional)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

APPLYING CLASS ACCESS MODIFIERS

A
  • top-level class can only have public or package-private access.
  • An inner class is a class defined inside of another class and is the opposite of a top-level class.
  • In addition to public and package-private access, inner classes can also have protected and private access.
  • We will discuss inner classes in Chapter 9.
  • As you might recall, a Java file can have many top-level classes but at most one public top-level class.
  • In fact, it may have no public class at all.
  • There’s also no requirement that the single public class be the first class in the file.
  • One benefit of using the package-private access is that you can define many classes within the same Java file.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

ACCESSING THE THIS REFERENCE

A
public class Flamingo {
    private String color;
    public void setColor(String color) {
        color = color;
    }
}		

Java uses the most granular scope, so when it sees color = color, it thinks you are assigning the method parameter value to itself.

  • when you have a local variable with the same name as an instance variable is to use the this reference or keyword.
  • The this reference refers to the current instance of the class and can be used to access any member of the class, including inherited members.
  • It can be used in any
    • instance method,
    • constructor,
    • and instance initializer block.
  • It cannot be used when there is no implicit instance of the class, such as in a
    • static method
    • or static initializer block.
public void setColor(String color) {
    this.color = color;
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

CALLING THE SUPER REFERENCE

A
  • super reference refers the members accessible via inheritance.
  • No access to current class members
  • you often only use super when you have a naming conflict via inheritance. For example, you have a method or variable defined in the current class that matches a method or variable in a parent class.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Declaring Constructors

A
  • a constructor is a special method that matches the name of the class
  • and has no return type.
  • It is called when a new instance of the class is created.
  • Like method parameters, constructor parameters can be any valid class, array, or primitive type, including generics, but may not include var.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

CREATING A CONSTRUCTOR

A
public class Bunny {
    public Bunny() { //constructor name match class name and not return type
        System.out.println("constructor");
    }
}
  • A class can have multiple constructors, so long as each constructor has a unique signature. In this case, that means the constructor parameters must be distinct.
  • declaring multiple constructors with different signatures is referred to as constructor overloading.
  • Constructors are used when creating a new object. This process is called instantiation because it creates a new instance of the class. A constructor is called when we write new followed by the name of the class we want to instantiate.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

DEFAULT CONSTRUCTOR

A
  • Every class in Java has a constructor whether you code one or not.
  • If you don’t include any constructors in the class, Java will create one for you without any parameters.
  • This Java-created constructor is called the default constructor and is added anytime a class is declared without any constructors.
  • We often refer to it as the default no-argument constructor for clarity.
  • The default constructor has an empty parameter list and an empty body.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

private constructors

A

> [!NOTE]
Having only private constructors in a class tells the compiler not to provide a default no-argument constructor.It also prevents other classes from instantiating the class.This is useful when a class has only static methods or the developer wants to have full control of all calls to create new instances of the class.Remember, static methods in the class, including a main() method, may access private members, including private constructors.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

CALLING OVERLOADED CONSTRUCTORS WITH this()

A
  • Constructors can be called only by writing new before the name of the constructor.
  • They are not like normal methods that you can just call.
  • Use this() to calls another constructor on the same instance of the class.
  • the this() call must be the first statement in the constructor.
  • The side effect of this is that there can be only one call to this() in any constructor
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

There’s one last rule for overloaded constructors you should be aware of.

A
  • The compiler is capable of detecting that this constructor is calling itself infinitely. Since this code can never terminate, the compiler stops and reports this as an error.
public class Gopher {
    public Gopher(int dugHoles) {
        this(5); // DOES NOT COMPILE
    }
}
  • the constructors call each other, and the process continues infinitely. Since the compiler can detect this, it reports this as an error.
public class Gopher {
    public Gopher() {
        this(5); // DOES NOT COMPILE
    }
    public Gopher(int dugHoles) {
        this(); // DOES NOT COMPILE
    }
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

THIS VS. THIS()

A
  • Despite using the same keyword, this and this() are very different.
    • The first, this, refers to an instance of the class,
    • while the second, this(), refers to a constructor call within the class.
  • The exam may try to trick you by using both together, so make sure you know which one to use and why.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

CALLING PARENT CONSTRUCTORS WITH SUPER()

A
  • In Java, the first statement of every constructor is either a call to another constructor within the class, using this(), or a call to a constructor in the direct parent class, using super().
  • If a parent constructor takes arguments, then the super() call also takes arguments.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

SUPER VS. SUPER()

A
  • Like this and this(), super and super() are unrelated in Java.
    • The first, super, is used to reference members of the parent class,
    • while the second, super(), calls a parent constructor.
  • Anytime you see the super keyword on the exam, make sure it is being used properly.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Understanding Compiler Enhancements

A

Java compiler automatically inserts a call to the no-argument constructor super() if you do not explicitly call this() or super() as the first line of a constructor.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

ARE CLASSES WITH ONLY PRIVATE CONSTRUCTORS CONSIDERED FINAL?

A
  • only an inner class defined in the class itself can extend it.
  • An inner class is the only one that would have access to a private constructor and be able to call super().
  • Other top-level classes cannot extend such a class.
  • Don’t worry—knowing this fact is not required for the exam. We include it here for those who were curious about declaring only private constructors.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Missing a Default No-Argument Constructor
What happens if the parent class doesn’t have a no-argument constructor?

A
  • you must create at least one constructor in your child class that explicitly calls a parent constructor via the super() command.
  • We can fix this by adding a call to a parent constructor that takes a fixed argument.
public class Mammal {
public Mammal(int age) {}
}
public class Elephant extends Mammal { // DOES NOT COMPILE
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

SUPER() ALWAYS REFERS TO THE MOST DIRECT PARENT

A

For constructors, though, super() always refers to the most direct parent.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

CONSTRUCTORS AND FINAL FIELDS

A
  • final static variables must be assigned a value exactly once. You saw this happen in the
    • line of the declaration
    • and in a static initializer.
  • Instance variables marked final follow similar rules. They can be assigned values in
    • the line in which they are declared
    • or in an instance initializer.
    • The constructor is part of the initialization process, so it is allowed to assign final instance variables in it.
  • For the exam, you need to know one important rule. By the time the constructor completes, all final instance variables must be assigned a value.
  • Unlike local final variables, which are not required to have a value unless they are actually used, final instance variables must be assigned a value.
  • Default values are not used for these variables.
  • If they are not assigned a value in the line where they are declared or in an instance initializer, then they must be assigned a value in the constructor declaration. Failure to do so will result in a compiler error on the line that declares the constructor.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

> [!NOTE]
On the exam, be wary of any instance variables marked final.
Make sure they are assigned a value in the line where they are declared, in an instance initializer, or in a constructor.
They should be assigned a value only once,
and failure to assign a value is considered a compiler error in the constructor.
We can assign a null value to final instance variables, so long as they are explicitly set.

A
  • instance variables marked final.
  • assigned a value
    • in the line where they are declared,
    • in an instance initializer,
    • or in a constructor.
  • assigned a value only once,
  • failure to assign a value is considered a compiler error in the constructor.
    We can assign a null value to final instance variables, so long as they are explicitly set.
25
Q

ORDER OF INITIALIZATION

A
26
Q

Class Initialization

A
  • First, you need to initialize the class, which involves invoking all static members in the class hierarchy, starting with the highest superclass and working downward.
  • This is often referred to as loading the class.
  • The JVM controls when the class is initialized, although you can assume the class is loaded before it is used.
  • The class may be initialized when the program first starts, when a static member of the class is referenced, or shortly before an instance of the class is created.
  • The most important rule with class initialization is that it happens at most once for each class.
  • The class may also never be loaded if it is not used in the program.

Initialize Class X
1. If there is a superclass Y of X, then initialize class Y first.
2. Process all static variable declarations in the order they appear in the class.
3. Process all static initializers in the order they appear in the class.

27
Q

WHY THE HIPPO PROGRAM PRINTED C AFTER AB

public class Animal {
    static { System.out.print("A"); }
}

public class Hippo extends Animal {
    static { System.out.print("B"); }
    public static void main(String[] grass) {
        System.out.print("C");
        new Hippo();
        new Hippo();
        new Hippo();
    }
}

public class HippoFriend {
    public static void main(String[] grass) {
        System.out.print("C");
        new Hippo();
    }
}
A

Assuming the class isn’t referenced anywhere else, this program will likely print
CAB,

  • with the Hippo class not being loaded until it is needed inside the main() method.
  • We say likely, because the rules for when classes are loaded are determined by the JVM at runtime.
  • For the exam, you just need to know that a class must be initialized before it is referenced or used.
  • Also, the class containing the program entry point, aka the main() method, is loaded before the main() method is executed.
28
Q

Instance Initialization

A
  • An instance is initialized anytime the new keyword is used. In our previous example, there were three new Hippo() calls, resulting in three Hippo instances being initialized.
  • Instance initialization is a bit more complicated than class initialization, because a class or superclass may have many constructors declared but only a handful used as part of instance initialization.
  • First, start at the lowest-level constructor where the new keyword is used.
  • Remember, the first line of every constructor is a call to this() or super(), and if omitted, the compiler will automatically insert a call to the parent no-argument constructor super().
  • Then, progress upward and note the order of constructors.
  • Finally, initialize each class starting with the superclass, processing each instance initializer and constructor in the reverse order in which it was called.

Initialize Instance of X
1. If there is a superclass Y of X, then initialize the instance of Y first.
2. Process all instance variable declarations in the order they appear in the class.
3. Process all instance initializers in the order they appear in the class.
4. Initialize the constructor including any overloaded constructors referenced with this().

29
Q

REVIEWING CONSTRUCTOR RULES

A
  1. The first statement of every constructor is a call to an overloaded constructor via this(), or a direct parent constructor via super().
  2. If the first statement of a constructor is not a call to this() or super(), then the compiler will insert a no-argument super() as the first statement of the constructor.
  3. Calling this() and super() after the first statement of a constructor results in a compiler error.
  4. If the parent class doesn’t have a no-argument constructor, then every constructor in the child class must start with an explicit this() or super() constructor call.
  5. If the parent class doesn’t have a no-argument constructor and the child doesn’t define any constructors, then the child class will not compile.
  6. If a class only defines private constructors, then it cannot be extended by a top-level class.
  7. All final instance variables must be assigned a value exactly once by the end of the constructor. Any final instance variables not assigned a value will be reported as a compiler error on the line the constructor is declared.
30
Q

Inheriting Members

A

One of Java’s biggest strengths is leveraging its inheritance model to simplify code.

Java allows subclass to replace, or override, the parent method implementation at runtime.

31
Q

CALLING INHERITED MEMBERS

A
  • Java classes may use any public or protected member of the parent class, including methods, primitives, or object references.
  • If the parent class and child class are part of the same package, then the child class may also use any package-private members defined in the parent class.
  • Finally, a child class may never access a private member of the parent class, at least not through any direct reference.
  • As you saw earlier in this chapter, a private member age was accessed indirectly via a public or protected method.
  • To reference a member in a parent class, you can just call it directly
  • Remember, you can use this to access visible members of the current or a parent class,
  • and you can use super to access visible members of a parent class.
class Fish {
    protected int size;
    private int age;
    public Fish(int age) {
        this.age = age;
    }
    public int getAge() {
        return age;
    }
}

public class Shark extends Fish {
    private int numberOfFins = 8;
    public Shark(int age) {
        super(age);
        this.size = 4;
    }
    public void displaySharkDetails() {
        System.out.print("Shark with age: "+getAge());
        System.out.print(" and "+size+" meters long");
        System.out.print(" with "+numberOfFins+" fins");
    }
}
32
Q

INHERITING METHODS

A

Inheriting a class not only grants access to inherited methods in the parent class but also sets the stage for collisions between methods defined in both the parent class and the subclass. In this section, we’ll review the rules for method inheritance and how Java handles such scenarios.

33
Q

Overriding a Method

What if there is a method defined in both the parent and child classes with the same signature?

A
  • The solution is to override the method in the child class.
  • In Java, overriding a method occurs when a subclass declares a new implementation for an inherited method with the same signature and compatible return type.
  • Remember that a method signature includes the name of the method and method parameters.
  • When you override a method, you may reference the parent version of the method using the super keyword.
  • In this manner, the keywords this and super allow you to select between the current and parent versions of a method, respectively.
34
Q

To override a method, you must follow a number of rules.

A

The compiler performs the following checks when you override a method:
1. The method in the child class must have the same signature as the method in the parent class.
2. The method in the child class must be at least as accessible as the method in the parent class.
3. The method in the child class may not declare a checked exception that is new or broader than the class of any exception declared in the parent class method.
4. If the method returns a value, it must be the same or a subtype of the method in the parent class, known as covariant return types.

35
Q

METHOD OVERRIDING AND RECURSIVE CALLS

A
  • A recursive method is one that calls itself as part of execution.
  • It is common in programming but must have a termination condition that triggers the end to recursion at some point or depth.
  • In this example, there is no termination condition; therefore, the application will attempt to call itself infinitely and produce a StackOverflowError at runtime.
public double getAverageWeight() {
    return getAverageWeight()+20; // StackOverflowError
}
36
Q

DEFINING SUBTYPE AND SUPERTYPE

A

When discussing inheritance and polymorphism, we often use the word subtype rather than subclass, since Java includes interfaces.
A subtype is the relationship between two types where one type inherits the other.
If we define X to be a subtype of Y, then one of the following is true :

  • X and Y are classes, and X is a subclass of Y.
  • X and Y are interfaces, and X is a subinterface of Y.
  • X is a class and Y is an interface, and X implements Y (either directly or through an inherited class).

Likewise, a supertype is the reciprocal relationship between two types where one type is the ancestor of the other. Remember, a subclass is a subtype, but not all subtypes are subclasses.

37
Q

OVERLOADING VS. OVERRIDING

A
  • Overloading and overriding a method are simLilar in that they both involve redefining a method using the same name.
  • They differ in that an overloaded method will use a different list of method parameters.
  • This distinction allows overloaded methods a great deal more freedom in syntax than an overridden method would have.

Any time you see a method on the exam with the same name as a method in the parent class, determine whether the method is being overloaded or overridden first; doing so will help you with questions about whether the code will compile.

Java avoids these types of ambiguity problems by limiting overriding a method to access modifiers that are as accessible or more accessible than the version in the inherited method.

that overriding a method cannot declare new checked exceptions or checked exceptions broader than the inherited method. This is done for similar polymorphic reasons as limiting access modifiers. In other words, you could end up with an object that is more restrictive than the reference type it is assigned to, resulting in a checked exception that is not handled or declared. We will discuss what it means for an exception to be checked in Chapter 10, “Exceptions.” For now, you should just recognize that if a broader checked exception is declared in the overriding method, the code will not compile.

The overriding method must use a return type that is covariant with the return type of the inherited method.

38
Q

> [!NOTE]
A simple test for covariance is the following:
Given an inherited return type A and an overriding return type B, can you assign an instance of B to a reference variable for A without a cast?
If so, then they are covariant.
This rule applies to primitive types and object types alike.
If one of the return types is void, then they both must be void, as nothing is covariant with void except itself.

A
  • covariance test
  • assing instance of B to a reference variable for A without cast
  • This rule applices to primitive types and object types
  • If one of the return types is void, then they both must be void, as nothing is covariant with void except itself.
39
Q

Overriding a Generic Method

A
40
Q

Review of Overloading a Generic Method

A

Cannot overload methods by changing the generic type due to type erasure.
To review, only one of the two methods is allowed in a class because type erasure will reduce both sets of arguments to (List input).

public class LongTailAnimal {
    protected void chew(List<Object> input) {}
    protected void chew(List<Double> input) {} // DOES NOT COMPILE
}

For the same reason, you also can’t overload a generic method in a parent class.

public class LongTailAnimal {
    protected void chew(List<Object> input) {}
}
public class Anteater extends LongTailAnimal {
    protected void chew(List<Double> input) {} // DOES NOT COMPILE
}

Both of these examples fail to compile because of type erasure. In the compiled form, the generic type is dropped, and it appears as an invalid overloaded method.

41
Q

Generic Method Parameters

A

Can override a method with generic parameters, but you must match the signature including the generic type exactly.

public class LongTailAnimal {
    protected void chew(List<String> input) {}
}
public class Anteater extends LongTailAnimal {
    protected void chew(List<String> input) {}
}
public class LongTailAnimal {
    protected void chew(List<Object> input) {}
}
public class Anteater extends LongTailAnimal {
    protected void chew(ArrayList<Double> input) {} //OVERLOAD
}

Yes, these classes do compile. However, they are considered overloaded methods, not overridden methods, because the signature is not the same. Type erasure does not change the fact that one of the method arguments is a List and the other is an ArrayList.

42
Q

GENERICS AND WILDCARDS

A

Java includes support for generic wildcards using the question mark (?) character. It even supports bounded wildcards.

void sing1(List<?> v) {} // unbounded wildcard
void sing2(List<? super String> v) {} // lower bounded wildcard
void sing3(List<? extends String> v) {} // upper bounded wildcard
43
Q

Generic Return Types

A
  • When you’re working with overridden methods that return generics, the return values must be covariant.
  • In terms of generics, this means that the return type of the class or interface declared in the overriding method must be a subtype of the class defined in the parent class.
  • The generic parameter type must match its parent’s type exactly.
public class Mammal {
    public List<CharSequence> play() { ... }
    public CharSequence sleep() { ... }
}
public class Monkey extends Mammal {
    public ArrayList<CharSequence> play() { ... }
}
public class Goat extends Mammal {
    public List<String> play() { ... } // DOES NOT COMPILE
    public String sleep() { ... }
}
  • The play() method in the Goat class does not compile, though.
  • For the return types to be covariant, the generic type parameter must match.
  • Even though String is a subtype of CharSequence, it does not exactly match the generic type defined in the Mammal class.
  • Therefore, this is considered an invalid override.
  • Notice that the sleep() method in the Goat class does compile since String is a subtype of CharSequence.
  • This example shows that covariance applies to the return type, just not the generic parameter type.

For the exam, it might be helpful for you to apply type erasure to questions involving generics to ensure that they compile properly. Once you’ve determined which methods are overridden and which are being overloaded, work backward, making sure the generic types match for overridden methods. And remember, generic methods cannot be overloaded by changing the generic parameter type only.

44
Q

Redeclaring private Methods

A
  • In Java, you can’t override private methods since they are not inherited.
  • Just because a child class doesn’t have access to the parent method doesn’t mean the child class can’t define its own version of the method.
  • It just means, strictly speaking, that the new method is not an overridden version of the parent class’s method.
  • Java permits you to redeclare a new method in the child class with the same or modified signature as the method in the parent class.
  • This method in the child class is a separate and independent method, unrelated to the parent version’s method, so none of the rules for overriding methods is invoked.
45
Q

Hiding Static Methods

A
  • A hidden method occurs when a child class defines a static method with the same name and signature as an inherited static method defined in a parent class.
  • Method hiding is similar but not exactly the same as method overriding.
  • The previous four rules for overriding a method must be followed when a method is hidden. In addition, a new rule is added for hiding a method:5. The method defined in the child class must be marked as static if it is marked as static in a parent class.
  • Put simply, it is method hiding if the two methods are marked static,
  • and method overriding if they are not marked static.
  • If one is marked static and the other is not, the class will not compile.
46
Q

Creating final Methods

A
  • final methods cannot be replaced.
  • By marking a method final, you forbid a child class from replacing this method.
  • This rule is in place both when you override a method and when you hide a method.
  • In other words, you cannot hide a static method in a child class if it is marked final in the parent class.
public class Bird {
    public final boolean hasFeathers() {
        return true;
    }
    public final static void flyAway() {}
}
public class Penguin extends Bird {
    public final boolean hasFeathers() { // DOES NOT COMPILE
        return false;
    }
    public final static void flyAway() {} // DOES NOT COMPILE
}

The static method flyAway() is also marked final, so it cannot be hidden in the subclass. In this example, whether or not the child method used the final keyword is irrelevant—the code will not compile either way.

47
Q

WHY MARK A METHOD AS FINAL?

A
  • Although marking methods as final prevents them from being overridden, it does have advantages in practice.
  • For example, you’d mark a method as final when you’re defining a parent class and want to guarantee certain behavior of a method in the parent class, regardless of which child is invoking the method.
  • The reason methods are not commonly marked as final in practice, though, is that it may be difficult for the author of a parent class method to consider all of the possible ways her child class may be used.
  • For example, although all adult birds have feathers, a baby chick doesn’t; therefore, if you have an instance of a Bird that is a chick, it would not have feathers.
  • For this reason, the final modifier is often used when the author of the parent class wants to guarantee certain behavior at the cost of limiting polymorphism.
48
Q

HIDING VARIABLES

A
  • Java doesn’t allow variables to be overridden.
  • Variables can be hidden, though.
  • A hidden variable occurs when a child class defines a variable with the same name as an inherited variable defined in the parent class.
  • This creates two distinct copies of the variable within an instance of the child class:
    • one instance defined in the parent class
    • and one defined in the child class.
49
Q

Understanding Polymorphism

A
  • Java supports polymorphism,
  • the property of an object to take on many different forms.
  • To put this more precisely, a Java object may be accessed using a reference with the same type as the object, a reference that is a superclass of the object, or a reference that defines an interface the object implements, either directly or through a superclass.
  • Furthermore, a cast is not required if the object is being reassigned to a super type or interface of the object.
50
Q

INTERFACE PRIMER

A
  • An interface can define abstract methods.
  • A class can implement any number of interfaces.
  • A class implements an interface by overriding the inherited abstract methods.
  • An object that implements an interface can be assigned to a reference for that interface.
51
Q

OBJECT VS. REFERENCE

A
  • In Java, all objects are accessed by reference, so as a developer you never have direct access to the object itself.
  • Conceptually, though, you should consider the object as the entity that exists in memory, allocated by the Java runtime environment.
  • Regardless of the type of the reference you have for the object in memory, the object itself doesn’t change.
  • For example, since all objects inherit java.lang.Object, they can all be reassigned to java.lang.Object, as shown in the following example:
Lemur lemur = new Lemur();
Object lemurAsObject = lemur;

We can summarize this principle with the following two rules:
1. The type of the object determines which properties exist within the object in memory.
2. The type of the reference to the object determines which methods and variables are accessible to the Java program.

52
Q

CASTING OBJECTS

A

We summarize these concepts into a set of rules for you to memorize for the exam:
1. Casting a reference from a subtype to a supertype doesn’t require an explicit cast.
2. Casting a reference from a supertype to a subtype requires an explicit cast.
3. The compiler disallows casts to an unrelated class.
4. At runtime, an invalid cast of a reference to an unrelated type results in a ClassCastException being thrown.

public class Rodent {}
public class Capybara extends Rodent {
    public static void main(String[] args) {
        Rodent rodent = new Rodent();
        Capybara capybara = (Capybara)rodent; // ClassCastException
    }
}
53
Q

THE INSTANCEOF OPERATOR

A

instanceof operator, which can be used to check whether an object belongs to a particular class or interface and to prevent ClassCastExceptions at runtime.

the following code snippet doesn’t throw an exception at runtime and performs the cast only if the instanceof operator returns true:

if(rodent instanceof Capybara) {
    Capybara capybara = (Capybara)rodent;
}

Just as the compiler does not allow casting an object to unrelated types, it also does not allow instanceof to be used with unrelated types. We can demonstrate this with our unrelated Bird and Fish classes:

public static void main(String[] args) {
    Fish fish = new Fish();
    if (fish instanceof Bird) { // DOES NOT COMPILE
        Bird bird = (Bird) fish; // DOES NOT COMPILE
    }
}

In this snippet, neither the instanceof operator nor the explicit cast operation compile.

54
Q

POLYMORPHISM AND METHOD OVERRIDING

A

In Java, polymorphism states that when you override a method, you replace all calls to it, even those defined in the parent class.

Output : 8

class Penguin {
    public int getHeight() { return 3; }
    public void printInfo() {
        System.out.print(this.getHeight());
    }
}
public class EmperorPenguin extends Penguin {
    public int getHeight() { return 8; }
    public static void main(String []fish) {
        new EmperorPenguin().printInfo();
    }
}
  • The facet of polymorphism that replaces methods via overriding is one of the most important properties in all of Java.
  • It allows you to create complex inheritance models, with subclasses that have their own custom implementation of overridden methods.
  • It also means the parent class does not need to be updated to use the custom or overridden method.
  • If the method is properly overridden, then the overridden version will be used in all places that it is called.
  • Remember, you can choose to limit polymorphic behavior by marking methods final, which prevents them from being overridden by a subclass.
55
Q

CALLING THE PARENT VERSION OF AN OVERRIDDEN METHOD

A

there is one exception to overriding a method where the parent method can still be called, and that is when the super reference is used.

class Penguin {
...
    public void printInfo() {
        System.out.print(super.getHeight()); // DOES NOT COMPILE
    }
}

this does not compile, as super refers to the superclass of Penguin, in this case Object. The solution is to override printInfo() in the EmperorPenguin class and use super there.

public class EmperorPenguin extends Penguin {
...
    public void printInfo() {
        System.out.print(super.getHeight());
    }
...
}

This new version of EmperorPenguin uses the getHeight() method declared in the parent class and prints 3.

56
Q

OVERRIDING VS. HIDING MEMBERS

A
  • While method overriding replaces the method everywhere it is called, static method and variable hiding does not.
  • Strictly speaking, hiding members is not a form of polymorphism since the methods and variables maintain their individual properties.
  • Unlike method overriding, hiding members is very sensitive to the reference type and location where the member is being used.
class Penguin {
    public static int getHeight() { return 3; }
    public void printInfo() {
        System.out.println(this.getHeight());
    }
}
public class CrestedPenguin extends Penguin {
    public static int getHeight() { return 8; }
    public static void main(String... fish) {
        new CrestedPenguin().printInfo();
    }
}
57
Q
class Marsupial {
    protected int age = 2;
    public static boolean isBiped() {
        return false;
    }
}
public class Kangaroo extends Marsupial {
    protected int age = 6;
    public static boolean isBiped() {
        return true;
    }
    public static void main(String[] args) {
        Kangaroo joey = new Kangaroo();
        Marsupial moey = joey;
        System.out.println(joey.isBiped());
        System.out.println(moey.isBiped());
        System.out.println(joey.age);
        System.out.println(moey.age);
    }
}
A

The program prints the following:

true
false
6
2
  • in this example, only one object, of type Kangaroo, is created and stored in memory.
  • Since static methods can only be hidden, not overridden,
  • Java uses the reference type to determine which version of isBiped() should be called,
  • resulting in
    • joey.isBiped() printing true
    • and moey.isBiped() printing false.
  • Likewise, the age variable is hidden, not overridden,
  • so the reference type is used to determine which value to output.
    • This results in joey.age returning 6
    • and moey.age returning 2.
58
Q

DON’T HIDE MEMBERS IN PRACTICE

A
  • Although Java allows you to hide variables and static methods, it is considered an extremely poor coding practice.
  • As you saw in the previous example, the value of the variable or method can change depending on what reference is used, making your code very confusing, difficult to follow, and challenging for others to maintain.
  • This is further compounded when you start modifying the value of the variable in both the parent and child methods, since it may not be clear which variable you’re updating.
  • When you’re defining a new variable or static method in a child class, it is considered good coding practice to select a name that is not already used by an inherited member.
  • Redeclaring private methods and variables is considered less problematic, though, because the child class does not have access to the variable in the parent class to begin with.

For the exam, make sure you understand these examples as they show how hidden and overridden methods are fundamentally different. In practice, overriding methods is the cornerstone of polymorphism and is an extremely powerful feature.