hashCode and equals
If you want to compare two objects meaningfully, you need to override the equals() method. Without overriding it, equals() defaults to using == operator, which checks whether two variables point to the same object.

If you don't override an equals() method for your class, you won't be able to use instances of that class as keys in a hashtable, and your Sets will be messed up.

Syntax for overriding the equals method:

    public boolean equals(Object o)
You must keep it public, and it must accept a single Object as an argument, which is the object to be compared.

  • When comparing objects in the equals method, the first thing you should do, is make sure the object is of the right type, by using instanceof. If it's not the right type, return false, but beware - this is how the Wrapper classes work, and you know how it drives you crazy that you can't compare an Integer 1 to a Long 1, right? Reach deeper and think of everything that might possibly be a legitimate comparison, before returning false from the comparison.
  • Next, cast the object to the desired type.
  • Last, compare the attributes you care about (but not transient variables!), and return true if you think the objects are meaningfully equivalent, and false otherwise.
If you override the equals method, you should override the hashCode method as well. The rules are:
  • If two objects are equal, then their hashcodes must be equal, as well. You could fulfill this rule by returning the same value for all objects of your class, such as 3, but that would cause poor hash table performance.
  • If two hashcodes are not equal, the x.equals(y) test must return false.
The equality test must be at least as complex as the hashCode routine. For instance, you can't examine 27 different member variables on the hashCode routine to calculate the hashcode, but only check 1 member variable on the equality test.
However, it IS permitted to check one field only on the hashCode routine, and check 27 different fields on the equals routine.

Don't use transient variables for either the hashCode or the equals test!

