Scala неявный выбор использования
Мне было интересно, прозрачный лиimplicit
преобразования - это действительно хорошая идея, и может ли лучше использовать импликации более явно. Например, предположим, у меня есть метод, который принимает Date
в качестве параметра, и у меня есть неявное преобразование, которое превращает String
в Date
:
implicit def str2date(s: String) : Date = new SimpleDateFormat("yyyyMMdd").parse(s)
private def foo(d: Date)
Тогда, очевидно, я могу назвать это с прозрачным implicit
преобразование:
foo("20090910")
Было бы лучше сделать тот факт, что я преобразовываю строку в дату более явным?
class DateString(val s: String) {
def toDate : Date = new SimpleDateFormat("yyyyMMdd").parse(s)
}
implicit def str2datestr(s: String) : DateString = new DateString(s)
Итак, использование выглядит больше как:
foo("20090910".toDate)
Преимущество этого в том, что позже становится яснее, что происходит - меня несколько раз ловил прозрачный implicit
конверсии, о которых я должен знать (Option
в Iterable
кто-нибудь?) и это использование все еще позволяет нам использовать в своих интересах силу implicit
s.
2 ответа
Я считаю, что более "явный" способ выполнения неявных преобразований намного лучше с точки зрения читабельности, чем полностью прозрачный, по крайней мере, в этом примере.
На мой взгляд, используя implicit
полностью прозрачно от типа A
печатать B
хорошо, когда вы всегда можете просмотреть объект типа A
как возможность быть использованным всякий раз, когда и объект типа B
нужно. Например, неявное преобразование String
к RandomAccessSeq[Char]
всегда имеет смысл - String
концептуально всегда можно рассматривать как последовательность символов (например, в C строка - это просто последовательность символов). Вызов x.foreach(println)
имеет смысл для всех String
s.
С другой стороны, более явные преобразования должны использоваться, когда объект типа A
иногда может использоваться как объект типа B
, В вашем примере вызов foo("bar")
не имеет смысла и выдает ошибку. Поскольку у Scala нет проверенных исключений, вызов foo(s.toDate)
четко сигнализирует, что может быть выброшено исключение (s
может не быть действительной датой). Также, foo("bar".toDate)
выглядит неправильно, в то время как вам нужно ознакомиться с документацией, чтобы понять, почему foo("bar")
может быть не так. Примером этого в стандартной библиотеке Scala являются преобразования из String
с Int
с помощью toInt
метод RichString
обертка (String
с можно рассматривать как Int
с, но не все время).
Когда вы делаете неявное преобразование из X в Y (например, преобразование из String в Date выше), вы, по сути, говорите, что, если бы у вас был полный контроль над написанием X в первую очередь, вы бы заставили X реализовать или быть подкласс Y.
Если для X имеет смысл реализовать Y, добавьте преобразование. Если это не так, то, возможно, это не подходит. Например, для String имеет смысл реализовывать RandomAccessSeq[Char], но, возможно, для String не имеет смысла реализовывать Date (хотя String, реализующий StringDate, выглядит неплохо).
(Я немного опоздал, и у Флавиу отличный ответ, но я хотел добавить комментарий о том, как я думаю о последствиях.)