Inheritance
Hypernym and Hyponym
Interface: the Hypernym
- All methods must be public, therefore
public
is redundant. - All variables are public static final (value can never change)
- Cannot be instantiated
- Can implement more than one interface per class
Interface Inheritance
- What the subclasses are able to do
- Abstract methods: no {}
- Provide method declarations
public interface List61B<Item> {
public void addFirst(Item x);
public void add Last(Item y);
public Item getFirst();
public Item getLast();
public Item removeLast();
public Item get(int i);
public void insert(Item x, int position);
public int size();
}
Implementation Inheritance
- How the subclasses should behave
- Default (concrete) methods
- Provide implementation with keyword
default
default public void print() {
for (int i = 0; i < size(); i += 1) {
System.out.print(get(i) + " ");
}
System.out.println();
}
Subclass Definition: the Hyponym
public class AList<Item> implements List61B<Item>{...}
Overriding
@Override
tag before subclass method signature- Run time polymorphism
- Derived class provides the specific implementation of the method that is already provided by the base class or parent class. Return type must be same or co-variant (return type may vary in same direction as the derived class).
@Override
public void addFirst(Item x) {
insert(x, 0);
}
Dynamic method selection
List61B<String> lst = new SLList<String>();
List61B
is the “static type” andSLList
is the “dynamic type”lst
is a pointer ofList61B
(static) type pointer pointing to aSLList
(dynamic) type object- Does not work for Overloading
- Java checks static type to see which matches the method signature
Extends
public class RotatingSLList<Item> extends SLList<Item>
The extends
keyword lets us keep the original functionality of SLList, while enabling us to make modifications and add additional functionality.
implements
defines the relationship between interface and classes, whereas extends
defines the relationship between classes. Child class is a parrent class.
By using the extends
keyword, subclasses inherit all members of the parent class. “Members” includes:
- All instance and static variables
- All methods
- All nested classes
Constructors are not inherited, and private members cannot be directly accessed by subclasses.
Example: VengefulSLList
@Override
tag: Overrides theremoveLast()
method in SLListsuper
: access the member in parent class
public class VengefulSLList<Item> extends SLList<Item> {
SLList<Item> deletedItems;
public VengefulSLList() {
deletedItems = new SLList<Item>();
}
@Override
public Item removeLast() {
Item x = super.removeLast();
deletedItems.addLast(x);
return x;
}
/** Prints deleted items. */
public void printLostItems() {
deletedItems.print();
}
}
Constructors: the Not-Inherited
Explicit: constructors must start with a call to one of its superclass’s constructors. Pass proper parameters when calling super()
.
Implicit: Or, if we choose not to, Java will automatically make a call to the superclass’s no-argument constructor for us.
The Object Class
All the classes extends
Object implicitly. As seen in the documentation for the Object class, the Object class provides operations that every Object should be able to do.
Type checking
If method is overidden, dynamic method selection. If not overriden, use static method.
Compiler determines whether or not something is valid based on the static type of the object. Even though dynamic type is valid, still generates error.
Compile-time (static) type of expressions
Assignment
SLList<Integer> sl = new VengefulSLList<Integer>();
VALID: Above, the compile-time type of the RHS is VengefulSLList. The compiler checks to make sure that VengefulSLList “is-a” SLList, and allows this assignment.
VengefulSLList<Integer> vsl = new SLList<Integer>();
ERROR: Above, the compile-time type of the RHS is SLList. The compiler checks if SLList “is-a” VengefulSLList, which it is not in all cases, and thus a compilation error results.
Method calls
public static Dog maxDog(Dog d1, Dog d2) { ... }
The static type of maxDog
is Dog
.
Poodle frank = new Poodle("Frank", 5);
Poodle frankJr = new Poodle("Frank Jr.", 15);
Dog largerDog = maxDog(frank, frankJr);
Poodle largerPoodle = maxDog(frank, frankJr); //A dog is a poodle? Does not compile! RHS has compile-time type Dog
Casting
Poodle largerPoodle = (Poodle) maxDog(frank, frankJr);
// compiles! RHS has compile-time type Poodle after casting
Casting is dangerous. Casting is telling the compiler not to do its type-checking duties - telling it to trust you and act the way you want it to. May compile but throw error during run time.
Higher Order Functions
In Python:
def tenX(x):
return 10*x
def do_twice(f, x):
return f(f(x))
In Java:
The initial problem: no function pointer
public interface IntUnaryFunction {
int apply(int x);
}
public class TenX implements IntUnaryFunction {
/* Returns ten times the argument. */
public int apply(int x) {
return 10 * x;
}
}
public static int do_twice(IntUnaryFunction f, int x) {
return f.apply(f.apply(x));
}
System.out.println(do_twice(new TenX(), 2));
Subtype Polymorphism
Example: Comparable
Compare two objects and return the max.
max function
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;
}
public static void main(String[] args) {
Dog[] dogs = {new Dog("Elyse", 3), new Dog("Sture", 9), new Dog("Benjamin", 15)};
Dog maxDog = maxDog(dogs);
maxDog.bark();
}
Deficiency:
- Code redundancy: specific to dog not general enough, inflexible measure of comparison.
- fundamental issue: Objects cannot be compared with
>
OurComparable
public interface OurComparable {
public int compareTo(Object o);
}
Define a interface for objects that are able to compare to other objects.
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;
}
}
Notice that since compareTo
takes in any arbitrary Object o, we have to cast the input to a Dog to make our comparison using the size
instance variable.
public static OurComparable max(OurComparable[] items) {
int maxDex = 0;
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
function can take in an array of any OurComparable
type objects and return the maximum object in the array.
Advantages:
- max function can be used by any object that implements ourComparable.
- Do not need to have a max function for each class. Only a compareTo() would do.
Deficiencies:
- Not a built-in interface. There are tons of classes that do not have compareTo() function.
- Wierd casting.
Comparable
public class Dog implements Comparable<Dog> {
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(Dog uddaDog) {
return this.size - uddaDog.size;
}
}
Notice that since compareTo
takes in Dog now. It avoids wierd casting as seen in advantages section.
public static Comparable max(Comparable[] items) {
// Not Comparable<Dog>[]
int maxDex = 0;
for (int i = 0; i < items.length; i += 1) {
int cmp = items[i].compareTo(items[maxDex]);
if (cmp > 0) {
maxDex = i;
}
}
return items[maxDex];
}
Changes OurComparable to Comparable in max function.
public static void main(String[] args) {
Dog[] dogs = {new Dog("Elyse", 3), new Dog("Sture", 9), new Dog("Benjamin", 15)};
Dog maxDog = (Dog) Maximizer.max(dogs);
maxDog.bark();
}
Main function does not change. Still need casting at return type of max function.
Advantages:
- Classes like String use Comparable
- Libraries like
Array.sort()
,Collection.max()
use Comparable - Avoids casting: see below
The better Comparable avoids casting:
public class Dog implements Comparable<Dog> {
public int compareTo(Dog uddaDog) {
return this.size - uddaDog.size;
}
}
OurComparable: Wierd casting
public class Dog implements OurComparable {
public int compareTo(Object obj) {
Dog uddaDog = (Dog) obj;
return this.size - uddaDog.size;
}
}
Deficiency:
Dog maxDog = (Dog) Maximizer.max(dogs);
still uses casting- Can only compare objects from same class
- Does not support multiple ordering
Comparator
Comparator
is a java interface. The object that implements Comparator must have a compare method.
public interface Comparator<T> {
int compare(T o1, T o2);
}
Since a comparator is an object, we’ll use Comparator
by writing a nested class inside Dog that implements the Comparator
interface. The Dog class now has a NameComparator
in it, which has a compare method, which defers to String
's already defined compareTo
method.
import java.util.Comparator;
public class Dog implements Comparable<Dog> {
...
public int compareTo(Dog uddaDog) {
return this.size - uddaDog.size;
}
private static class NameComparator implements Comparator<Dog> {
public int compare(Dog a, Dog b) {
return a.name.compareTo(b.name);
// deferring to String's compareTo()
}
}
public static Comparator<Dog> getNameComparator() {
return new NameComparator();
}
}
The NameComparator is a static class because we do not need to instantiate a Dog to get a NameComparator.
If there is no getNameComparator()
in the Dog
class, in main()
, we can use NameComparator
like this:
Dog.NameComparator<Dog> nc = new Dog.NameComparator();
if(nc.compare(d1,d3)>0) ...
To improve, create a getter function for main
to use the NameComparator
:
import java.util.Comparator;
....
//NameComparator is the subclass of Comparator<Dog>
Comparator<Dog> nc = Dog.getNameComparator();
if(nc.compare(d1,d3)>0) ...
Comparator
is like a third-party machine that compares two objects whereas Comparable
compares another object to itself because itself is a comparable object.
Java Libraries
List, Set, Map Demo
List: Method getWords
takes in a String inputFileName
and puts every word into a list.
package: import java.util.List; import java.util.ArrayList;
public static List<String> getWords(String inputFileName) {
List<String> lst = new ArrayList<String>();
In in = new In();
while (!in.isEmpty()) {
lst.add(in.readString()); //optionally, define a cleanString() method that cleans the string first.
}
return lst;
}
Set: Method countUniqueWords
takes in a List<String>
and counts how many unique words there are in the file.
package: import java.util.Set; import java.util.HashSet;
public static int countUniqueWords(List<String> words) {
Set<String> ss = new HashSet<>();
for (String s : words) { // enhanced for loop
ss.add(s);
}
return ss.size();
}
**Map: **Method collectWordCount
takes in a taget and words List<String>
and counts how many times words in target appears in the words list.
package: import java.util.Map; import java.util.HashMap;
public static Map<String, Integer> collectWordCount(List<String> words, List<String> target) {
Map<String, Integer> counts = new HashMap<String, Integer>();
for (String t: target) { // set up empty counts
counts.put(s, 0);
}
for (String s: words) { // actually count the words
if (counts.containsKey(s)) {
counts.put(word, counts.get(s)+1);
}
}
return counts;
}
Why java?
-
Takes less time to write programs:
- Static types (type checking and helps guide programmer)
- Force interface inheritance leading to cleaner subtype polymorphism
- Access control modifiers
-
More efficient code:
-
more control over engineering tradeoffs (ADTs)
-
Single valued arrays lead to better performance (?)
-
-
Basic data structures more closely resemble underlying hardware
Abstract Classes
An intermediate level between interfaces and classes.
Interface | Abstract Class |
---|---|
Methods must be public | Methods can be public or private |
Variables must be public static final | Can have any types of variables |
Cannot be instantiated | Cannot be instantiated |
Methods are by default abstract unless specified to be default | Methods are by default concrete unless specified to be abstract |
Can implement more than one interface per class | Can only implement one per class |
When in doubt, use interfaces.
Packages
Package names give give a canonical name for everything.
import ug.joshh.animal.Dog
or import ug.joshh.animal.*