Scala неявное def не работает, если имя def toString - PullRequest
0 голосов
/ 25 февраля 2019

Этот код не компилируется:

object Foo {
  implicit def toString(i: Int): String = i.toString      
  def foo(x: String) = println(x)
  foo(23)
}    

Выше код не компилируется со следующей ошибкой:

error: type mismatch;
found   : scala.this.Int(23)
required: String
  foo(23)

Но этот код компилируется

object Foo {
  implicit def asString(i: Int): String = i.toString      
  def foo(x: String) = println(x)
  foo(23)
}

Почему имя implicit def должно иметь значение?

Примечание. Если метод называется equals, он также не работает, но работает, если он называется hashCode или clone и т. Д.

Ответы [ 2 ]

0 голосов
/ 25 февраля 2019

Проблема здесь не в том, что toString перегружен на Foo, так как один из других (теперь удаленных) ответов утверждает (вы можете попробовать перегрузить asString аналогично, и это будет работать), это то, чтоtoString, который вы импортируете, сталкивается с toString включающего класса (в вашем случае это некий синтетический объект, составленный из REPL).

Я думаю, что следующие неявные примеры (которые также неНе используйте «встроенные» имена методов, такие как toString), чтобы показать проблему немного яснее:

class Foo {
  def asString(i: Int): String = "this is the one from Foo!"
}

class Bar {
  def asString(i: Int): String = "this is the one from Bar!"
}

object Demo extends Bar {
  val instance = new Foo
  import instance._

  println(asString(23))
}

Это будет использовать asString из Bar, даже если вы думаете, чтоимпортированный будет иметь приоритет:

scala> Demo
this is the one from Bar!
res1: Demo.type = Demo$@6987a133

На самом деле он будет использовать определение из Bar, даже если аргументы не совпадают:

class Foo {
  def asString(i: Int): String = "this is the one from Foo!"
}

class Bar {
  def asString(): String = "this is the one from Bar!"
}

object Demo extends Bar {
  val instance = new Foo
  import instance._

  println(asString(23))
}

Это не компилируется:

<pastie>:25: error: no arguments allowed for nullary method asString: ()String
  println(asString(324))
                   ^

Теперь мы можем сделать это больше похожим на ваш исходный код:

class Foo {
  implicit def asString(i: Int): String = "this is the one from Foo!"
  def foo(s: String): String = s
}

class Bar {
  def asString(): String = "this is the one from Bar!"
}

object Demo extends Bar {
  val instance = new Foo
  import instance._

  println(foo(23))
}

Это происходит с той же ошибкой, что и вы, по той же причине: импортированное неявное преобразованиескрыт по определению с тем же именем в включающем классе.

Сноска 1

Вы спросили следующее:

Почему имя implicit def имеет значение?

Именавлияет на материю все время .Так работает язык.Например:

scala> List(1, 2, 3) + ""
res0: String = List(1, 2, 3)

scala> trait Garbage
defined trait Garbage

scala> implicit val any2stringadd: Garbage = new Garbage {}
any2stringadd: Garbage = $anon$1@5b000fe6

scala> List(1, 2, 3) + ""
<console>:13: error: value + is not a member of List[Int]
       List(1, 2, 3) + ""
                     ^

Мы определили неявное значение, которое скрывает неявное преобразование any2stringadd в scala.Predef.(Да, это ужасно.)

Сноска 2

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

class Foo {
  def asString(i: Int): String = "this is the one from Foo!"
}

class Bar {
  def asString(): String = "this is the one from Bar!"
}

object Demo extends Bar {
  def test(): Unit = {
    val instance = new Foo
    import instance._

    println(asString(23))
  }
}

… вы получите гораздо более разумное сообщение:

<pastie>:26: error: reference to asString is ambiguous;
it is both defined in class Bar and imported subsequently by
import instance._
    println(asString(23))
            ^

На мой взгляд, этопочти наверняка то, что компилятор должен рассказать вам в вашем первоначальном случае.Я также не уверен, почему скрытое неявное значение рассматривается для преобразования вообще, но это, как вы можете сказать, если вы запускаете свой код в REPL с -Xlog-implicits:

scala> foo(23)
<console>:16: toString is not a valid implicit value for Int(23) => String because:
no arguments allowed for nullary method toString: ()String
       foo(23)
           ^

Так чтоПохоже, неявность стирается на другом toString?Если честно, я понятия не имею, что здесь происходит, но я на 90% уверен, что это ошибка.

0 голосов
/ 25 февраля 2019

Не уверен, что это считается ответом (возможно, кто-то, имеющий больше знаний о внутренностях компилятора, мог бы дать более подробное объяснение) , но, поиграв с вашим кодом, я нашел кое-что, чтоЯ считаю, что корень ошибки.

Дано:

object Foo {
  implicit def toString(i: Int): String = i.toString      
}

import Foo.toString

Тогда:

val s: String = 10

Производит:

: 10: warning: импортированный `toString 'навсегда скрыт по определению метода toString в классе Object
import Foo.toString

Что, я думаю, означает, что неявное преобразование скрыто, поскольку его имя конфликтуетс универсальным toString методом, определенным в java.langObject scala.Any) .


Достаточно странно, это работает.

implicit val int2str: Int => String = Foo.toString
val s: String = 10
// s: String = 10
...