简体   繁体   中英

How to transfer type parameter through class methods in scala?

In my project, I need to write a generic class, which in a single method handles some types with its handler in a special way (Numeric is used for clarity in the example).

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)


I think this happens because the compiler picks up the type parameter but not the actual one. Tell me, is there any way around this?

PS Okay If we assume that the answer is correct, then it is necessary to overload the dosomething method to achieve polymorphism.

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
}

But in this case, another problem arises.

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)

I am not completely sure this is want your want, but I hope it helps.

Basically, you need to forward the evidence that the T type is a Numeric to the outer method. But, you also have to handle the case where it is not.
For that case, you can provide a default value for the implicit parameter like this:

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)
}

It seems to work.

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

Note that, if you will have many methods, maybe a cleanest solution would be to make A a trait with two implementations, one for numeric types and other for no numeric types.
Make the constructors of both sub classes private and create a factory for A in the companion object which ask for the implicit numeric parameter, and if found it will return a new instance of the numeric subclass.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM