У меня есть два неявных преобразования, которые добавляют метод apply.

implicit def f1(foo: Foo) = new {
  def apply(x: Int) = ???
}

implicit def f2(foo: Foo) = new {
  def apply(x: Int, y: Int) = ???
}

Но я не могу их использовать, потому что компилятор жалуется на неоднозначные неявные преобразования

foo(1) // compile error

Почему он жалуется, если ясно, какой из них следует использовать?

0
Yaroslav 24 Апр 2017 в 13:01

2 ответа

Лучший ответ

Если проблема заключается в существовании неявного внутри Predef, вы должны отключить Predef импорт, как описано здесь: Переопределить неявные преобразования Predef

В качестве примера, давайте попробуем создать новую функцию apply для String.

scala> implicit def stringToFunction(s: String) = new {
     |   def apply(x1: Int) = ???
     | }
stringToFunction: (s: String)AnyRef{def apply(x1: Int): Nothing}

scala> "123"(15)
<console>:13: error: type mismatch;
 found   : String("123")
 required: ?{def apply: ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method augmentString in object Predef of type (x: String)scala.collection.immutable.StringOps
 and method stringToFunction of type (s: String)AnyRef{def apply(x1: Int): Nothing}
 are possible conversion functions from String("123") to ?{def apply: ?}
       "123"(15)
       ^
<console>:13: error: String("123") does not take parameters
       "123"(15)
            ^

Итак, мы должны отключить augmentString импорт из Predef:

scala> import Predef.{augmentString => _, _}
import Predef.{augmentString=>_, _}

scala> "123"(15)
<console>:14: error: type mismatch;
 found   : String("123")
 required: ?{def apply: ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method wrapString in class LowPriorityImplicits of type (s: String)scala.collection.immutable.WrappedString
 and method stringToFunction of type (s: String)AnyRef{def apply(x1: Int): Nothing}
 are possible conversion functions from String("123") to ?{def apply: ?}
       "123"(15)
       ^
<console>:14: error: String("123") does not take parameters
       "123"(15)
            ^

Давайте также отключим wrapString, чтобы в итоге добиться того, что мы хотели сделать:

scala> import Predef.{augmentString => _, wrapString => _, _}
import Predef.{augmentString=>_, wrapString=>_, _}

scala> "123"(15)
scala.NotImplementedError: an implementation is missing
  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:284)
  at $anon$1.apply(<console>:12)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  ... 31 elided

Вы можете сделать то же самое для неявных преобразований из вашего класса Foo после жалоб компилятора на неоднозначные преобразования.

1
Community 23 Май 2017 в 12:17

Вы должны включить оба apply() в одно неявное:

implicit def f1(foo: Foo) = new {
  def apply(x: Int) = ???
  def apply(x: Int, y: Int) = ???
}

С http://docs.scala-lang.org/tutorials/tour/implicit -conversions :

Неявное преобразование из типа S в тип T определяется неявным значением, имеющим тип функции S => T, или неявным методом, конвертируемым в значение этого типа.

Таким образом, у вас должен быть ровно один неявный метод, преобразующий Foo в функцию.

Как это работает в вашем примере:

  1. Компилятор видит вызов foo(1).
  2. Он заменяет его на foo.apply(1).
  3. Выясняется, что класс Foo не имеет метода apply и пытается найти неявное преобразование в класс с помощью этого метода.
  4. Он находит два преобразования, f1 и f2, и отказывается от него.
3
Evgeny Veretennikov 24 Апр 2017 в 10:23
43585146