skip to content

Why does declaring both method(List<String>) and method(List<Integer>) in the same class fail to compile?

level: juniorimportance: must knowfreq 62%

answer

  1. Erasure: List<String> and List<Integer> both become raw List
  2. JVM identifies methods by erased descriptor
  3. 'name clash: same erasure' compile error
  4. Return type is not part of the signature
  5. Fix by renaming or changing raw parameter type

basics

~10 s

Java removes the generic part at compile time, so both methods become method(List). That leaves two methods with the same name and same parameter types, which is not allowed.

solid answer

~40 s

Java generics use type erasure: the compiler checks generic types, then erases them in the bytecode. List<String> and List<Integer> both erase to the raw type List. So method(List<String>) and method(List<Integer>) both become method(List) after erasure. Two methods with the same name and identical erased signatures would be indistinguishable at the bytecode level, so the compiler rejects them with a 'name clash: both methods have the same erasure' error. This is not an overloading problem per se; the JVM has no concept of generics, so the erased signatures must differ. To work around it you must change the actual erased signature, for example by renaming one method, changing a parameter's raw type, or adding a distinguishing parameter.

go deeper

for a junior

Can state that generics are removed at compile time so both methods collapse to method(List) and that it is a compile error.

for a middle

Explains type erasure and the erased descriptor, and can list practical workarounds (rename, change raw type).

for a senior

Cites the override-equivalent / same-erasure rule from the JLS and distinguishes it from ordinary overload resolution; knows return type is irrelevant.

for a principal

Discusses why erasure exists (binary compatibility, no code bloat), trade-offs versus reified generics, and how API design should avoid relying on type-argument-only overloads.

## The problem in one sentence You cannot have two methods in the same class whose signatures become identical after Java erases generic type information. ## Background terms (defined from scratch) - **Generics**: a Java feature (since Java 5) that lets you parameterize a type, e.g. `List<String>` is 'a list of Strings'. The `<String>` part is the *type argument*. - **Type erasure**: the mechanism Java uses to implement generics. The compiler verifies that your generic code is type-safe, then *throws away* the type arguments before producing bytecode. `List<String>`, `List<Integer>`, and `List<?>` all become the same plain type `List` (called the *raw type*) in the compiled `.class` file. This was done so generic code stays binary-compatible with pre-generics code. - **Method signature**: the part of a method that distinguishes it for overloading — its name plus the *types* of its parameters (return type is NOT part of the signature for overloading). - **Overloading**: having several methods with the same name but different parameter types in one class. The compiler picks one based on the argument types at the call site. ## Why the clash happens Consider: ```java void method(List<String> a) {} void method(List<Integer> a) {} ``` At source level these look distinct: one takes a list of Strings, the other a list of Integers. But after erasure, both signatures become `method(List)`. The compiled class would then contain two methods named `method` taking one `List` parameter — literally the same method twice. The JVM identifies methods by their erased descriptor (e.g. `method(Ljava/util/List;)V`), and two methods cannot share a descriptor in one class. So the Java compiler refuses *at compile time*, reporting: ``` name clash: method(List<Integer>) and method(List<String>) have the same erasure ``` ## The precise rule (JLS) The Java Language Specification states that it is a compile-time error if two methods of a class have *override-equivalent signatures* — and two signatures are override-equivalent if they have the same name and *the same erasure* of their parameter types. So the rule is not 'same source signature' but 'same erased signature'. ## What is NOT a clash - `method(List<String>)` and `method(Set<String>)` — erase to `method(List)` and `method(Set)`, which differ. Fine. - `method(String)` and `method(Integer)` — no generics, distinct types. Fine. - `method(List<String>)` and `method(String)` — erase to `method(List)` and `method(String)`. Fine. ## How to fix it 1. **Rename** one method (`addStrings`, `addInts`) — usually the cleanest. 2. **Add a distinguishing parameter** whose erasure differs. 3. **Take a different collection type** so the raw types differ. 4. **Accept a single generic method** `<T> void method(List<T>)` if the bodies are actually the same. ## Why Java keeps erasure Erasure keeps generics a compile-time-only concept, so generic and legacy raw code interoperate and no per-type-argument class bloat is produced (unlike C# reified generics or C++ templates). The overload-clash limitation is a direct, accepted cost of that design.

  • Does changing only the return type of one method resolve the clash?
    No. The return type is not part of a method signature for overloading, and erasure still produces identical parameter types, so the name clash remains.
  • Would method(List<String>) and method(Collection<String>) clash?
    No. They erase to method(List) and method(Collection), which are distinct raw types, so both are valid overloads.

saying these in an interview costs you the question

  • Saying it is a normal overloading resolution problem the JVM resolves at runtime
  • Thinking the methods compile but throw at runtime — it is a compile-time error
  • Believing changing only the return type would fix it
  • Claiming List<String> and List<Integer> are different types at the bytecode level

context