Как передать параметр типа через методы класса в Scala? - PullRequest
4 голосов
/ 30 июня 2019

В моем проекте мне нужно написать универсальный класс, который в одном методе обрабатывает некоторые типы с помощью своего обработчика особым образом (в примере для ясности используется Numeric).

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)


Я думаю, это происходит потому, что компилятор выбирает параметр типа, но не фактический. Скажите, есть ли способ обойти это?

PS Хорошо. Если мы предположим, что ответ правильный, то для достижения полиморфизма необходимо перегрузить метод dosomething.

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
}

Но в этом случае возникает другая проблема.

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)

1 Ответ

1 голос
/ 01 июля 2019

Я не совсем уверен, что это то, чего вы хотите, но я надеюсь, что это поможет.

По сути, вам необходимо переслать доказательство того, что тип T является Числовым , во внешний метод. Но вы также должны разобраться со случаем, когда это не так.
Для этого случая вы можете указать значение по умолчанию для неявного параметра, например:

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

Кажется, работает.

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

Обратите внимание, что если у вас будет много методов, возможно, самым чистым решением было бы сделать A a trait с двумя реализациями, одна для числовых типов, а другая - без числовых типов.
Сделайте конструкторы обоих подклассов закрытыми и создайте factory для A в сопутствующем объекте, который запрашивает неявный числовой параметр, и, если он найден, он возвращает новый экземпляр числового подкласса.

...