CS 61B | Lecture 3 ~ 7 | Testing, Inheritance, Type Checking, and Java Stuff


Testing

Thinking Process in Selection Sort

Often, development is an incremental process that involves lots of task switching and on the fly design modification.

Tests provide stability and scaffolding:

  • Provide confidence in basic units and mitigate possibility of breaking them.
  • Help you focus on one task at a time.

In large projects, test also allow you to safely refactor (still have the same function)!

Testing Philosophy

Autograder Driven Development (ADD)

The worst way to approach programming. This workflow is slow and unsafe!

Unit Tests

But, how do you test units that rely on others. e.g. how do you test addFirst in Project 1?

Test-Driven Development (TDD)

Integration Testing

Idea: Tests cover many units at once.

Not JUnit’s focus, but JUnit can do this.

But it tests at highest level of abstraction what may miss subtle or rare errors.

Exercise in Study Guide

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void testEquals() {
/** Arrays */
int[][] a1 = {{1, 2, 3}, {2}, {3}};
int[][] a2 = {{1, 2, 4}, {2}, {3}};
assertArrayEquals(a1, a2); /* Test fails */
/** String (Wrapping Class - Compare values) */
String s1 = "123123123";
String s2 = "123123123";
String s3 = new String("123123123");
String s4 = new String("123123123");
assertEquals(s1, s2); /* Test pass! */
assertEquals(s3, s4); /* Test pass! */
assertEquals(s1, s4); /* Test pass! */
/** Other Classes - Compare addresses*/
Object o1 = new Object();
Object o2 = new Object();
assertEquals(o1, o2); /* Test fails */
}

Inheritance, Implements

Interface inheritance (what):

  • Subclass inherits signatures, but NOT implementation

Implementation inheritance (how):

  • Subclasses can inherit signatures AND implementation.

If Y overrides the method, Y’s method is used instead. This is known as dynamic method selection.

Implementation inheritance may sound nice and all but there are some drawbacks:

  • We are fallible humans, and we can’t keep track of everything, so it’s possible that you overrode a method but forgot you did.
  • It may be hard to resolve conflicts in case two interfaces give conflicting default methods.
  • It encourages overly complex code

Extends, Casting, Higher Order Functions

Encapsulation

When building large programs, our enemy is complexity.

Ways of managing complexity (ultimately determine whether a programmer is successful):

  • Hierarchical abstraction (List, LinkedList, ArrayList)
  • Design for change (D. Parnas)
    • Organize program around objects
    • Let objects decide how things are done
    • Hide information others don’t need

Even when writing tests, you don’t (usually) want to peer inside.

Ideally, a user should not be able to observe the internal workings of, say, a data structure they are using. Java is a great language for enforcing abstraction barriers with syntax.

Inheritance Breaks Encapsulation

By allowing inheritance in a particular situation, we now have a way to peek inside a beautifully encapsulated class. We didn’t change anything in overridden barkMany, but someone changed the superclass implementation that they thought it was safe and kapow (爽).

Type Checking and Casting

Type Checking

Static type is also called compile-time type.

If a method in SLList is overridden by the VengefulSLList class, then the method that is called at runtime is determined by the run-time type (dynamic type) of that variable (dynamic method selection).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
VengefulSLList<Integer> vsl = new VengefulSLList<Integer>(9);
/* Static type: VengefulSLList<Integer> */
/* Dynamic type: VengefulSLList<Integer> */
SLList<Integer> sl = vsl;
/* Static type: SLList<Integer> */
/* Dynamic type: VengefulSLList<Integer> */

sl.addLast(50); /* Not overridden, call static type */
sl.removeLast(); /* Overridden, call dynamic type VengefulSLList<Integer> */

/** Compile-time Error */
sl.printLostItems(); /* Compiler is conservative in type checking */
/* Not overridden, call static type. But SLList<Integer> has no idea about printLostItems() in compile-time, even later it knows in runtime. */
VengefulSLList<Integer> vsl2 = sl;
/* In general, the compiler only allows method calls and assignments based on compile-time types. Since the compiler only sees that the static type of sl is SLList, it will not allow a VengefulSLList "container" to hold it. */

