javagenericsarraylistimplementation

Why java allowed adding an int to a List<String>? Why generics are not implemented strictly at compile time and runtime both?


I am new to generics, and I came across this experiment.

import java.util.List;
import java.util.ArrayList;
public class Main {
    private static void addIntegerToNames(List listOfNames, Integer i) {
        listOfNames.add(i);
    }
    public static void main(String[] args) {
        List<String> Names = new ArrayList<String>();
        Names.add("stackOverFlow");
        addIntegerToNames(Names, 100);
        System.out.println(Names);
    }
}

Output : [stackOverFlow, 100]

The compiler does not throw an error while adding an integer to listOfNames because it sees that listOfNames is a list of objects. When this goes at runtime, it still doesn't throw an error, My assumption is that at runtime, "Names" is a List of Objects, because generics are only implemented at compile time.

I have two follow up questions.

  1. Is my assumption correct?
  2. Why did java not implemented generics strictly both at compile time and runtime ? Does the current implementation of generics not make Java more error-prone? Was it a tradeoff between performance and safety?

Solution

  • Java allows adding an int to a List<String> due to the way generics are implemented in the language. Generics in Java are implemented through type erasure, which means that the type information is erased at runtime and not available for runtime checks.

    In other words, at runtime, a List<String> and a List<Integer> are both just List objects. This allows for greater compatibility between different versions of Java that might have different types of List objects. However, it also means that the compiler can't always catch type errors at compile-time.

    To address this issue, Java provides some level of type checking at compile-time through the use of bounded type parameters and wildcards. By specifying a type parameter with an upper bound, such as List<? extends Number>, you can restrict the types that can be added to the list. This helps catch errors at compile-time and prevents type errors at runtime.

    However, it's worth noting that even with these restrictions, it's still possible to cast objects to different types in Java, which can lead to runtime errors. This is why it's important to always use generics and type checking whenever possible to avoid errors and ensure code correctness.