Lesson Completion
Back to course

Generic Restrictions: What You Cannot Do

Advanced
15 minutesβ˜…4.6Java

1. The Hook (The "Byte-Sized" Intro)

In a Nutshell: Generics have 7 major restrictions due to type erasure: (1) Cannot instantiate type parameters (new T()), (2) Cannot create generic arrays (new T[]), (3) Cannot use primitives (List<int>), (4) Cannot use static generic fields, (5) Cannot catch generic exceptions, (6) Cannot instanceof with generics, (7) Cannot overload methods with same erasure. All stem from type erasureβ€”types don't exist at runtime!

Think of ghost in a photo. Generics = visible during development (compile-time), invisible in final product (runtime). Can't interact with invisible things!


2. Conceptual Clarity (The "Simple" Tier)

πŸ’‘ The Analogy: The Blueprint vs Building

Generics = blueprint annotations (compile-time). Final building has no annotations (runtime). Can't build from erased info!


3. Technical Mastery (The "Deep Dive")

The 7 Restrictions

RestrictionWhy?Workaround
Cannot new T()Type erasedPass Class<T>, use Factory
Cannot new T[]Arrays store type, but T erasedUse ArrayList<T>
Cannot List<int>Primitives aren't objectsUse List<Integer>
Cannot static T fieldStatic shared across all typesUse static generic methods
Cannot catch E extends ExceptionCatch clause needs runtime typeUse type parameter differently
Cannot instanceof List<String>Type info erasedUse raw type check
Cannot overload same erasureBoth erase to same signatureUse different method names

4. Interactive & Applied Code

java
import java.util.*; public class GenericRestrictionsDemo { // ❌ RESTRICTION 1: Cannot instantiate type parameter static class Box<T> { // ❌ private T instance = new T(); // Compile error! // βœ… WORKAROUND 1: Factory pattern private T instance; public static <T> Box<T> create(Class<T> clazz) throws Exception { Box<T> box = new Box<>(); box.instance = clazz.getDeclaredConstructor().newInstance(); return box; } // βœ… WORKAROUND 2: Supplier private T instance2; public Box(java.util.function.Supplier<T> supplier) { this.instance2 = supplier.get(); } } // ❌ RESTRICTION 2: Cannot create generic arrays static class Container<T> { // ❌ private T[] array = new T[10]; // Compile error! // βœ… WORKAROUND: Use ArrayList private List<T> list = new ArrayList<>(); // βœ… WORKAROUND: Use Object[] + cast (unsafe!) @SuppressWarnings("unchecked") private T[] array = (T[]) new Object[10]; } // ❌ RESTRICTION 3: Cannot use primitives static void primitiveExample() { // ❌ List<int> numbers = new ArrayList<>(); // Compile error! // βœ… Use wrapper classes List<Integer> numbers = new ArrayList<>(); numbers.add(5); // Autoboxing int x = numbers.get(0); // Unboxing } // ❌ RESTRICTION 4: Cannot have static generic field static class StaticExample<T> { // ❌ private static T value; // Compile error! private T instanceValue; // βœ… Instance field OK // βœ… Static generic METHOD is OK (has own <T>) public static <T> List<T> createList(T item) { List<T> list = new ArrayList<>(); list.add(item); return list; } } // ❌ RESTRICTION 5: Cannot catch generic exception static class ExceptionExample<E extends Exception> { public void method() { try { throw new Exception(); } // ❌ catch (E e) { } // Compile error! catch (Exception e) { // βœ… Catch concrete type System.out.println(e); } } } // ❌ RESTRICTION 6: Cannot instanceof with generics static void instanceofExample() { List<String> strings = new ArrayList<>(); // ❌ if (strings instanceof List<String>) {} // Compile error! // βœ… Use raw type if (strings instanceof List) { // OK System.out.println("Is a List"); } } // ❌ RESTRICTION 7: Cannot overload with same erasure static class OverloadExample { // Both erase to process(List) // ❌ public void process(List<String> list) {} // ❌ public void process(List<Integer> list) {} // Compile error! // βœ… Use different names public void processStrings(List<String> list) {} public void processIntegers(List<Integer> list) {} } public static void main(String[] args) throws Exception { // Demonstrate workarounds Box<String> box = Box.create(String.class); System.out.println("Box created via reflection"); Box<String> box2 = new Box<>(() -> "Default value"); System.out.println("Box created via Supplier"); primitiveExample(); instanceofExample(); } }

5. The Comparison & Decision Layer

Most Common Restrictions

graph TD A[Generic Restriction?] A --> B[Cannot new T] A --> C[Cannot new T[]] A --> D[Cannot List<int>] B --> B1[Use Class<T> + reflection] C --> C1[Use ArrayList<T>] D --> D1[Use List<Integer>]

6. The "Interview Corner" (The Edge)

The "Killer" Interview Question: "Why can't you have private static T value in a generic class?" Answer: Static members are shared across ALL instances of the class, but T is determined per instance:

java
class Box<T> { // ❌ If this worked: // private static T value; } Box<String> stringBox = new Box<>(); // T = String Box<Integer> intBox = new Box<>(); // T = Integer // What would Box.value be? String or Integer? // BOTH classes share the same static field!

Static fields can't have instance-specific types!

Pro-Tip: @SafeVarargs for generic varargs:

java
// Generic varargs creates array internally @SafeVarargs // Suppresses heap pollution warning public static <T> List<T> asList(T... elements) { List<T> list = new ArrayList<>(); for (T element : elements) { list.add(element); } return list; } // Usage List<String> list = asList("A", "B", "C");

Only use @SafeVarargs if you're SURE the method is safe!

Topics Covered

Java FundamentalsAdvanced Java

Tags

#java#generics#type-safety#reusability

Last Updated

2025-02-01