CS61B(9): Extends, Casting, HoFs

This is the lecture note of CS61B - Lecture 9.

🌳 In this lecture, we will talk more about implementation inheritance.

Implementation Inheritance: Extends

In the last lecture, we talked about implements keyword. When a class is a hyponym of an interface, we use implements.

If you want one class to be a hyponym of another class, you use extends.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* SLList, but with additional rotateRight operation. */
public class RotatingSLList<Item> extends SLList<Item> {

/** Todo: Implement RotatingSLList such that code compiles
* and outputs correct result.
* */

/** Rotates list to the right. */
public void rotateRight() {
Item x = removeLast();
addFirst(x);
}

public static void main(String[] args) {
RotatingSLList<Integer> rel = new RotatingSLList<>();
rel.addLast(10);
rel.addLast(11);
rel.addLast(12);
rel.addLast(13);

rel.rotateRight();
rel.print(); // 13, 10, 11, 12
}
}

We should notice that constructors won't be inherited.

Let's look at another example. Suppose we want to build an SLList called VengefulSLList that:

  • Remembers all Items that have been destroyed by removeLast.
  • Has an additional method printLostItems(), which prints all deleted items.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class VengefulSLList {
// ...

public static void main(String[] args) {
VengefulSLList<Integer> vs1 = new VengefulSLList<Integer>();
vs1.addLast(1);
vs1.addLast(5);
vs1.addLast(10);
vs1.addLast(13); /* [1, 5, 10, 13] */
vs1.removeLast(); /* 13 gets deleted. */
vs1.removeLast(); /* 10 gets deleted. */
System.out.print("The fallen are: ");
vs1.printLostItems(); /* Should print 10 and 13. */
}
}

How should we do?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/** SList with additional operation printLostItems() which prints all items
* that have ever been deleted. */
public class VengefulSLList<Item> extends SLList<Item> {
SLList<Item> deletedItems;

public VengefulSLList() {
// super();
deletedItems = new SLList<Item>();
}

public VengefulSLList(Item x) {
super(x);
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();
}
}

Constructor Behavior

Important Note: both implements and extends should only be used for is-a relationship instead of has-a relationship.

Encapsulation

Module: A set of methods that work together as a whole to perform some task or set of related tasks.

A module is said to be encapsulated if its implementation is completely hidden, and it can be accessed only through a documented interface.

🌴 Reminder: You should use the idea of encapsulation to guide yourself when completing project1b, and other large project.

Type Checking and Casting

Higher Order Functions

Python is good at defining higher order functions (HOFs).

1
2
3
4
5
6
7
8
9
# example of HoF

def tenX(x):
return 10 * x

def do_twice(f, x):
return f(f(x))

do_twice(tenX, 2)

HoFs is powerful and useful. However, before Java7 and earlier, there is a fundamental issue that memory boxes(variable) cannot contain pointers to functions. So if you want to define HoFs in Java7 or earlier, you should use interface instead.

Let's see an example.

1
2
3
4
/** Represent a function that takes in an integer, and returns an integer. */
public interface IntUnaryFunction {
int apply(int x);
}
1
2
3
4
5
6
7
public class TenX implements IntUnaryFunction {
@Override
/** Returns ten times its argument. */
public int apply(int x) {
return 10 * x;
}
}
1
2
3
4
5
6
7
8
9
10
public class HoFDemo {
public static int do_twice(IntUnaryFunction f, int x) {
return f.apply(f.apply(x));
}

public static void main(String[] args) {
IntUnaryFunction tenX = new TenX();
System.out.println(do_twice(tenX, 2));
}
}

HoFs in Java8 or Later

In Java 8, new types were introduced, and you can hold references to methods.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.function.Function;

public class Java8HoFDemo {
public static int tenX(int x) {
return 10 * x;
}

public static int doTwice(Function<Integer, Integer> f, int x) {
return f.apply(f.apply(x));
}

public static void main(String[] args) {
int result = doTwice(Java8HoFDemo::tenX, 2);
System.out.println(result);
}
}

/*
<T> – the type of the input to the function
<R> – the type of the result of the function

public interface Function<T, R> {
// ....
}
*/