E120: Double Definition

E120: Double Definition (이중 정의)

두 정의가 소거(erasure) 후에 같은 이름과 타입을 가져서 충돌할 때 나오는 에러예요.

출처: Scala 3 Reference

본문

JVM은 타입 소거(type erasure) 때문에 런타임에 제네릭 파라미터를 제거해요. 그래서 서로 다른 제네릭 시그니처를 가진 메서드가 런타임에는 같은 시그니처를 갖게 되어 충돌이 생길 수 있어요.

예시

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

에러 메시지

-- [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.
   -----------------------------------------------------------------------------

해결 방법

메서드 이름을 다르게 하거나, @targetName으로 서로 다른 바이트코드 이름을 주거나, 하나의 제네릭 메서드로 합치면 돼요.

// 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 = ()

더 알아보기

  • @targetName 어노테이션에 대한 자세한 내용은 Scala 3 Reference의 @targetName 문서를 참고해요.