Also, expressions using the new keyword also have compile-time types.

1
VengefulSLList<Integer> vsl = new SLList<Integer>();

Above, the compile-time type of the right-hand side of the expression is SLList. The compiler checks if SLList “is-a” VengefulSLList, which it is not in all cases, and thus a compilation error results.

Further, method calls have compile-time types equal to their declared type. Suppose we have this method:

1
2
3
4
5
6
7
8
public static Dog maxDog(Dog d1, Dog d2) { ... }
/* 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 (right hand side) has compile-time type Dog

Casting

Java has a special syntax where you can tell the compiler that a specific expression has a specific compile-time type. This is called casting. With casting, we can tell the compiler to view an expression as a different compile-time type, ignoring its type checking duties.

1
2
3
4
Poodle frank = new Poodle("Frank", 5);
Malamute frankSr = new Malamute("Frank Sr.", 100);
Poodle largerPoodle = (Poodle) maxDog(frank, frankJr);
/* At runtime, it'll crash when maxDog returns the Malamute. */
1
2
Animal a = (Dog) new Animal(); /* Runtime Error */
Dog a = (Dog) new Animal(); /* Runtime Error */

Has Nothing To Do With overriding

1
2
Dog d = new Dog();
((Animal) d).makeNoise(d); /* the left d will do type checking, Animal is supposed to have makeNoise method */

((Animal) d).makeNoise(d);: When checking to see if a method exists in Animal, do we check for a makeNoise(Dog) method or a makeNoise(Animal) method? Yes. In particular, the compiler sees if ANY method exists that accepts Dog. If it happens to have both, then it’ll use makeNoise(Dog) because this is the most specific version.

THIS IS TOTALLY DISTINCT FROM DYNAMIC METHOD SELECTION. Has nothing to do with overriding.

1
2
3
4
5
6
7
8
9
10
class Dog extends Animal {
@Override
public void print(Animal a) {
System.out.println("Dog1");
}
/* Overload */
public void print(Dog d) {
System.out.println("Dog2");
}
}
1
2
3
4
5
Animal a = (Animal) new Dog();
a.print(new Dog()); /* Dog1 */
a.print(new Animal()); /* Dog1 */
(new Dog()).print(new Animal()); /* Dog1 */
(new Dog()).print(new Dog()); /* Dog2 */

If the instance’s class is Animal, it will definitely goes for the print(Animal) version. If the instance’s class is Dog, it will check the parameter for the specific one.

Higher Order Functions (HoFs)

Higher Order Function: A function that treats another function as data.

1
2
3
4
5
6
7
8
# In Python
def tenX(x):
return 10 * x

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

print(do_twice(tenX, 2))

In old school Java (Java 7 and earlier), memory boxes (variables) could not contain pointers to functions. What that means is that we could not write a function that has a “Function” type, as there was simply no type for functions.

Interface:

1
2
3
public interface IntUnaryFunction {
public int apply(int x);
}

Implementation:

1
2
3
4
5
public class TenX implements IntUnaryFunction {
public int apply(int x) {
return 10 * x;
}
}

Using:

1
2
3
4
5
6
public static int do_twice(IntUnaryFunction f, int x) {
return f.apply(f.apply(x));
}
public static void main(String[] args) {
do_twice(new TenX(), 2);
}

Exam Prep 4

Playing with Puppers

1
2
3
4
5
6
7
8
9
10
11
public class Dog {
public void bark(Dog d) { /* Method A */ }
}

public class Corgi extends Dog {
public void bark(Corgi c) { /* Method B */ }
@Override
public void bark(Dog d) { /* Method C */ }
public void play(Dog d) { /* Method D */ }
public void play(Corgi c) { /* Method E */ }
}

For the following main method, at each call to play or bark, tell us what happens at runtime by selecting which method is run or if there is a compiler error or runtime error.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) {
Dog d = new Corgi();
Corgi c = new Corgi();

d.play(d); /* Compile-Error */
d.play(c); /* Compile-Error */
c.play(d); /* Method D */
c.play(c); /* Method E */

c.bark(d); /* Method C */
c.bark(c); /* Method B */
d.bark(d); /* Method C (dynamic selecting) */
d.bark(c); /* Method C (dynamic selecting), not B */
}

