Extends, Casting, Higher Order Functions
Although interfaces provide us a way to define a hierarchical relationship, the extends
key word let us define a hierarchical relationship between classes.
If we want to define a new class which have all methods in the SLList
but new ones such as RotatingRight
.
public class RotatingSLList<Item> extends SLList<Item>
With extends
key word, the subclass withh inhert all these components:
All instance and static variables
All methods
All nested classes
VengefulSLList
We could build a VengefulSLList
class to make a list that could remeber the deleted items. super
key word could be used to call the corresponding method in the super 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
While constructors are not inherited, Java requires that all constructors must start with a call to one of its superclass's constructors. If you don't call it explicitly, Java will automatically call it for you. If we forget to specify which contructor to use, Java will call the default one without parameters.
The Object Class
Every class in Java is a descendant of the Object class, or extends the Object class. Even classes that do not have an explicit extends in their class still implicitly extend the Object class.
Encapsulation
A model is a set of methods working together to perform some task or set of related tasks.
A model is said to be encapsulated if its implementation is highly hidden: It can be accessed merely through the documented interfaces.
Type Checking and Casting
Compilers will check types of objects based on its staitc type. For instance, the following code will result in a compile-time error since the compiler thinks that SLList
does not have the printLostItem
method and vsl2
can't contain the SLList
object.
VengefulSLList<Integer> vsl = new VengefulSLList<Integer>(9);
SLList<Integer> sl = vsl;
sl.printLostItems();
VengefulSLList<Integer> vsl2 = sl;
Expressions
As we seen above, expression with new
key word has compile-time types.
SLList<Integer> sl = new VengefulSLList<Integer>();
Above, the compile-time type of the right-hand side of the expression is VengefulSLList
. The compiler checks to make sure that VengefulSLList
"is-a" SLList
, and allows this assignment.
Method
The type of a method's return value is the method's compile-time type. Since the return type of maxDog
is Dog
, any call to maxDog
will have compile-time type Dog
.
Poodle frank = new Poodle("Frank", 5);
Poodle frankJr = new Poodle("Frank Jr.", 15);
Dog largerDog = maxDog(frank, frankJr);
Poodle largerPoodle = maxDog(frank, frankJr); //does not compile! RHS has compile-time type Dog
Casting
We could specify the type of an expression or a method to let Java compiler ignore type check. That might be dangerous and may cause run-time errors.
Poodle largerPoodle = (Poodle) maxDog(frank, frankJr); // compiles! Right hand side has compile-time type Poodle after casting
High Order Functions
In Python, we could define a function that will take another function as a parameter.
def tenX(x):
return 10 * x
def do_twice(f, x):
return f(f(x))
In Java, we could do so by declaring an interface.
public interface IntUnaryFunction{
int apply(int x) {}
}
public class TenX implements IntUnaryFunction{
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));
Last updated