2 - Generics
2.1 - Introduction
- Generics = A parameter in a class that represents an objecttype.
- Used to:
- Detect errors at compile-time instead of run-time. --> More reliable software.
- Improves readablility of the software.
- Flexible, make one class for multiple types.
- Can only be an reference type. No primitive types (int, double, char) allowed.
- Implemented in Java since JDK 1.5
- Used to:
- Generic Instantiation = During compiling, when the generics get replaced with an contrete type.
- Formal Generic Type = <T> or <E> that represents a type in the code.
- Actual Concrete Type = An actual objecttype, after generic instantiation.
- Autoboxing = Converting an primitive type (int) into an reference type (Integer)
- Autounboxing =Converting an reference type (Integer) into an primitive type (int).
- Bounded Generic Type = When a generic type is specified as a subtype of another type.
- Generic Method = A static method using a generic type.
- Generic Class = A class using a generic type.
- Raw Type = A generic class/interface used without an concrete type, to enable backward compatibility with older versions of Java.
- Raw Types are unsafe, since they are checked at run-time.
//Below JDK 1.5 GenericStack stack = new GenericStack(); // JDK 1.5 and higher: GenericStack<Object> stack = new GenericStack<Object>();
2.2 - Defining Generic Classes and Interfaces
In the examples, T can be replaced with E or any other letter.
Declaring a generic class:
public class ExampleGenericClass<T> { public ExampleGenericClass() {} }
Declaring a generic class using multiple types:
public class ExampleMultipleGenericsClass<T1, T2, T3, ...> { public ExampleMultipleGenericsClass() {} }
Declaring a class while extending a generic class:
public class ExampleExtendsGenericClass extends ExampleGenericClass<T> { public ExampleExtendsGenericClass() {} }
Declaring a class while implementing a generic interface:
public class ExampleImplementsGenericClass extends ExampleGenericInterface<T> { public ExampleImplementsGenericClass() {} }
Declaring a class with a bounded generic type:
public class ExampleEnforceGenericClass<T extends String> { public ExampleEnforceGenericClass() {} }
Declaring a static generic method with a generic type:
public static <T> void genericReturnMethodExample(T[] list){}
- Call the method:
Class.<String>genericReturnMethodExample(strings); genericReturnMethodExample(strings);
Declaring a method with a generic return type:
public <T> genericReturnMethodExample(){ <T> type = new <T>; return type; }
Declaring a method with a generic parameters/arguments types:
public void genericArgumentMethodExample(<T1> argument1, <T2 extends ArrayList> argument2){ argument1.print(); argument2.get(0); }
2.3 - Wildcards
- Wildcard Generic Types = Specifing a range for a generic type.
- Unbounded wildcard = <?>, or <? extends Object>
- Bounded wildcard = <? extends T>
- Lower-bound wildcard = <? super T>
2.4 - Easure and Restrictions on Generics
- Type easure = The information on generics is used by the compiler but is not available at runtime.
- The compiler erasus the information after compiling, when it is sure that the generics are used safely.
The compiler replaces:
Example1:
ArrayList<String> list = new ArrayList<>(); String state = list.get(o); //INTO ArrayList list = new ArrayList(); String state = (String) (list.get(0));
Example2:
public static <E> void print(E[] list){} //INTO public static void print (Object[] list){}
Example3:
public static <E extends GeometricObject> boolean equalArea{ E object1; E object2; } //INTO public static boolean equalArea{ GeometricObject object1; GeometricObject object2; }
A generic class is shared by all its instances regardless of its actual concrete type.
- ArrayList<Integer> and ArrayList<String> will be in the JVM as class ArrayList, because of type erasure.
- So:
- ArrayList<Integer> instanceof ArrayList == TRUE
- ArrayList<String> instanceof ArrayList == TRUE
- ArrayList<String> instanceof ArrayList<String> == (Compiler/IDE error)
Generic type restrictions:
- Cannot use new E()
- New E() is executed at runtime, but E is not available at runtime.
- Cannot use new E[]
- new E[] is executed at runtime, but E is not available at runtime.
- Circumvent this by using:
E[] elements = (E[]) new Object[capacity];
- This causes an unchecked compile warning.
- At runtime this can cause a ClassCastException.
- Same for ArrayLists.
- Generic type parameters not allowes in a static context
- All instances of a generic class have the same runtime class.
- Static variables and methods of a generic class are shared by all its instances.
- Thus, it is illegal to refer to a generic type parameter for a class in a static method, field or initializer.
- Exception classes cannot be generic
- A generic class may not extend java.lang.Throwable.
- Because, the JVM has to check the exception thrown to see if it matches the type in the catch().
- This is not possible, because type information is not available at runtime.
- Cannot use new E()
2.E - Used Examples
- Pg 741 - Generic Stack
- Pg 744 - Array Sorting
- Pg 746 - Return highest parameter
- Pg 752 - Generic Matrix Class