E120: Double Definition

This error is emitted when two definitions conflict because they have the same name and type after erasure.

Due to JVM type erasure, generic parameters are removed at runtime. This can cause methods with different generic signatures to have identical runtime signatures, creating a conflict.


Example

class Example:
  def process(list: List[Int]): Unit = ()
  def process(list: List[String]): Unit = ()

Error

-- [E120] Naming Error: example.scala:3:6 --------------------------------------
3 |  def process(list: List[String]): Unit = ()
  |      ^
  |Conflicting definitions:
  |def process(list: List[Int]): Unit in class Example at line 2 and
  |def process(list: List[String]): Unit in class Example at line 3
  |have the same type (list: List): Unit after erasure.
  |
  |Consider adding a @targetName annotation to one of the conflicting definitions
  |for disambiguation.
  |-----------------------------------------------------------------------------
  | Explanation (enabled by `-explain`)
  |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  |
  | As part of the Scala compilation pipeline every type is reduced to its erased
  | (runtime) form. In this phase, among other transformations, generic parameters
  | disappear and separate parameter-list boundaries are flattened.
  |
  | For example, both `f[T](x: T)(y: String): Unit` and `f(x: Any, z: String): Unit`
  | erase to the same runtime signature `f(x: Object, y: String): Unit`. Note that
  | parameter names are irrelevant.
  |
  | In your code the two declarations
  |
  |   def process(list: List[Int]): Unit
  |   def process(list: List[String]): Unit
  |
  | erase to the identical signature
  |
  |   (list: List): Unit
  |
  | so the compiler cannot keep both: the generated bytecode symbols would collide.
  |
  | To fix this error, you must disambiguate the two definitions by doing one of the following:
  |
  | 1. Rename one of the definitions.
  | 2. Keep the same names in source but give one definition a distinct
  |    bytecode-level name via `@targetName`; for example:
  |
  |       @targetName("process_2")
  |       def process(list: List[String]): Unit
  |
  | Choose the `@targetName` argument carefully: it is the name that will be used
  | when calling the method externally, so it should be unique and descriptive.
   -----------------------------------------------------------------------------

Solution

// Use different method names
class Example:
  def processInts(list: List[Int]): Unit = ()
  def processStrings(list: List[String]): Unit = ()
// Or use @targetName to give them different bytecode names
import scala.annotation.targetName

class Example:
  def process(list: List[Int]): Unit = ()

  @targetName("processStrings")
  def process(list: List[String]): Unit = ()
// Or combine into a single generic method
class Example:
  def process[T](list: List[T]): Unit = ()