Может ли/неявное преобразование из T в Option [T] быть добавлено/создано в Scala?
Является ли это возможностью сделать вещи немного более эффективными (для прорамера): мне кажется, что немного утомительно обернуть вещи в Some
, например. Some(5)
. Что-то вроде этого:
implicit def T2OptionT( x : T) : Option[T] = if ( x == null ) None else Some(x)
Ответы
Ответ 1
Вы потеряете некоторую безопасность и, возможно, будете путаться.
Например:
val iThinkThisIsAList = 2
for (i <- iThinkThisIsAList) yield { i + 1 }
Я (по какой-то причине) думал, что у меня есть список, и он не попал в компилятор, когда я его повторил, потому что он был автоматически преобразован в Option [Int].
Я должен добавить, что я думаю, что это очень неявное, чтобы явно импортировать, возможно, не глобальное значение по умолчанию.
Ответ 2
Обратите внимание, что вы можете использовать явный неявный шаблон который позволит избежать путаницы и одновременно сохранить код.
То, что я подразумеваю под явным неявным, а не с прямым преобразованием из T
в Option[T]
, вы могли бы преобразовать в объект-оболочку, который предоставляет средства для преобразования из T
в Option[T]
.
class Optionable[T <: AnyRef](value: T) {
def toOption: Option[T] = if ( value == null ) None else Some(value)
}
implicit def anyRefToOptionable[T <: AnyRef](value: T) = new Optionable(value)
... Я мог бы найти лучшее имя для него, чем Optionable
, но теперь вы можете написать код вроде:
val x: String = "foo"
x.toOption // Some("foo")
val y: String = null
x.toOption // None
Я считаю, что этот путь полностью прозрачен и помогает в понимании написанного кода - устраняет все проверки на нуль хорошим способом.
Обратите внимание на T <: AnyRef
- вы должны сделать это неявное преобразование для типов, которые позволяют значения null
, которые по определению являются ссылочными типами.
Ответ 3
Общие рекомендации для неявных преобразований следующие:
- Когда вам нужно добавить участников к типу (a la "open classes", aka "шаблон pimp my library" ), преобразуйте его в новый тип, который расширяет
AnyRef
и который определяет только тех членов, которые вам нужны.
- Когда вам нужно "исправить" иерархию наследования. Таким образом, у вас есть некоторый тип
A
, который должен иметь подклассу B
, но по какой-то причине не был. В этом случае вы можете определить неявное преобразование от A
до B
.
Это единственные случаи, когда целесообразно определить неявное преобразование. Любое другое преобразование спешит на проблемы безопасности и корректности типов в спешке.
Для T
действительно не имеет смысла расширять Option[T]
, и, очевидно, цель преобразования - это не просто добавление членов. Таким образом, такое преобразование было бы нецелесообразным.
Ответ 4
Казалось бы, это может смутить других разработчиков, поскольку они читают ваш код.
Как правило, кажется, что implicit
работает, чтобы помочь сбрасывать из одного объекта в другой, вырезать запутанный код литья, который может загромождать код, но, если у меня есть переменная, и она каким-то образом становится Some
, то это будет кажется, надоедает.
Возможно, вы захотите поместить некоторый код, показывающий, что он используется, чтобы увидеть, насколько запутанным было бы.
Ответ 5
Вы также можете попробовать перегрузить метод:
def having(key:String) = having(key, None)
def having(key:String, default:String) = having(key, Some(default))
def having(key: String, default: Option[String]=Option.empty) : Create = {
keys += ( (key, default) )
this
}
Ответ 6
Это выглядит хорошо для меня, за исключением того, что он не может работать для примитива T (который не может быть null). Я предполагаю, что неспециализированный родословный всегда получает примитивы в коробке, поэтому, вероятно, это хорошо.