Generics Study Guide
Author: Kartik Kapur

Lecture Code

Code from this lecture available at

https://github.com/Berkeley-CS61B/lectureCode-sp18/tree/master/syntax1.

Overview

Autoboxing and Unboxing Autoboxing is the Java’s automatic conversion of between wrappers (Integer) to primitives (int). In most cases, if Java expects a wrapper class and gets a primitive instead, it autoboxes the primitive. Alternatively, if Java expects a primitive and gets a wrapper, it unboxes the wrapper.

Drawbacks of Autoboxing and Unboxing Though you can almost always interchange there are some things to the process.

Immutability Immutable data types are types that cannot change. To make sure that a variable does not change, use the final keyword. Once a variable is declared final, it can never change after initial assignment. An important note is that if an address is declared final it means that the address can’t change- it says nothing about its contents. For example the below syntax is valid:

final int[] arr = new int[1];
arr[0] = 1;
arr[0] = 3

But this one is not:

final int[] arr = new int[3];
arr = new int[4];

Because you are changing the address of the actual array the variable is pointing to.

Generic Classes To make it so that a class can have variables or methods that have a generic type, use the following syntax:

public class ArrayMap<K,V>{...}

Then when instantiating the class pass in some “real”, or known, types to the class

Generic Methods You can define a method that takes in generic parameters with the following syntax.

public static <Chow, Der> Chow get(ArrayMap<Chow, Der)> am, Chow key){...}

From left to right we have the declaration of the generics being used in this function then we have the return type. Finally, we have our arguments, the first being an ArrayMap with 2 generics and the latter being a generic type object.

To use a generic method use the following syntax

ArrayMap<Integer, String> ismap = new ArrayMap<Integer, String>();
System.out.println(MapHelper.get(ismap, 5));

Comparing Objects with Generic Methods Now we have the ability to put vague Objects into methods. However this lends itself to a bit of a problem- how do we compare these Objects? We cannot simply use ‘>’ because we aren’t sure if our object is a numerical primitive. We can get around this by using .compareTo(Object O).

Now we have a new problem. How do we know if our generic has a compareTo method. To get around this, we can make sure that our generic must be a type of our OurComparable. How do we do this? Well take a gander below and check it out.

public static <K extends OurComparable, V> K maxKey(ArrayMap<K, V> am) {
  ...
  if (k.compareTo(largest) > 0) {
    ...
}

Basically what’s happening is that, in the header, we ensure that K needs to extend OurComparable.

Exercises

C level

  1. Implement the ArrayMap Class that was done in lecture. It should contain the following methods
    • put(key, value): Associate key with value.
    • containsKey(key): Checks to see if arraymap contains the key.
    • get(key): Returns value, assuming key exists..
    • keys(): Returns a list of all keys.
    • size(): Returns number of keys.

B level

  1. Say we had a set of classes defined as follows:

     public interface Fat<Gar extends comparable, Field>{...}
     public class FatCat implements Feline<Chubster , Integer>{...}
     public class Chubster{...}
    

    Would this compile? Assume the class bodies are well defined.

  2. Is there a way to compare generics without ensuring the types in the class/method header extend OurComparable?

  3. Below are a few reference classes

     public class Grodie(){
       static final int cow;
     }
     public class OdeMiester(){
       public final int breath;
       public final Grodie gosh;
       public final Grodie[] arr;
     }
    

    Say which of the following lines would cause an error. Assume that if a compilation error exists the rest of the program can run without a problem.

     final OdeMiester groovy = new OdeMiester();
     groovy.breath = 5;
     groovy.gosh = new Grodie();
     groovy.gosh.cow = 10;
     arr = new Grodie[2];
     groovy.breath = 6;
     arr[0] = groovy;
     OdeMiester radical = new OdeMiester();
     OdeMiester lit = groovy;
     arr[0] =  lit;
     groovy = radical;
     arr[1]= radical;
     arr[1] = lit;
     radical.gosh.cow = 2;