From charlesreid1

Can use Java Generics to define classes that wrap generic types.

Can also define methods that take generic types of arguments, slightly different syntax.

Let's look at each.

Generic Classes

Applications

One common application of generic classes is when building data structures like linked lists or trees. These objects use node structures to store data under the hood, and these node structures often take the form of objects storing an element of data of arbitrary type, along with pointers.

If you use the built-in container types, you also see generic class types. It is the concept that the container can deal with objects of any type, because the container just needs to hold them, or set and get container elements, but never interact with the elements themselves. That's the reason behind the LinkedList<String> syntax.

The important part here is the diamond notation <String> - when we define a generic class we deal with a passed parameter of type <E>, where the letter that goes in the diamond is arbitrary but is the keyword indicating the generic type of object that this Node class wraps. Later in the class, the object is referred to using a pointer of generic type E.

Maps and Dictionaries have keys and values that can each take on different types, but Java implements a similar idea for maps by using a similar idea, defining a Map<K,V> that allows the class definition to deal with a generic key/value type.

Example

This is an example of a Node class found in a linked binary tree class (see Binary Trees/LinkedBinTree for actual implementation):

git.charlesreid1.com link: https://charlesreid1.com:3000/cs/java/src/master/trees/oop-correct-tree/LinkedBinTree.java

public class LinkedBinaryTree<E> {

	protected static class Node<E> {
		// No need to expose things.
		private E element;

		private Node<E> parent; // pointer to parent node
		private Node<E> left;  // pointer to left child
		private Node<E> right; // pointer to right child

		// constructor with element and neighbors
		public Node(E e, Node<E> above, Node<E> lefty, Node<E> righty) { 
			element = e;
			parent = above;
			left = lefty;
			right = righty;
		}

		// get methods - one for each attribute
		public E getElement() { return element; }

		public Node<E> getParent() { return parent; }
		public Node<E> getLeft() { return left; }
		public Node<E> getRight() { return right; }

		// update methods
		public void setElement(E e) { element = e; }

		public void setParent(Node<E> up) { parent = up; }
		public void setLeft(Node<E> lefty) { left = lefty; }
		public void setRight(Node<E> righty) { right = righty; }

	}