Doble definición de Scala (2 métodos tienen el mismo tipo de borrado)

Resuelto Jérôme asked hace 14 años • 11 respuestas

Escribí esto en Scala y no se compila:

class TestDoubleDef{
  def foo(p:List[String]) = {}
  def foo(p:List[Int]) = {}
}

el compilador notifica:

[error] double definition:
[error] method foo:(List[String])Unit and
[error] method foo:(List[Int])Unit at line 120
[error] have same type after erasure: (List)Unit

Sé que JVM no tiene soporte nativo para genéricos, así que entiendo este error.

Podría escribir envoltorios para List[String]y List[Int]pero soy vago :)

Tengo dudas pero, ¿hay otra forma de expresar List[String]que no es del mismo tipo que List[Int]?

Gracias.

Jérôme avatar Jul 22 '10 16:07 Jérôme
Aceptado

Me gusta la idea de Michael Krämer de utilizar implícitos, pero creo que se puede aplicar de forma más directa:

case class IntList(list: List[Int])
case class StringList(list: List[String])

implicit def il(list: List[Int]) = IntList(list)
implicit def sl(list: List[String]) = StringList(list)

def foo(i: IntList) { println("Int: " + i.list)}
def foo(s: StringList) { println("String: " + s.list)}

Creo que esto es bastante legible y sencillo.

[Actualizar]

Hay otra forma sencilla que parece funcionar:

def foo(p: List[String]) { println("Strings") }
def foo[X: ClassTag](p: List[Int]) { println("Ints") }
def foo[X: ClassTag, Y: ClassTag](p: List[Double]) { println("Doubles") }

Para cada versión necesitas un parámetro de tipo adicional, por lo que esto no se escala, pero creo que para tres o cuatro versiones está bien.

[Actualización 2]

Para exactamente dos métodos encontré otro buen truco:

def foo(list: => List[Int]) = { println("Int-List " + list)}
def foo(list: List[String]) = { println("String-List " + list)}
Landei avatar Jul 22 '2010 13:07 Landei

En lugar de inventar valores implícitos ficticios, puede usar el DummyImplicitdefinido Predefque parece estar hecho exactamente para eso:

class TestMultipleDef {
  def foo(p:List[String]) = ()
  def foo(p:List[Int])(implicit d: DummyImplicit) = ()
  def foo(p:List[java.util.Date])(implicit d1: DummyImplicit, d2: DummyImplicit) = ()
}
Jean-Philippe Pellet avatar Apr 20 '2011 20:04 Jean-Philippe Pellet

Para comprender la solución de Michael Krämer , es necesario reconocer que los tipos de parámetros implícitos no son importantes. Lo importante es que sus tipos son distintos.

El siguiente código funciona de la misma manera:

class TestDoubleDef {
   object dummy1 { implicit val dummy: dummy1.type = this }
   object dummy2 { implicit val dummy: dummy2.type = this }

   def foo(p:List[String])(implicit d: dummy1.type) = {}
   def foo(p:List[Int])(implicit d: dummy2.type) = {}
}

object App extends Application {
   val a = new TestDoubleDef()
   a.foo(1::2::Nil)
   a.foo("a"::"b"::Nil)
}

En el nivel de código de bytes, ambos foométodos se convierten en métodos de dos argumentos ya que el código de bytes JVM no sabe nada de parámetros implícitos o listas de parámetros múltiples. En el sitio de llamada, el compilador de Scala selecciona el foométodo apropiado para llamar (y por lo tanto el objeto ficticio apropiado para pasar) observando el tipo de lista que se pasa (que no se borra hasta más tarde).

Si bien es más detallado, este enfoque libera a quien llama de la carga de proporcionar los argumentos implícitos. De hecho, incluso funciona si los objetos ficticiosN son privados de la TestDoubleDefclase.

Aaron Novstrup avatar Aug 23 '2010 00:08 Aaron Novstrup

Debido a las maravillas del borrado de tipos, los parámetros de tipo de la Lista de sus métodos se borran durante la compilación, reduciendo así ambos métodos a la misma firma, lo cual es un error del compilador.

Viktor Klang avatar Jul 22 '2010 10:07 Viktor Klang