Collections
If you want to use Collections or Lists, you will have to import java.util.* first.
Collections can hold objects, but not primitives. If you insert a primitive into a collection, it is actually autoboxed into a wrapper object.
Here is the interface hierarchy you should know:

  • Collection Interface
    Methods include: add, addAll, clear, contains, containsAll, equals, hashCode, isEmpty, iterator, remove, removeAll, retainAll, size, toArray
    • Set Interface
      Unique objects, stored without a key.
      Methods include: add, addAll, clear, contains, containsAll, equals, hashCode, isEmpty, iterator, remove, removeAll, retainAll, size, toArray
      • HashSet (Unordered, unsorted)
        Uses the hashcode of the object being inserted, so the faster your hashCode(), the faster it is. Use this when you want a collection without duplicates or keys, and don't care about the order when you iterate through it. MUST OVERRIDE HASHCODE, otherwise you will get duplicates. NOT SYNCHRONIZED!
        Methods include: add, clear, clone, contains, isEmpty, iterator, remove, size
        • LinkedHashSet (Ordered, unsorted)
          Ordered version of HashSet. Choose this class over HashSet when you need to keep the objects in insertion order. MUST OVERRIDE HASHCODE, otherwise you will get duplicates. NOT SYNCHRONIZED!
          Methods include: add, clear, clone, contains, isEmpty, iterator, remove, size
      • SortedSet Interface
        Methods include: first, last, headSet, tailSet, subSet, comparator.
        • NavigableSet Interface
          Methods include: ceiling, descendingIterator, descendingSet, floor, headSet, higher, iterator, lower, pollFirst, pollLast, subSet, tailSet
          • TreeSet (Unordered, sorted)
            Doesn't maintain insertion order, but does sort objects by natural order (via compareTo), or a custom order via a passed in Comparator. Choose over HashSet when you want the objects in sorted order. NOT SYNCHRONIZED!
            Methods include: add, addAll, ceiling, clear, clone, comparator, contains, descendingIterator, descendingSet, first, floor, headSet, higher, isEmpty, iterator, last, lower, pollFirst, pollLast, remove, size, subSet, tailSet,
    • List Interface
      Implements lists of objects.
      Methods include: add, addAll, clear, contains, containsAll, equals, get, hashCode, indexOf, isEmpty, iterator, lastIndexOf, listIterator, remove, removeAll, retainAll, set, size, subList, toArray
      • ArrayList (Ordered, unsorted)
        Choose this over a LinkedList when you need fast iteration, but aren't likely to be doing a lot of insertions and deletions. NOT SYNCHRONIZED!
        Methods include: add, addAll, clear, clone, contains, ensureCapacity, get, indexOf, isEmpty, lastIndexOf, remove, removeRange, set, size, toArray, trimToSize
      • Vector (Ordered, unsorted)
        Older, same as ArrayList, but synchronized. Choose over ArrayList when you need thread safety.
        Methods include:add, addAll, addElement, capacity, clear, clone, contains, containsAll, copyInto, elementAt, elements, ensureCapacity, equals, firstElement, get, hashCode, indexOf, insertElementAt, isEmpty, lastElement, lastIndexOf, remove, removeAll, removeAllElements, removeElement, removeElementAt, removeRange, retainAll, set, setElementAt, setSize, size, subList, toArray, toString, trimToSize
      • LinkedList(Ordered, unsorted) (Also in Queue)
        Choose over ArrayList when you need fast insertions and deletions.
        Methods include: add, addAll, addFirst, addLast, clear, clone, contains, descendingIterator, element, get, getFirst, getLast, indexOf, lastIndexOf, listIterator, offer, offerFirst, offerLast, peek, peekFirst, peekLast, poll, pollFirst, pollLast, pop, push, remove, removeFirst, removeFirstOccurrence, removeLast, removeLastOccurrence, set, size, toArray
    • Queue Interface
      Ordered lists of objects to be processed.
      Methods include: add, element, offer, peek, poll, remove
      • LinkedList (Ordered, unsorted) (also in List)
        Methods include: add, addAll, addFirst, addLast, clear, clone, contains, descendingIterator, element, get, getFirst, getLast, indexOf, lastIndexOf, listIterator, offer, offerFirst, offerLast, peek, peekFirst, peekLast, poll, pollFirst, pollLast, pop, push, remove, removeFirst, removeFirstOccurrence, removeLast, removeLastOccurrence, set, size, toArray
      • PriorityQueue (Unordered, sorted) (doesn't allow nulls)
        A priority-in, priority-out queue, instead of a typical FIFO queue. The elements of the priority queue are ordered according to their natural ordering, or by a Comparator provided at queue construction time, depending on which constructor is used. Does not permit insertion of non-comparable objects - doing so might result in a RuntimeError ClassCastException. NOT SYNCHRONIZED!
        Methods include: add, clear, comparator, contains, iterator, offer, peek, poll, remove, size, toArray

  • Map Interface
    Objects with a unique key, stored by key.
    Methods include: clear, containsKey, containsValue, entrySet, equals, get, hashCode, isEmpty, keySet, put, putAll, remove, size, values.
    • HashMap (Unsorted, unordered) (Allows null key)
      When you need to store objects by key and don't care about the order, this is the fastest method. Uses the hashcode of the object, so the faster your hashCode(), the faster it is. NOT SYNCHRONIZED!
      Methods include: clear, clone, containsKey, containsValue, entrySet, get, isEmpty, keySet, put, putAll, remove, size, values.
      • LinkedHashMap (Unsorted, ordered)
        Choose when you need to maintain insertion order. Slower on adding/removing elements, faster iteration than a HashMap. NOT SYNCHRONIZED!
        Methods include: clear, containsValue, get, removeEldestEntry.
    • Hashtable (Unsorted, unordered) (Allows no null keys)
      Older, similar to HashMap, but synchronized, and allows no null keys. Choose over HashMap when you need thread safety.
      Methosd include: clear, clone, contains, containsKey, containsValue, elements, entrySet, equals, get, hashCode, isEmpty, keys, keySet, put, putAll, rehash, remove, size, toString, values.
    • SortedMap Interface
      Methods include: comparator, entrySet, firstKey, headMap, keySet, lastKey, subMap, tailMap, values
      • NavigableMap Interface
        Methods include: descendingKeySet, descendingMap, firstEntry, lastEntry, headMap, tailMap, subMap, navigableKeySet, pollFirstEntry, pollLastEntry,
        lowerEntry - return list of objects whose key < this object key.
        higherEntry - return list of objects whose key > this object key.
        floorEntry - return list of objects whose key <= this object key.
        ceilingEntry - return list ofs object whose key >= this object key.
        lowerKey - return highest key < this object key.
        higherKey - return lowest key > this object key.
        floorKey - return highest key <= this object key.
        ceilingKey - return lowest key >= this object key.
        • TreeMap (Unordered, sorted)
          A map that is sorted by the natural order of its objects (via compareTo) or by the order specified in the Comparator in the constructor.
          Methods include: ceilingEntry, ceilingKey, clear, clone, comparator, containsKey, containsValue, descendingKeySet, descendingMap, entrySet, firstEntry, firstKey, floorEntry, floorKey, get, headMap, higherEntry, higherKey, keySet, lastEntry, lastKey, lowerEntry, navigableKeySet, pollFirstEntry, pollLastEntry, put, putAll, remove, size, subMap, tailMap, values
  • Utility Classes
    • java.util.Arrays
      Static methods include:
      • static List asList(T[]) - Convert an array to a List (and bind them.)
      • static int binarySearch(Object[] or primitive[], key) - Search a sorted array for a given value, return an index or insertion point.
      • static int binarySearch(T[], key, Comparator) - Search a Comparator-sorted array for a value.
      • static boolean equals(Object[] or primitive[], Object[] or primitive[]) - Compare two arrays to determine if their contents are equal.
      • public static void sort(T[], Comparator) - Sorts the elements of an array using a Comparator.
      • public static String toString(Object[] or primitive[]) - Create a String containing the contents of an array.
    • java.util.Collections
      Static methods include:
      • static int binarySearch(List, key) - Search a sorted List for a given value, return an index or insertion point.
      • static int binarySearch(List, key, Comparator) - Search a list sorted with a Comparator for a given value, return an index or insertion point.
      • static void reverse(List) - Reverse the order of elements in a list.
      • static Comparator reverseOrder() - Return a Comparator that sorts the reverse of the collection's current sort sequence.
      • static Comparator reverseOrder(Comparator) - Return a Comparator that sorts the reverse of the collection's current sort sequence.
      • static void sort(List) - Sort a list by natural order
      • static void sort(List, Comparator) - Sort a list by a Comparator (not natural order.)
Collection Implementation Classes
ClassMapSetListOrderedSorted
Hashtablex NoNo
HashMapx NoNo
TreeMapx SortedBy natural order or comparator
LinkedHashMapx By insertion orderNo
HashSet x NoNo
TreeSet x SortedBy natural order or comparator
LinkedHashSet x By insertion orderNo
ArrayList xBy indexNo
Vector xBy indexNo
LinkedList xBy indexNo
PriorityQueue SortedBy to-do order
Sorting Collections and Arrays
To sort a collection, you need to either:
  1. Implement Comparable in your object
    Create the compareTo method in your object with a function signature of:
        public int compareTo(obj2)
    Call Collections.sort on the collection (Arrays.sort if an array)

    or

  2. Create a custom Comparator<T> object
    Create the compare method in the comparator with a function signature of:
        public int compare(T obj1, T obj2)
    Call Collection.sort on the collection (Arrays.sort if an array).
The compareTo routine on a Comparable object is passed a single object to compare to, and should return:
    -1 if this < obj2
    0 if this == obj2
    1 if this > obj2

The compare routine on a Comparable object is passed two objects to compare, and should return:
    -1 if obj1 < obj2
    0 if obj1 == obj2
    1 if obj1 > obj2.

An easy way to remember what value it should return, is this: If they were numbers, you'd be returning num1 - num2.

When you want to sort an array or collection, the items must all be mutually comparable. In other words, if you have an array of Object and you put ChristmasTree and Integer objects into it, you won't be able to sort it. Depending on the implementation, you may either get no error or a RuntimeError.

Searching Collections and Arrays
Searches are performed using the static methods Collections.binarySearch() or Arrays.binarySearch() method.
Before calling this routine, first make sure your collection or array is sorted!
If you used a comparator, be sure and pass the comparator to the binarySearch() method, or you will get unexpected results.
An example illustrating searching and sorting using custom comparators follows:
import java.util.*;

public class testBinarySearch {
	public static void main(String[] args) {
		// Create a list
		Integer[] arr = new Integer[10];
		for (int i = 0; i < 10; i++)
			arr[i] = i;
		
		// Sort it
		Arrays.sort(arr);
		
		// Search it
		int index = Arrays.binarySearch(arr, 2);
		System.out.println(Arrays.toString(arr));
		System.out.printf("Item found at index %d - value was %d.\n", 
            index, arr[index]);
	
		// Use a custom comparator
		ReverseComparator rs = new ReverseComparator<Integer>();
		Arrays.sort(arr, rs);
        //must pass comparator since arr was sorted with it
		index = Arrays.binarySearch(arr, 2, rs); 
		System.out.println(Arrays.toString(arr));
		System.out.printf("Item found at index %d - value was %d.\n", 
            index, arr[index]);	
	}
}
class ReverseComparator<T extends Comparable> implements Comparator<T> {
	public int compare(T obj1, T obj2) {
		return obj2.compareTo(obj1);
	}

}
Array and Collection Misc.
The List and Set classes have "toArray() methods that convert the collection to a normal array.

The Arrays class has a method called "asList" that copies the items into a List. When you use this, the array and list are joined - when you change one of them, the other gets updated automatically. Though this seems handy, it's not so much - you can't modify the size of the created list, ie, remove items or add items. The following program illustrates the problem.

import java.util.*;

public class testAsList {
  public static void main(String[] args) {
    // Arrays.asList demo
    String[] sa = {"one", "two", "buckle", "my", "shoe"};
    List lst = Arrays.asList(sa);
    System.out.println(Arrays.toString(sa));
    System.out.println(lst.toString());
    try {
      lst.add("three");
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    // can't enlarge arrays.
    // sa.length = sa.length + 1;
  }
}

ArrayLists are an ordered, unsorted implementation of List. Though usually accessed by index, you can also access it by iterator.

To create an iterator, use the syntax:

Iterator i = lst.iterator();
while (i.hasNext()) {
	Dog obj = (Dog)i.next();
	// Do something nice with obj
}
Or the generic form:
Iterator<Cat> i = lst.iterator();
while (i.hasNext()) {
	Cat cat = i.next();
	// Do something nice with cat
}
TreeSets are a set whose elements are sorted. If you insert duplicate items into a treeset, you'll get a RuntimeError. The items in a TreeSet should be mutually comparable - if they're not, you'll get an error at runtime.
   lower      - return the highest element <  e
   higher     - return the lowest  element >  e
   floor      - return the highest element <= e
   ceiling    - return the lowest  element >= e
   
   Items with no suffix,        such as ceiling,      returns a value
   Items with a suffix 'Key',   such as ceilingKey,   returns a key
   Items with a suffix 'Entry', such as ceilingEntry, returns a key 
                                                        + value (Map.Entry)

Polling both retrieves AND removes items.

Backed collections are collections that are subsets of the original collection. Changes made in one, are reflected in the other, including adding and removing. Backed collections are created with the following routines:
    // Return a subset starting at element e
    // A set is a value only collection (no keys)
    headSet(e, inclusive)                      
    tailSet(e, inclusive)                      
    subSet (s, fromInclusive, e, toInclusive)

    // Return a submap starting at element e
    // A map is a key/value collection
    headMap(k, inclusive)
    tailMap(k, inclusive)
    subMap (s, fromInclusive, e, toInclusive)
If a boolean 'inclusive' argument exists, the result is a NavigableSet or NavigableMap.
If no boolean argument exists, the result is a SortedSet or a SortedMap.

An example follows:

import java.util.*;

public class testTailMap {
  public static void main(String... args) {
    TreeSet ts = new TreeSet();
    for (int i = 65; i < 65 + 26; i++)
      ts.add((char)i);
    System.out.println(ts.toString());
    System.out.println("");
    
    // headSet defaults to inclusive = false
    // Return a subset starting at element E and exclusive of E
    System.out.println(ts.headSet('E').toString());     
    // Return a subset starting at element E and exclusive of E
    System.out.println(ts.headSet('E', false).toString()); 
    // Return a subset starting at element E and inclusive of E
    System.out.println(ts.headSet('E', true).toString());   
    System.out.println("");
    
    // tailSet defaults to inclusive = true
    // Return a subset starting at key k and inclusive of k
    System.out.println(ts.tailSet('W').toString());      
    // Return a subset starting at key k and inclusive of k
    System.out.println(ts.tailSet('W', true).toString()); 
    // Return a subset starting at key k and exclusive of k
    System.out.println(ts.tailSet('W', false).toString());   
    System.out.println("");

    // subSet defaults to fromInclusive = true, toInclusive = false
    // Return a subset starting at element start and ending at end - 1
    System.out.println(ts.subSet('A', 'C').toString());    
    // Return a subset starting at element start and ending at end - 1
    System.out.println(ts.subSet('A', true, 'C', false).toString());      
    // Return a subset starting at element start and ending at end
    System.out.println(ts.subSet('A', true,  'C', true).toString());      
    // Return a subset starting at element start + 1 and ending at end 
    System.out.println(ts.subSet('A', false,  'C', true).toString());      
    // Return a subset starting at element start + 1 and ending at end - 1
    System.out.println(ts.subSet('A', false,  'C', false).toString());    
    System.out.println("");
    
    /*
    The above program prints out:

    [A, B, C, D, E, F, G, H, I, J, K, L, M, N, 
     O, P, Q, R, S, T, U, V, W, X, Y, Z]

    [A, B, C, D]
    [A, B, C, D]
    [A, B, C, D, E]

    [W, X, Y, Z]
    [W, X, Y, Z]
    [X, Y, Z]

    [A, B]
    [A, B]
    [A, B, C]
    [B, C]
    [B]

    */
    
    TreeMap tm = new TreeMap();
    tm.put("b", "Bear");
    tm.put("c", "Cat");
    tm.put("d", "Dog");
    tm.put("e", "Emu");
    tm.put("f", "Frog");
    
    SortedMap sm = tm.subMap("b", true, "c", true);
    System.out.println(tm.toString());
    System.out.println(sm.toString());
    // {b=Bear, c=Cat, d=Dog, e=Emu, f=Frog}
    // {b=Bear, c=Cat}

    tm.put("bee", "Bee");
    System.out.println(tm.toString());
    System.out.println(sm.toString());
    // {b=Bear, bee=Bee, c=Cat, d=Dog, e=Emu, f=Frog}
    // {b=Bear, bee=Bee, c=Cat}

    sm.put("x", "exception"); // RuntimeException "key out of range"
  }
}
HashMaps and Hashtables use put, not add!

Internally, when the JVM is looking for an item in a hashtable, it uses the hashCode() of the object to find the correct bucket. After finding the correct bucket, it searches all of them with equals() to find the object in the bucket.

Generics
Generics can be used in many places, but most programmers use them only to create type-safe collections.

Generics provide compile-time assistance ONLY! At run time, type erasure gets rid of the generic types, and everything is manipulated as an Object again. So don't go using generics with the notion that it will help ensure run-time safety, or anything. If your code would be simpler or faster without generics, don't use it.

Problems can arise when mixing older non-generic code with newer code that uses generics - the compiler will warn you if it detects this. It can happen when you have a newer type-safe generic list, and pass it to older code, which could stuff anything in there - Integers, Strings, fish, hatchets.

To make non-generic code generic, add angle brackets after the collection type in both the variable declaration and the constructor call, including any place you declare a variable, like arguments and return types. An older List that held objects

    List list = new ArrayList();
turns into this, to make it hold only Integers
    List<Integer> list = new ArrayList<Integer>();
Here's an example:
import java.util.*;

public class testGenerics {
  public static void main(String[] args) {
    testArrayList();
    testArrayListWithGenerics();
  }
  
  public static void testArrayList() {
    ArrayList arr = new ArrayList();
    for (int i = 0; i < 10; i++)
      arr.add(i);
    System.out.println(arr.toString());
    // Prints [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    // This will work just fine.
    Double d = 54.2;
    arr.add(d);   
    
    // Cast required to get item out of array
    Integer foundit = (Integer) arr.get(2); 
    System.out.println(foundit);
    // Prints 2
    
    // Throws error
    //for (Object obj : arr) {
      // Will throw RuntimeException IllegalFormatConversionException
      // when it hits the double value 54.2!
      // That's the problem without using generics.
      //  System.out.printf("%d ", obj);      
    //}
  }
  
  public static void testArrayListWithGenerics() {
    ArrayList<Integer> arr = new ArrayList<Integer>();
    for (int i = 0; i < 10; i++)
      arr.add(i);
    System.out.println(arr.toString());
    // Prints [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    // Compile time error - can't add double to Integer list
    Double d = 54.2;
    // arr.add(d);   <-- can't do this
    
    // No cast required to get item out of array.
    Integer foundit = arr.get(2);
    System.out.println(foundit);
    // Prints 2
    
    // Prints 0 1 2 3 4 5 6 7 8 9 
    for (Object obj : arr) {
      // Will no longer throw RuntimeException
      // because double value 54.2 was not stuffed in there.
      // Generics fixed the problem.
      System.out.printf("%d ", obj);  
    }
  }
}
Polymorphism doesn't apply to the types stuffed in the angle brackets, with generics. If the variable says List<Integer> myList, it can only be assigned to an ArrayList<Integer>, not an ArrayList<Object>. The stuff in the angle brackets has to match! Or be missing altogether, such as when passing a ArrayList<Integer> to a method that requires an List.

Remember, we're talking about generics here. Without generics, an array of Object can be pointed to an array of Animal, no problem, since Animal inherits from Object. With generics, you can't do this. In short, this is valid:

    Object[] myArray = new JButton[4]; // OK, doesn't use generics
much in the way this, is not:
    List<Object> myArray = new ArrayList<JButton>(); // ERROR! Can't do this.
There is a way to tell the compiler to take any generic subtype of the declared argument type, as long as you promise not to add anything to the collection. That mechanism is the wildcard <?>. Following is an example:
import java.util.*;
public class testWildcards {
	public static void main(String[] args) {
		List<Integer> intlist = new ArrayList<Integer>();
		List<Float> floatlist = new ArrayList<Float>();
		intlist.add(1);
		intlist.add(4);
		floatlist.add(3.5f);
		floatlist.add(2.6f);
		viewFirstItem(intlist);         // Prints 'The first item is 1'
		viewFirstItem(floatlist);       // Prints 'The first item is 3.5'
		//AddToListBad(intlist, 2);     // Can't do this.
		AddToList(intlist, 2);          // You CAN do this.
	}
	public static void viewFirstItem(List<? extends Number> numlist) {
		System.out.printf("The first item is %s\n", numlist.get(0));
	}
	//public static void AddToListBad(List<? extends Number> numlist, Number arg) {
		// The following line causes a compile error, because you can't
		// add to this list with a <? extends Number> parameterized type.
		// numlist.add(arg);
	//}
	
	public static void AddToList(List<Integer> numlist, Integer arg) {
		numlist.add(arg);
		System.out.printf("Successfully added %s to list.\n", arg);
	}
}
The AddToList method accepts an argument of type List<? extends Number> - that means it can accept a list of any generic type that extends Number. Therefore, it can accept a list of Integer, or a list of Float, etc. You can read from items in the list, but you can't add new items to the list.
The List<? extends XXX> format can be used to accept lists of either classes OR interfaces - XXX could be of type Comparable, for instance, which is an interface, not a class. Regardless of whether you're accepting classes or interfaces, use 'extends' (never implements) in generic function signatures.
If you were to use List<?> by itself, without "extends XXX", that means a list of any type. You can read items from such a list, but you can't add new items to the list.
You can't use wildcards when creating new Lists or Collections - they always have to be a specific type. For instance, the following won't compile:
    //WON'T COMPILE
    List<? super Animal> lst = new ArrayList<? super Animal>();
But the following will compile:
    // Will compile
    List<? super Animal> lst = new ArrayList<Animal>();
And finally, there is a way to tell the compiler to take a generic list AND still be able to add items to the list - you must use the '? super XXX'. wildcard syntax. Examine the following:
import java.math.BigInteger;
import java.util.*;
public class testWildcards {
	public static void main(String[] args) {
		List<Number>numlist = new ArrayList<Number>();
		AddToList(numlist, 2.2f);	
		AddToList(numlist, 5l);
		AddToList(numlist, new BigInteger("2010"));
		System.out.println(numlist.toString());
	}	
	
	public static void AddToList(List<? super Number> numlist, Number arg) {
		numlist.add(arg);
		System.out.printf("Successfully added %s to list.\n", arg);		
	}
}
Using Java arrays, an array of Animal can hold an array of Cat or an array of Dog, because a variable of type Animal can hold an instance Animal or any of its subtypes, including Dog or Cat.

This is a way to make generics work like that. Saying List<? super Number> says 'I can accept a list of Number, or any of its supertypes.' That means you can't pass in an list of Integer, because that's a subtype of Number - but you CAN pass in a list of Number, or Object. In the same way that a Number can point to an Integer or Float, a List<? super Number> can accept a list of Number, or any objects higher up the hierarchy.

To help decide when to use super versus extends in wildcards, use this:

  • Consider super when you need to add different types to a list, or reassign a list reference from one type to another.
  • Consider extends when you need to process items of different types in a list.
Generic Usage
  • A generic type is a type with formal type parameters.
  • A parameterized type is an instantiation of a generic type with actual type arguments.
  • Instantiations of a generic type share the same runtime type, due to type erasure.
  • When choosing a generic type identifier, if the argument is taking a collection, use E for element; if it's not a collection, use T for type. You can also use whole words, such as 'TypeGoesHere', but no one does.
  • Generics won't work on primitives - for instance, you can't pass an array of int to a method expecting a generic array - you must pass an array of Integer instead.
  • Type parameters cannot be used in a static context, and thus cannot be used with static methods or variables, or enums. In addition, Exceptions and anonymous inner classes cannot be generic classes.
  • To use generic type parameter T in a method:

    • If the class is generic and has declared the type parameter you wish to refer to, you can use it as follows:
          public void makeArrayList(T t)
      
    • If it hasn't been declared as the class generic type parameter already, you must specify the generic type parameter, such as <T>, just before the method return type; or in the case of constructors, right before the constructor name. Example:
          public <T> void makeArrayList(T t)
      
Generic type identifiers can be used in class, interface, method return type, method argument, and variable declarations:
    class MyGenericClass<T>{}     class
    interface MyGenericClass<T>{} interface
    public <T> void getIt()       method return type
    void doIt(T obj) {}           method argument  - can also use wildcards <?>
    T obj                         variable         - can also use wildcards <?>
Unbounded types <T>
  • Useful when you do not invoke any methods of the type parameter T.
Bounded types <T extends Number>
  • Can have several upper bounds. Example:
        <T extends Comparable<T> & Cloneable>
  • Can be used only as a formal type parameter in the definition of a generic method, class, or interface; ie, as the declared parameter type of a generic class or interface, or the return type of a method.
  • Restrict instantiation - restricts the types that can be used for instantiation of the generic type.
  • Give access to all public non-static methods and fields of the upper bound. For example, T extends Comparable would give the methods of the class access to the compareTo function of the instantiated type.
  • During type erasure, the leftmost upper bound is used for type erasure, and replaces the type parameter in the byte code. For example, if T extends Number, all occurrences of T would be replaced by Number.
Unbounded wildcard type <?>
  • Assignment compatible with any eligible type.
Wildcard type <? extends Number> upper bound
<? super Number> lower bound
  • Can have only one bound, a lower bound (super) or an upper bound (extends).
    A list of wildcard bounds is not permitted.
  • Cannot be used as the formal type parameter for a class or method - ie, there can be no wildcards after a class declaration, and a method return value cannot have wildcards.
  • Most often used to declare the type of a formal parameter in a method - ie, it CAN be used when declaring parameters in a method.
  • If you need to refer to the type farther on in the method, you''re out of luck - change the wildcard character ? to T, then you can refer to it.