Next we’ll examine Java generics.
This is an advanced topic, but one that you are bound to encounter as you continue to use Java.
Generics also allow the compiler to better check our code—and we always want that.
So let’s do this!
We’ve already seen generics at use when using Java’s containers like Lists.
We can create a bareList, but using one requires dangerous downcasting:
importjava.util.List;
importjava.util.ArrayList;
// This List can store any Object
Listlist=newArrayList();
list.add("test");
list.add(4);
Strings= (String) list.get(1); // Oh no! A runtime error...
System.out.println(s.length());
Instead, by providing a type parameter when we create the list, we can get the compiler to help us:
importjava.util.List;
importjava.util.ArrayList;
// This List can store only Strings
List<String>list=newArrayList<>();
list.add("test");
list.add(4); // Now the compiler will fail here...
Strings= (String) list.get(1);
System.out.println(s.length());
Remember: runtime errors cause things to fail right in the user’s face.
This is not good!
Compiler errors, in contrast, must be caught in development.
So transforming runtime errors to compiler errors helps us produce more correct programs.
This is good!
OK—so now we know how to use classes that accept type parameters.
But how about using the in our own classes?
This turns out to not be too hard!
Let’s explore together.
To start, let’s design a class that does not support type parameters:
publicclassLastN {
}
Interactive Walkthrough
Click on an icon below or the play button above to start!
Next, let’s look at how to add a generic type parameter to our example above.
This will allow the compiler to ensure that all of the values added to LastN are the same type!
publicclassLastN {
}
Interactive Walkthrough
Click on an icon below or the play button above to start!
There are a few important things to understand about how Java compiles generic classes.
First, the type parameter is not a variable.
You can use them in most places that you would normally use a type, but you can’t assign to them, or use them in a constructor call:
publicclassExample<E> {
privateEvalue; // This is fine, since E replaces a type
publicExample() {
E=String; // But you can't reassign a type parameter
value=newE(); // Also doesn't work, since maybe E doesn't have an empty constructor?
}
}
One useful way to think about what happens when your program is compiled is that the compiler replaces the type parameters with types used in your code.
So, given this generic class:
publicclassExample<E> {
privateEvalue;
publicExample(EsetValue) {
value=setValue;
}
}
Example<String>example=newExample<>("test");
If I create a Example<String>, it’s almost as if I had written this code:
You may have noticed above that when we implemented LastN we used a List rather than an array.
That wasn’t an accident!
Let’s see why and what problems arise with generic arrays.
publicclassLastN {
}
Interactive Walkthrough
Click on an icon below or the play button above to start!
You regularly see generic type parameters in Javadoc.
Now that we’ve discussed a bit about how to use type parameters in our own code, we’re more prepared to understand documentation for classes that use them.
Let’s look at one familiar example together and discuss how to identify and interpret these type parameters.
Solve: BinaryTree Balanced
Created By: Geoffrey Challen
/ Version: 2020.11.0
Let's determine if a binary tree is height balanced.
A tree is height balanced if the height of any node's two subtrees (right and left) never differ by more than 1.
Provide a public class named BinaryTreeBalanced providing a single public class method named isBalanced.
isBalanced accepts a BinaryTree and returns true if the tree is balanced, and false otherwise.
If the passed tree is null, you should throw an IllegalArgumentException.
A few hints on this problem:
Your main entry point method will probably need to start the recursion using a private helper method, because the
main method needs to throw on null which you want to handle as a valid base case in your recursion
This helper method will probably have the same arguments as the main method, so you'll need to change something
else to make sure that the method signature is different and the compiler can determine which one you want to use
You will probably need a second helper method implementing tree height that you call in your primary recursive
method