Избегая неявной неопределенности def в Scala

Я пытаюсь создать неявное преобразование из любого типа (скажем, Int) в String...

Неявное преобразование в String означает, что методы RichString (например, reverse) недоступны.

implicit def intToString(i: Int) = String.valueOf(i)
100.toCharArray  // => Array[Char] = Array(1, 0, 0)
100.reverse // => error: value reverse is not a member of Int
100.length // => 3

Неявное преобразование в RichString означает, что методы String (например, toCharArray) недоступны

implicit def intToRichString(i: Int) = new RichString(String.valueOf(i))
100.reverse // => "001"
100.toCharArray  // => error: value toCharArray is not a member of Int
100.length // => 3

Использование обоих неявных преобразований означает, что дублированные методы (например, длина) неоднозначны.

implicit def intToString(i: Int) = String.valueOf(i)
implicit def intToRichString(i: Int) = new RichString(String.valueOf(i))
100.toCharArray  // => Array[Char] = Array(1, 0, 0)
100.reverse // => "001"
100.length // => both method intToString in object $iw of type 
   // (Int)java.lang.String and method intToRichString in object
   // $iw of type (Int)scala.runtime.RichString are possible 
   // conversion functions from Int to ?{val length: ?}

Итак, возможно ли неявно преобразовать в String и все еще поддерживать все методы String и RichString?

Ответы

Ответ 1

Либо сделайте огромный прокси-класс, либо сосать его, и попросите его устранить его неоднозначно:

100.asInstanceOf [String].length

Ответ 2

У меня нет решения, но прокомментирую, что методы причины RichString недоступны после того, как ваш intToString неявный означает, что Scala не связывает неявные вызовы (см. 21.2 "Правила для implicits" в Программе в Scala).

Если вы введете промежуточный String, Scala сделает импринцирование конвертированием в RichString (это подразумевается в Predef.scala).

например.

$ scala
Welcome to Scala version 2.7.5.final [...].
Type in expressions to have them evaluated.
Type :help for more information.

scala> implicit def intToString(i: Int) = String.valueOf(i)
intToString: (Int)java.lang.String

scala> val i = 100
i: Int = 100

scala> val s: String = i
s: String = 100

scala> s.reverse
res1: scala.runtime.RichString = 001

Ответ 3

Что касается Scala 2.8, это было улучшено. По этот документ (§ Избегание неоднозначностей):

Ранее наиболее специфичный перегруженный метод или неявное преобразование будет выбираться исключительно на основе типов аргументов методов. Это был дополнительное предложение, в котором говорилось, что наиболее специфический метод не может быть определяемый в надлежащем суперклассе любой из других альтернатив. Эта схема была заменена в Scala 2.8 следующим, более либеральным: При сравнении двух разных применимых альтернатив перегруженного метод или неявный, каждый метод получает один балл за конкретные аргументы и еще один момент для определения в надлежащем подкласс. Альтернатива "выигрывает" над другой, если она получает большее число точек в этих двух сравнениях. Это означает, в частности, что если альтернативы имеют одинаковые типы аргументов, тот, который определен в побеждает подкласс.

См. эту другую статью (§6.5) для примера.

Ответ 4

Единственное, что я вижу, это создать новый класс String Wrapper MyString и позволить ему вызывать любой метод, который вы хотите вызвать в неоднозначном случае. Затем вы можете определить неявные преобразования в MyString и два неявных преобразования из MyString в String и RichString, на всякий случай, если вам нужно передать его в библиотечную функцию.

Ответ 5

Принятое решение (опубликовано Митчем Блевинсом) никогда не будет работать: downcasting Int to String с использованием asInstanceOf всегда будет терпеть неудачу.

Одним из решений вашей проблемы является добавление преобразования из любого типа, преобразованного в String, в RichString (или, скорее, в StringOps, как теперь его называют):

implicit def stringLikeToRichString[T](x: T)(implicit conv: T => String) = new collection.immutable.StringOps(conv(x))

Затем определите преобразование (-ы) в строку как раньше:

scala> implicit def intToString(i: Int) = String.valueOf(i)
warning: there was one feature warning; re-run with -feature for details
intToString: (i: Int)String

scala> 100.toCharArray
res0: Array[Char] = Array(1, 0, 0)

scala> 100.reverse
res1: String = 001

scala> 100.length
res2: Int = 3

Ответ 6

Я смущен: не можете ли вы использовать .toString для любого типа, тем самым избегая необходимости в неявных преобразованиях?