(Note: This article is a bit difficult to understand, so I translated most of the original text directly... Title: Subtype Polymorphism vs. Explicit Higher Order Functions)
1. Warm up: Dynamic method selection
Suppose we have two classes, Dog and ShowDog, where showDog implements Dog and both have bark() and showDog Override bark() methods
To summarize the is-a relationship, we can get:
- Every showDog is a Dog
- Every Dog is an Object
- All types in Java are subtypes of Object
Now, consider the following code:
Object o2 = new ShowDog("Mortimer","Corgi",25,512.2); ShowDog sdx = ((ShowDog)o2); sdx.bark(); Dog dx = ((Dog)o2); dx.bark(); ((Dog)o2).bark(); Object o3 = (Dog) o2; o3.bark();
For each line assignment, consider whether compilation errors will result
For each call to bark (), consider ShowDog, which is called. What about bark ()? Or Dog.bark()? Is it a grammatical error?
Review the rules:
- The compiler allows the memory box to hold variables of any subtype
- The compiler checks whether a method can be called depending on the static type of the variable
- Dynamic method selection s only work with subclass Overridden nonstatic methods and occur at runtime, where the call to the method depends on the dynamic type of the variable
- Casting is not a long-term validity, it only acts instantaneously on a particular expression using cast (equivalent to valid on that line of code, invalidated on the next line, variable reverting to its original type), which deceives the compiler by saying, "Trust me, this variable is the type," bypassing the compiler's type checks. Essentially, casting does not change the real type the variable is pointing to
hiding
hiding is a bad style, for example:
- A variable in a subclass has the same name as a variable in a parent class
- Static methods in subclasses are consistent with static method declarations in parent classes (including parameter names)
- For static methods, we don't call Override, we call hiding
So called hiding, josh doesn't intend to teach in 61B because he thinks it's a grammatical error. if you interested it,see this link
2. Subtype Polymorphism
Polymorphism: Provide a single interface for different types of entities
In Java, polymorphism means that objects can take many forms or types. In object-oriented programming, polymorphism involves how an object is considered an instance of its own class, its superclass, its superclass, and so on.
3.DIY Comparison
Suppose we now need to write a program that prints out the larger of the two Object s
In python,
1. Write using methods explicitly called by higher-order functions:
def print_larger(x, y, compare, stringify): if compare(x, y): return stringify(x) return stringify(y)
Sometimes compare() is also called callback.
2. Write using the method of subtype polymorphism:
def print_larger(x, y): if x.largerThan(y): return x.str() return y.str()
By using methods that are explicitly called by higher-order functions, you can print results in a common way, whereas in methods with subtype polymorphisms, the object's call to largeFunction() depends on what x and y are actually
Assuming we want to write a max(), we can return the maximum element in any type of array
Which line compiles errors for code in the red box?
Obviously
items[i] > items[maxDex]
Since Object s cannot be compared with each other in Java using >, in addition, operator overloading is not supported in Java, which also means that Java cannot overload like C++ and python>
One native solution is to define the max() method within the Dog class:
public static Dog maxDog(Dog[] dogs) { if (dogs == null || dogs.length == 0) { return null; } Dog maxDog = dogs[0]; for (Dog d : dogs) { if (d.size > maxDog.size) { maxDog = d; }} return maxDog; }
Then you can call:
Dog[] dogs = new Dog[]{d1, d2, d3}; Dog largest = Dog.maxDog(dogs);
But the downside is, what if the current Object is not a Dog? Is it Cat, Fish, or Lion? Aren't we going to write a max() for each Animal class?
We want to be able to create a max() that can be used to compare any Animals, so Solution is
- Create an interface and declare a comparison method in it
- Create a Dog class, implements interface
public interface OurComparable { public int compareTo(Object o); //The parameter Obejct can also be changed to OurComparable, no difference }
For the compareTo() function, its return value is defined as:
- if the current object this <object o, returns a negative number
- if both are equal, return 0
- if current object this > object o, return positive number
We use size as a benchmark for comparison
public class Dog implements OurComparable { private String name; private int size; public Dog(String n, int s) { name = n; size = s; } public void bark() { System.out.println(name + " says: bark"); } public int compareTo(Object o) { Dog uddaDog = (Dog) o; return this.size - uddaDog.size; } }
Note that because the compareTo(Object o) parameter is any Object o, we need to convert the o-cast type of the Object type to the Dog type (using uddaDog to store o) to ensure that we can use the size variable for comparison, otherwise there is no size in the Object class
Once that's done, we can generalize the max() method, which will work for any type of Animals instead of just Dog:
public class Maximizer { public static OurComparable max(OurComparable[] items) { int maxDex = 0; //OurComparable can also be changed to Object for (int i = 0; i < items.length; i += 1) { int cmp = items[i].compareTo(items[maxDex]); if (cmp > 0) { maxDex = i; } } return items[maxDex]; } }
max():
Dog[] dogs = new Dog[]{d1, d2, d3}; Dog largest = (Dog) Maximizer.max(dogs);
In addition, if we want to compare Cat classes, create a Cat class to implement our Comparable interface, so the max() at this point applies to all Animals
4.Interfaces Quiz
Q1: If we ignore compareTo() in the Dog class, which file will compile errors?
A: Dog.java compiles errors because Dog declares the implements OurComparable interface, meaning that Dog class promises to implement all methods () declared by OurComparable, and errors occur when these methods are not included in the Dog class
And DogLauncher.java also errors because of Dog. A compilation error in Java prevented javac from generating a Dog.class file, then in DogLaucher. Dog is used in java. Error will occur
Q2: If we ignore implements OurComparable in the Dog class header, which of the following files will fail?
A: DogLauncher.java will error because when the Dog class does not declare implements, the Dog class is not a subclass of OurComparable, and OurComparable's memory box does not hold the Dog type, so when trying to reach Maximizer. Error occurs when max() passes in a dogs array of type Dog as a parameter
5.Comparables interface
We have completed the OurComparable interface, but there are still many imperfections, such as:
- Cast between Object s each time:
Dog uddaDog = (Dog) o;
Dog[] dogs = new Dog[]{d1, d2, d3}; Dog largest = (Dog) Maximizer.max(dogs);
- No existing classes implement OurComparable (e.g. String, etc.)
- No existing classes use OurComparable (e.g. no built-in max function that uses OurComparable)
Java has a built-in interface that is more complete and powerful, similar to the OurComparable we implemented, and supports generics
Use: Just replace T with Dog
6. Comparator
Consider how Java implements callback, in python, using higher-order functions to display callbacks, compare being passed in as a parameter
def print_larger(x, y, compare, stringify): if compare(x, y): return stringify(x) return stringify(y)
How do I modify subtype polymorphisms to implement callbacks?
def print_larger(T x, T y): if x.largerThan(y): return x.str() return y.str()
Add a parameter comparator<T> C
def print_larger(T x, T y, comparator<T> c): if c.compare(x, y): return x.str() return y.str()
This is another Java built-in interface Comparator that we're going to introduce. What we're comparing above is the size of two dogs. What if we compare their names in alphabetical dictionary order? Comparator is available
Because Comparator is an Object, we use Comparator by nesting another class within the Dog class to implements the Comparator interface
public interface Comparator<T> { int compare(T o1, T o2); }
The rules are similar to compareTo():
- Return negative number if o1 < o2.
- Return 0 if o1 equals o2.
- Return positive number if o1 > o2.
import java.util.Comparator; public class Dog implements Comparable<Dog> { ... public int compareTo(Dog uddaDog) { return this.size - uddaDog.size; } public static class NameComparator implements Comparator<Dog> { public int compare(Dog a, Dog b) { return a.name.compareTo(b.name); } } }
Since the nested class NameComparator does not use any member variables (instance variables) of the external class Dog, you can optimize memory space with static instead
In DogLauncher. Calls in Java take the form of:
Dog.NameComparator nc = new Dog.NameComparator();
This is not the case with modern Java code styles, however. Consider wrapping the class NameComparator and modifying it to private:
private static class NameComparator implements Comparator<Dog> { public int compare(Dog a, Dog b) { return a.name.compareTo(b.name); } } public static Comparator<Dog> getNameComparator() { return new NameComparator(); }
So here at DogLauncher. Called in java:
Comparator<Dog> cd = new Dog.NameComparator(); if (cd.compare(d1, d3) > 0) { d1.bark(); } else { d3.bark(); }
The effect is the same, the latter being modern code style
Interfaces provide us with the ability to make function callbacks.
- Sometimes a function needs the help of another function that may not have been written out yet.
- For example: max requires compareTo
- This auxiliary function is sometimes called a callback.
- Some languages use explicit function passing to handle this problem, such as python,javascript.
- In Java, we do this by encapsulating the required functions in an interface (for example, Arrays.sort requires comparison, which is in the comparator interface).
- Arrays.sort "callbacks" when comparisons are needed.
- It's like giving someone your number when they need information.