En mi proyecto, necesito escribir una clase genérica, que en un solo método maneja algunos tipos con su manejador de una manera especial (se usa numéricos para mayor claridad en el ejemplo).

class A[T](val a:T){
    def doSomething(b:T):T = a match{
        case a : Int    => doSomethingWithIntOrDouble(b)
        case a : Double => doSomethingWithIntOrDouble(b)
        case _          => b
    }
    def doSomethingWithIntOrDouble(b:T)(implicit ev:Numeric[T]):T = 
        ev.plus(a,b)
}

<console>:13: error: could not find implicit value for parameter ev: Numeric[T]
                case a : Int    => doSomethingWithIntOrDouble(b)
                                                             ^
<console>:14: error: could not find implicit value for parameter ev: Numeric[T]
                case a : Double => doSomethingWithIntOrDouble(b)


Creo que esto sucede porque el compilador recoge el parámetro de tipo pero no el real. Dime, ¿hay alguna forma de evitar esto?

PS Bueno, si asumimos que la respuesta es correcta, entonces es necesario sobrecargar el método de Dosalgo para lograr el polimorfismo.

class A[T](val a:T){
    def doSomething(b:T)(implicit ev:Numeric[T]):T = ev.plus(a,b)
    def doSomething(b:T):T = b
}

Pero en este caso, surge otro problema.

scala> a.doSomething(2)
<console>:13: error: ambiguous reference to overloaded definition,
both method doSomething in class A of type (b: Int)Int
and  method doSomething in class A of type (b: Int)(implicit ev: Numeric[Int])Int
match argument types (Int)
       a.doSomething(2)
4
Ethan Knight 30 jun. 2019 a las 18:41

1 respuesta

La mejor respuesta

No estoy completamente seguro de que esto quiere su deseo, pero espero que ayude.

Básicamente, debe reenviar la evidencia de que el tipo T es un numérico al método externo. Pero, también tiene que manejar el caso donde no lo está.
Para ese caso, puede proporcionar un valor predeterminado para el parámetro implícito como este:

class A[T](val a: T) {
  def doSomething(b: T)(implicit ev: Numeric[T] = null): T = Option(ev) match {
    case Some(ev) => doSomethingWithNumeric(b)(ev)
    case None     => b
  }

  def doSomethingWithNumeric(b: T)(implicit ev: Numeric[T]): T = 
    ev.plus(a, b)
}

Parece funcionar.

(new A(10)).doSomething(100) // res: Int = 110
(new A("hey")).doSomething("world") // res: String = "world"

Tenga en cuenta que, si tiene muchos métodos, quizás una solución más limpia sería hacer A un rasgo con dos implementaciones, una para tipos numéricos y otros sin tipos numéricos.
Realice los constructores de ambas sub Clases privadas y cree una Fábrica para A en el objeto Companion que solicite el parámetro numérico implícito, y si se encuentra, devolverá una nueva instancia de la subclase numérica .

1
Luis Miguel Mejía Suárez 1 jul. 2019 a las 02:32