Cast the line

Consider each line independently whether it causes a compilation error, runtime error, or runs successfully.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Animal   Tree
// ^ ^
// | \
// Cat Dog
public static void main(String[] args) {
Cat c = new Animal(); // Compile Error
Animal a = new Cat(); // No Error
Dog d = new Cat(); // Compile Error
Tree t = new Animal(); // Compile Error

Animal a = (Cat) new Cat(); // No Error
Animal a = (Animal) new Cat(); // No Error
Dog d = (Dog) new Animal(); // Runtime Error
Cat c = (Cat) new Dog(); // Compile Error (not up or down casting)
Animal a = (Animal) new Tree(); // Compile Error
}

SLList Vista

indexOf

1
2
3
4
5
6
7
8
9
10
public int indexOf(int x) {
int count = 0;
IntList p = this.first;
while (p != null) {
if (p.item == x) { return count; }
p = p.next;
count += 1;
}
return -1;
}

Subtype Polymorphism vs. HoFs

Polymorphism, at its core, means 'many forms'.

  • In Java, polymorphism refers to how objects can have many forms or types.

  • In object-oriented programming, polymorphism relates to how an object can be regarded as an instance of its own class, an instance of its superclass, an instance of its superclass’s superclass, and so on.

Explicit Higher Order Functions vs. Subtype Polymorphism

1
2
3
4
5
6
7
8
9
10
11
# Explicit HoF Approach
def print_larger(x, y, compare, stringify): # callback: a function needs the help of another function (callback) that might not have been written yet (function passing)
if compare(x, y):
return stringify(x)
return stringify(y)

# Subtype Polymorphism Approach
def print_larger(x, y):
if x.largerThan(y): # x object makes its choices
return x.str()
return y.str()

In Python or C++, the way that the > operator works could be redefined to work in different ways when applied to different types.

Unfortunately, Java does not have this capability. Instead, we turn to interface inheritance to help us out. We do this by wrapping up the needed function in an interface (e.g. Arrays.sort needs compare which lives inside the comparator interface). Arrays.sort “calls back” whenever it needs a comparison.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface OurComparable {
public int compareTo(Object o);
}

public class Dog implements OurComparable {

private String name;
private int size;

public int compareTo(Object o) { /* or Comparable */
Dog uddaDog = (Dog) o; /* casting */
return this.size - uddaDog.size;
}
}

So we can generalize the max function.

1
2
3
4
5
6
7
8
9
10
11
/* Maximizer class */
public static OurComparable max(OurComparable[] items) {
int maxDex = 0;
for (int i = 0; i < items.length - 1; i += 1) {
int cmp = items[i].compareTo(items[maxDex]);
if (cmp > 0) {
maxDex = i;
}
}
return items[maxDex];
}

