Избегание неявной двусмысленности определения в Scala
Вопрос
Я пытаюсь создать неявное преобразование из любого типа (скажем, Int) в строку...
Неявное преобразование в строку означает, что методы 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 означает, что строковые методы (такие как 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
Использование обоих неявных преобразований означает, что дублированные методы (например, length) неоднозначны.
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?
Решение
Либо создайте огромный прокси-класс, либо смиритесь с ним и попросите клиента устранить неоднозначность:
100.asInstanceOf[строка].длина
Другие советы
У меня нет решения, но я прокомментирую причину RichString
методы недоступны после вашего intToString
неявным является то, что Scala не объединяет неявные вызовы (см. 21.2 «Правила для неявных вызовов» в Программирование на 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
Начиная с версии Scala 2.8, это было улучшено.Согласно этот документ (§ Избегание двусмысленностей) :
Ранее наиболее конкретный перегруженный метод или неявное преобразование выбирался исключительно на основе типов аргументов метода.Было дополнительное предложение, в котором говорилось, что наиболее конкретный метод не может быть определен в надлежащем суперклассе любой из других альтернатив.Эта схема была заменена в Scala 2.8 следующей, более либеральной:При сравнении двух различных применимых альтернатив перегруженного метода или неявного, каждый метод получает одно очко за наличие большего количества конкретных аргументов и другое очко за определение в соответствующем подклассе.Альтернатива “выигрывает” у другой, если она наберет большее количество очков в этих двух сравнениях. Это означает, в частности, что если альтернативы имеют идентичные типы аргументов, выигрывает тот, который определен в подклассе.
Видишь та другая бумага (§6.5) для примера.
Единственный вариант, который я вижу, — это создать новый класс String Wrapper MyString и позволить ему вызывать любой метод, который вы хотите вызвать в неоднозначном случае.Затем вы можете определить неявные преобразования в MyString и два неявных преобразования из MyString в String и RichString на тот случай, если вам понадобится передать их в библиотечную функцию.
Принятое решение (опубликованное Митчем Блевинсом) никогда не будет работать:принижение Int
к 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
Я в замешательстве:ты не можешь использовать .toString
в любом случае для любого типа, что позволяет избежать необходимости неявных преобразований?