Я не думаю, что пока есть стиль для всего сообщества.Я видел много соглашений.Я опишу мое и объясню, почему я его использую.
Именование
Я называю свои неявные преобразования одним из
implicit def whatwehave_to_whatwegenerate
implicit def whatwehave_whatitcando
implicit def whatwecandowith_whatwehave
Я не ожидаю, что они будутиспользуется явно, поэтому я склоняюсь к довольно длинным именам.К сожалению, в именах классов часто встречаются числа, поэтому соглашение whatwehave2whatwegenerate
сбивает с толку.Например: tuple22myclass
- это Tuple2
или Tuple22
, о котором вы говорите?
Если неявное преобразование определяется как в аргументе, так и в результате преобразования, я всегда используюx_to_y
обозначение для максимальной ясности.В противном случае, я рассматриваю имя больше как комментарий.Так, например, в
class FoldingPair[A,B](t2: (A,B)) {
def fold[Z](f: (A,B) => Z) = f(t2._1, t2._2)
}
implicit def pair_is_foldable[A,B](t2: (A,B)) = new FoldingPair(t2)
я использую как имя класса, так и неявное в качестве своего рода комментария о том, какой смысл кода, а именно - добавление метода fold
в пары(т.е. Tuple2
).
Использование
Pimp-My-Library
Я чаще всего использую неявные преобразования для конструкций в стиле pimp-my-library.Я делаю это повсюду , где добавляются недостающие функциональные возможности или делает полученный код более чистым.
val v = Vector(Vector("This","is","2D" ...
val w = v.updated(2, v(2).updated(5, "Hi")) // Messy!
val w = change(v)(2,5)("Hi") // Okay, better for a few uses
val w = v change (2,5) -> "Hi" // Arguably clearer, and...
val w = v change ((2,5) -> "Hi", (2,6) -> "!")) // extends naturally to this!
Теперь равно снижение производительности за неявные преобразования, поэтому я не пишу код в горячих точках таким образом.Но в противном случае, я, скорее всего, буду использовать шаблон pimp-my-library вместо def, если перейду к горстке случаев использования в рассматриваемом коде.
Есть еще одно соображение - инструментыеще не настолько надежны, чтобы показать, откуда приходят ваши неявные преобразования и откуда приходят ваши методы.Таким образом, если я пишу код, который сложен, и я ожидаю, что любому, кто его использует или поддерживает, придется усердно его изучать, чтобы понять, что требуется и как он работает, я - и это почти наобороттипичная философия Java - я больше , вероятно, буду использовать PML таким образом, чтобы сделать шаги более прозрачными для обученного пользователя.Комментарии предупреждают, что код должен быть глубоко понят;как только вы глубоко поймете, эти изменения скорее помогут, чем навредят.Если, с другой стороны, код делает что-то относительно простое, я с большей вероятностью оставлю defs на месте, поскольку IDE помогут мне или другим быстро набрать скорость, если нам нужно внести изменения.
Избегание явных преобразований
Я стараюсь избегать явных преобразований.Вы, конечно, можете написать
implicit def string_to_int(s: String) = s.toInt
, но это ужасно опасно, даже если кажется, что вы перебираете все свои строки с помощью .toInt.
Основное исключение, которое я делаю, - для классов-оболочек.Предположим, например, что вы хотите, чтобы метод принимал классы с предварительно вычисленным хеш-кодом.Я бы
class Hashed[A](private[Hashed] val a: A) {
override def equals(o: Any) = a == o
override def toString = a.toString
override val hashCode = a.##
}
object Hashed {
implicit def anything_to_hashed[A](a: A) = new Hashed(a)
implicit def hashed_to_anything[A](h: Hashed[A]) = h.a
}
и вернулся бы к тому классу, с которого я начал, либо автоматически, либо, в худшем случае, добавив аннотацию типа (например, x: String
).Причина в том, что это делает классы-оболочки минимально навязчивыми.Вы действительно не хотите знать об обертке;вам просто иногда нужна функциональность.Вы не можете полностью не заметить обёртку (например, вы можете фиксировать равные только в одном направлении, а иногда вам нужно вернуться к исходному типу).Но это часто позволяет вам писать код с минимальной суетой, что иногда просто необходимо.
Неявные параметры
Неявные параметры вызывают тревожную путаницу.Я использую значения по умолчанию всякий раз, когда возможно, вместо этого.Но иногда вы не можете, особенно с универсальным кодом.
Если возможно, я пытаюсь сделать неявный параметр тем, что никакой другой метод никогда не использовал бы.Например, библиотека коллекций Scala имеет класс CanBuildFrom
, который почти совершенно бесполезен, как что-либо, кроме неявного параметра для методов коллекций.Таким образом, существует очень небольшая опасность непреднамеренных перекрестных помех.
Если это невозможно - например, если параметр необходимо передать нескольким различным методам, но это действительно отвлекает от того, что делает код (например, пытается выполнить регистрацию в середине арифметики), тогдавместо того, чтобы сделать общий класс (например, String
) неявным val, я обертываю его в маркерный класс (обычно с неявным преобразованием).