What are the benefits to this approach?

  • No need for maximization code in every class(i.e. no Dog.maxDog(Dog[]) function required
  • We have code that operates on multiple types (mostly) gracefully

Comparables

The OurComparable interface that we just built works, but it’s not perfect. Here are some issues with it:

  • Awkward casting to/from Objects

    1
    2
    3
    4
    public int compareTo(Object o) { /* or Comparable */
    Dog uddaDog = (Dog) o; /* casting */
    return this.size - uddaDog.size;
    }
  • We made it up.

    • No existing classes implement OurComparable (e.g. String, etc.)
    • No existing classes use OurComparable (e.g. no built-in max function that uses OurComparable)

The solution? We’ll take advantage of an interface that already exists called Comparable. Comparable is already defined by Java and is used by countless libraries.

Comparable looks very similar to the OurComparable interface we made, but with one main difference. Can you spot it?

1
2
3
public interface Comparable<T> {
public int compareTo(T obj); /* The generic type will help us avoid having to cast an object to a specific type! */
}

Comparator

Let’s start off by defining some terminology.

  • Natural order - used to refer to the ordering implied in the compareTo method of a particular class. (Dog - size)

What if we’d like to sort Dogs in a different way than their natural ordering, such as by alphabetical order of their name?

In explicit HoF approach, we can use compare to specify a different compare function. However, in subtype polymorphism approach, compareTo1, compareTo2, and so forth? No!

1
2
3
4
def print_larger(T x, T y, comparator<T> c):
if c.compare(x, y):
return x.str()
return y.str()

Java’s way of doing this is by using Comparator‘s. Since a comparator is an object, the way we’ll use Comparator is by writing a nested class inside Dog that implements the Comparator interface.

1
2
3
public interface Comparator<T> {
int compare(T o1, T o2);
}

Now, the Dog class should be like:

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

public class Dog implements Comparable<Dog> {
// ...
public int compareTo(Dog uddaDog) {
return this.size - uddaDog.size;
}
/* Nested class */
// It is static because we don't need to instantiate a Dog to get a NameComparator
private static class NameComparator implements Comparator<Dog> {
public int compare(Dog a, Dog b) {
return a.name.compareTo(b.name);
}
}

/** getNameComparator()
* public: accessible (set NameComparator class as private)
* static: Dog.getNameComparator()
*/
public static Comparator<Dog> getNameComparator() {
return new NameComparator();
}
// ...
}

In DogLauncher, the code is:

1
2
3
Dog.NameComparator nc = Dog.getNameComparator();
/* or */
Comparator<Dog> nc = Dog.getNameComparator();

In summary, a Comparable says, “I want to compare myself to another object”. It is imbedded within the object itself, and it defines the natural ordering of a type.

However, a Comparator, on the other hand, is more like a third party machine that compares two objects to each other. Since there’s only room for one compareTo method, if we want multiple ways to compare, we must turn to Comparator.

Abstract Data Types (ADT)

In Java, Deque is called an interface. Conceptually, we call deque an Abstract data type. Deque only comes with behaviors, not any concrete ways to exhibit those behaviors. In this way, it is abstract.

Stack: LIFO property
Queue: FIFO property
Deque: Both LIFO and FIFO

Interface Summary (Details)

Interfaces:

  • Cannot be instantiated
  • Can provide either abstract or concrete methods.
    • Use no keyword for abstract methods
    • Use default keyword for concrete methods
  • Can provide only public static final variables
  • Can provide only public methods

Abstract class

Abstract class doesn’t have to implement the method listed in interface.

Interfaces:

  • Primarily for interface inheritance. Limited implementation inheritance.
  • Classes can implement multiple interfaces.

Abstract classes:

  • Can do anything an interface can do, and more.
  • Subclasses only extend one abstract class.

A More Accurate Hierarchy For Lists

AbstractList provides default implementations for methods. But why not just put them in List itself? No default methods in Java interfaces until 2014, and the AbstractList was public so can’t just throw it away.

When in doubt, try to use interfaces in order to reduce complexity.

Discussion 5

Some scenes of application:

  • Given a news article, find the frequency of each word used in the article

    Use a map.

  • Given an unsorted array of integers, return the array sorted from least to greatest

    Use a priority queue. For each integer in the unsorted array, enqueue the integer with a priority equal to its value.

  • Implement the forward and back buttons for a web browser

    Use two stacks, one for each button. Each time you visit a new web page, add the previous page to the back button’s stack.

    When you click the back button, add the current page to the forward button stack, and pop a page from the back button stack.

    When you click the forward button, add the current page to the back button stack, and pop a page from the forward button stack.

    Every time you visit a new page, clear the forward button stack.

Define a Queue using Stacks

Using two stacks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Queue {
private Stack stack = new Stack();
/** push */
public void push(E element) {
Stack temp = new Stack();
while (!stack.isEmpty()) {
temp.push(stack.poll());
}
stack.push(element);
while (!temp.isEmpty()) {
stack.push(temp.poll());
}
}
/** poll */
public E poll() { return stack.poll(); }
}

Recursive version:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Queue {
private Stack stack = new Stack();
/** push */
public void push(E element) { stack.push(element); }
/** poll */
public E poll() { return poll(stack.pop()); }
private E poll(E previous) {
if (stack.isEmpty()) { /* only one element left, just pop it */
return previous;
}
E toReturn = poll(stack.pop());
/* push it back to the stack */
push(previous);
return toReturn;
}
}

Ex 5

Use Them!

  • Given an array of integers A and an integer k, return true if any two numbers in the array sum up to k, and return false otherwise. How would you do this? Give the main idea and what ADT you would use.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public boolean twoSum(int[] A, int k) {
    Set<Integer> prevSeen = new HashSet<>();
    for (int i : A) {
    if (prevSeen.contains(k - i)) {
    return true;
    }
    prevSeen.add(i);
    }
    }
  • Find the k most common words in a document. Assume that you can represent this as an array of Strings, where each word is an element in the array. You might find using multiple data structures useful.

    Keep a count of all the words in the document using a HashMap<String, Integer>. After we go through all of the words, each word will be mapped to how many times it’s appeared.

    Then we can put all the words into a MaxPriorityQueue<String>, using a custom comparator that compares words based on the counts in the HashMap. We can then pop off the k most common words by just calling poll() on the MaxPriorityQueue k times.

    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
    public static void topFivePopularWords(String[] words, int k) {
    /** Counting words */
    Map<String, Integer> counts = new HashMap<>();
    for (String w : words) {
    if (!counts.containsKey(w)) {
    counts.put(w, 1);
    } else {
    counts.put(w, counts.get(w) + 1);
    }
    }
    /** Comparator */
    PriorityQueue<String> pq = new PriorityQueue<>(new Comparator<String>(){
    @Override
    public int compare(String a, String b) {
    return counts.get(b) - counts.get(a);
    }
    });

    for (String w : counts.keySet()) {
    pq.add(w); /* automatically put in order */
    }
    for (int i = 0; i < k; i += 1) {
    System.out.println(pq.poll());
    }
    }

Mutant ADTs

Suppose we wanted a data structure SortedStack that takes in integers, and maintains them in sorted order. SortedStack supports two operations: push(int i) and pop(). Popping returns the next smallest item in the SortedStack.

For example, if we inserted [10, 4, 8, 2, 14, 3] into a SortedStack, and then popped everything off, we would get [2, 3, 4, 8, 10, 14].

My solution (Not using stacks):

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
28
29
30
31
public class SortedStack() {

SLList items;
public SortedStack() {
items = new SLList();
}
/** Push */
public void push(int x) {
if (items.size == 0) {
items.addLast(x);
return;
}
/* From big to small number (First -> Last)*/
IntNode ptr = items.sentinel.next; /* First element */
while (ptr != sentinel) {
if (x <= ptr.item) {
/** Insert! But we have no related function of SLList */
ptr.next = new IntNode(x, ptr.next);
break;
}
ptr = ptr.next;
}
}
/** Pop */
public int pop() {
if (items.size == 0) {
return -1;
}
return items.removeLast();
}
}

Answer (using stacks):

We have two stacks A and B. A holds all the items, and B is our buffer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SortedStack<Item extends Comparable<Item>> {
private Stack<Item> main;
private Stack<Item> buffer;

public SortedStack() {
main = new Stack<>();
buffer = new Stack<>();
}

public void push(Item t) {
while (!main.empty() && main.peek().compareTo(t) < 0) {
/** Items that are less than should be put in the buffer first*/
buffer.push(main.poll());
}
main.push(t);
while (!buffer.empty( )) {
main.push(buffer.poll());
}
}

public Item poll() {
return main.poll();
}
}

Package

Package names give give a canonical name for everything. Canonical means a unique representation for a thing.

It is not really importing something (looking for files).

The built-in java.util package provides a number of useful interfaces and implementations.

Creating A Package

It is to address the fact that classes might share names:

  • A package is a namespace that organizes classes and interfaces.
  • Naming convention: Package name starts with website address (backwards).

Two steps:

  • At the top of every file in the package, put the package name.
  • Make sure that the file is stored in a folder with the appropriate folder name. For a package with name ug.joshh.animal, use folder ug/joshh.animal.

The Default Package

Any Java class without a package name at the top are part of the “default” package. In other words, from now on, when writing real programs, your Java files should always start with a package declaration.

Idea: Ensure that we never have two classes with the same name.

But, you cannot import code from the default package! For example, if I were to create a “DogLauncher.java” class in the default package, I would be unable to access this DogLauncher class anywhere else outside of the default package.

1
2
DogLauncher.launch(); // don't work
default.DogLauncher.launch(); // doesn't exist

Exercise

C level:

  • If an abstract class extends an abstract class, would it need to have function definitions for the abstract methods in the parent class? Similarly would an interface that implements another interface have to implement the non default methods in the parent interface.

    No. No.

  • Can an abstract class be the subclass of a normal class?

    Yes. (The default package)

  • If you don’t specify the package a class is in, is it part of a package? If so, which package?

    A class that is not in a named package is in an unnamed package.

    Such classes cannot be used from a named package, except via reflection.

B level:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Tree {
public void bark() {
System.out.println("haha - Tree");
}
}

interface Dog {
default void bark() {
System.out.println("haha - Dog");
}
}

public class Blevel extends Tree implements Dog {
public static void main(String[] args) {
Blevel blevel = new Blevel();
/* Compile and print: */
blevel.bark(); /* haha - Tree */
}
}

JAR Files

JAR file are just zip files.

  • They do not keep your code safe!
  • Easy to unzip and transform back into .Java files (from .class).
  • Important: Do not share .jar files of your projects with other students.

Conversion

Autoboxing and Unboxing

How it works:

  • If Java code expects a wrapper type and gets a primitive, it is autoboxed.
  • If the code expects a primitive and gets a wrapper, it is unboxed.
1
2
3
4
5
6
7
8
/* Not a magic */
public final class Integer extends Number implements Comparable<Integer> {
private final int value;
public Integer(int value) {
this.value = value;
}
// ...
}

Note:

  • Arrays are never autoboxed/unboxed, e.g. an Integer[] cannot be used in place of an int[] (or vice versa).
  • Autoboxing / unboxing incurs a measurable performance impact!
  • Wrapper types use MUCH more memory than primitive types.

Assuming:

  • Addresses are 64 bits.
  • ints are 32 bits.
  • All Java objects take 64 bits + their fields.
1
2
3
4
5
public static void bleepblorp() {
Integer x = new Integer(5);
/* 64 (address) + 64 (other information) + 32 (int) = 160 */
System.out.println(x);
}

Primitive Widening

1
2
double a = 5.0;
int b = a; /* Incompatible ty */

Access Control

How do public and private behave with packages and subclasses?

Private: Only code from the given class can access private members. It is truly private from everything else, as subclasses, packages, and other external classes cannot access private members. TL;DR: only the class needs this piece of code

Package Private: This is the default access given to Java members if there is no explicit modifier written. Package private entails that classes that belong in the same package can access, but not subclasses!

The original owners of the class that’s being extended may not want certain features or members to be tampered with, if people choose to extend it — hence, package-private allows those who are familiar with the inner workings of the program to access and modify certain members, whereas it blocks those who are subclassing from doing the same. TL;DR: only classes that live in the same package can access.

Protected: Protected members are protected from the “outside” world, so classes within the same package and subclasses can access these members, but the rest of the world (e.g. classes external to the package or non-subclasses) cannot! TL;DR: subtypes might need it, but subtype clients will not.

Public: This keyword opens up the access to everyone! This is generally what clients of the package can rely on to use, and once deployed, the public members’ signatures should not change. It’s like a promise and contract to people using this public code that it will always be accessible to them. Usually if developers want to “get rid of” something that’s public, rather than removing it, they would call it “deprecated” instead. TL;DR: open and promised to the world.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package syntax3.map;
public class ArrayMap
int size;
// ...

package syntax3.map;
public class MyonicArrayMap extends ArrayMap {
public MyonicArrayMap(int s) {
size = s; /* Will this work? Yes, because it is part of the same package */
/* Don't need to think about whether it is part of the same subclass */
/* Default access (package private) only supports Class, Package */
}
}
// ...

package syntax3.map;
public class MapHelper {
public static void printSize(ArrayMap am) {
System.out.println(am.size); // OK. It's part of the same package
}
}

Subtleties:

It is important to note that for interfaces, the default access for its methods is actually public, and not package-private.

Nested class

1
2
3
4
5
6
7
public class Outer {
public int outvar;

public class Inner {
public int invar;s
}
}

In the example above: Anybody can create an Outer, anybody can create an Inner, and anybody can access an Inner’s variable. And an Inner can access this.outvar.

1
2
3
4
5
6
7
public class Outer {
public int outvar;

private class Inner {
public int invar;
}
}

In the example above, Inner is private. At this point, the access modifiers to all of inners’ members are irrelevant.

  • An Outer CAN ACCESS an Inner’s variables. The reason is that “Inner” is a subordinate slave of Outer, and thus has no personal possessions.
  • We can change public (invar) to anything and have no observable effect.
  • Classes other than Outer cannot make or access Inner anymore. Common for things like LinkedListDeque (make your IntNode class private).
1
2
3
4
5
6
7
8
9
10
public class Outer {
private int outvar;

private class Inner {
public int invar;
public void test() {
outvar = 5; /* OKAY */
}
}
}

In the example above, can an Inner access an Outer’s outvar? Yes, it can.

What could I do so that an Inner can’t use outvar?

1
2
3
4
5
6
7
8
9
10
public class Outer {
private int outvar;

private static class Inner {
public int invar;
public void test() {
outvar = 5; /* I cannot access my enclosing instance's outvar */
}
}
}

Can I instantiate a non-static inner class without an outer instance

1
2
3
4
5
/** 1 */
Outer out = new Outer();
Outer.Inner in = out.new Inner();
/** 2 */
Outer.Inner in2 = new Outer().new Inner();

Discussion 6

Exception

There are 2 overarching types of exceptions:

  • Checked

    You must either wrap them in a try/catch block or pass the buck by using “throws exception” in the method header.

  • Unchecked

    You don’t need to handle these. They are typically an error on a user’s part, which can’t really be helped.

Iterators and Iterables

Final Modifier

1
2
3
4
5
6
7
8
9
10
11
12
13
public class PunkRock {
private final Rock[] band;
public PunkRock(Rock yRoad) {
band = { yRoad };
}
public Rock[] myBand() {
return band;
}
}

public class MommaRock {
public static final Pebble baby = new Pebble();
}

Ex 6 - Exception

Exceptions

Image Lost ???

AltList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public AltList<X, Y> pairSwapped() {
AltList<Y, X> ret = new AltList<Y, X>(next.item, new AltList<X, Y>(item, null));

/* Recursive */
if (next.next != null) {
ret.next.next = next.next.pairSwapped();
}

/* Iterative */
AltList<X, Y> ptr = next.next;
AltList<Y, X> newPtr = ret;
while (ptr != null) {
newPtr.next.next = new AltList<Y, X>(ptr.next.item, new AltList<X, Y>(ptr.item, null))
newPtr = newPtr.next.next;
ptr = ptr.next.next;
}

return ret;
}

Every K-th Element

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
28
29
30
31
32
33
34
35
36
37
38
39
40
public class KthIntList implements Iterator<Integer> {
public int k;
private IntList curList;
private boolean hasNext;

public KthIntList(IntList I, int k) {
this.k = k;
this.curList = I;
this.hasNext = true;
}

public boolean hasNext() {
return this.hasNext;
}

/* Returns the next K-th element of the IntList given in the constructor
* Returns the 0-th element firsts.Throws a NoSuchElementException if
* there are no Integers available to return.
*/
public Integer next() {

if (curList == null) {
throw new NoSuchElementException();
}

Integer ret = curList.item;

int count = k;
while (count > 0) {
curList = curList.next;
count -= 1;
if (curList == null) {
hasNext = false;
break;
}
}

return ret;
}
}
0%