ProgrammingBackend Developer

Tell us about the key aspects of working with the generation and usage of generics in Java, what nuances are important to know for safe application?

Pass interviews with Hintsage AI assistant

Answer.

Generics allow you to create classes, interfaces, and methods with type parameters, which provides type checking at compile time and helps avoid ClassCastException.

Key features and pitfalls:

  • Type Erasure: At compile time, information about type parameters is erased, so you cannot, for example, create an array of generic type: new List<String>[10] — compile-time error.
  • Restrictions on usage:
    • You cannot create instances of parameterized types: T obj = new T();
    • You cannot use instanceof with parameterized types: if(obj instanceof List<String>) — error.
    • You cannot create static fields of parameterized types.
  • Wildcards: ? extends T — covariance (reading), ? super T — contravariance (writing).
  • PECS principle (Producer Extends, Consumer Super): If you need to read only — use extends, if you need to write — use super.

Example:

// Covariant approach for reading void printNumbers(List<? extends Number> numbers) { for (Number n : numbers) { System.out.println(n); } } // Contravariant approach for writing void addIntegers(List<? super Integer> list) { list.add(10); } }

Trick question.

Question: "What is the difference between List<Object> and List<?>? Can you put any object in List<?>?"

Answer: No, you cannot add anything to List<?> (except for null), because the compiler does not know what type parameter is there. However, in List<Object> you can add any objects.

Example:

List<?> list1 = new ArrayList<String>(); // list1.add("test"); // Compile-time error! List<Object> list2 = new ArrayList<>(); list2.add("test"); // OK

Examples of real mistakes due to lack of knowledge of the nuances of the topic.


Story

The development team attempted to implement a cache based on an array of parameterized type T[]. Due to type erasure and the inability to create arrays of generic type, the solution did not work as expected: it resulted in an Object[] array, leading to ClassCastException during runtime casts.


Story

In one of the microservices, a developer tried to implement a receiver using List<?> as a parameter and attempted to modify the collection. This caused a compilation error and delayed the release timeline, as it was necessary to refactor the logic considering PECS.


Story

In a project integrating with an external system, a developer made an error overriding one collection type with another through an unchecked raw type: List list = new ArrayList<String>(), which led to ClassCastException and service crashes in production when attempting to cast elements to other types.