Scala параметры по умолчанию и null
У меня есть такой способ:
def aMethod(param: String = "asdf") = {
...
}
Если метод вызывается следующим образом, то параметру присваивается значение по умолчанию "asdf":
aMethod() ...
Но то, что я хотел бы, это то, что если метод вызывается с помощью null
, тогда также будет применено значение по умолчанию:
aMethod(null) //inside the method, I can use `param` and it has the value "asdf".
Каков наилучший способ сделать это в Scala? Я могу думать о сопоставлении с образцом или о простом выражении if
.
Ответы
Ответ 1
Соответствие шаблону
def aMethod(param: String = null) {
val paramOrDefault = param match {
case null => "asdf"
case s => s
}
}
Опция (неявно)
def aMethod(param: String = null) {
val paramOrDefault = Option(param).getOrElse("asdf")
}
Опция (явно)
def aMethod(param: Option[String] = None) {
val paramOrDefault = param getOrElse "asdf"
}
Последний подход на самом деле самый идиоматический и читаемый, как только вы его используете.
Ответ 2
def aMethod(param: String = null) = {
val p =
if(param == null)
"asdf"
else
param
println(p)
}
Но нужно задать вопрос: зачем разрешать null
? Может ли Option
быть в вашем случае? Для этого вы можете сделать:
def aMethod(param: Option[String]) = {
val p = param.getOrElse("asdf")
println(p)
}
Это дает понять, что ваш метод ожидает возможности "нулевого" аргумента.
Ответ 3
Если метод имеет только один или два параметра по умолчанию, которые могут быть установлены на null
, рассмотрим этот шаблон:
// please note that you must specify function return type
def aMethod (x:String = "asdf"):String = if (x==null) aMethod() else {
// aMethod body ...
x
}
Есть несколько преимуществ:
- В определении метода четко указано значение параметра по умолчанию.
- Правильное значение по умолчанию можно выбрать с помощью инструментов Scala, включая ScalaDoc.
- Нет необходимости определять дополнительное значение для замены исходного параметра внутри тела метода - меньше места для ошибок, проще рассуждать.
- Шаблон довольно краток.
Кроме того, рассмотрим следующий сценарий:
trait ATrait {
def aMethod (x:String = "trait default value for x"):String
}
class AClass extends ATrait {
....
}
Ясно, что здесь нам нужно расширить признак, сохраняя исходное значение по умолчанию. Любой из шаблонов, которые первоначально устанавливают параметр null
, за которым следует проверка и фактическое значение по умолчанию, нарушит контракт, установленный признаком:
class AClass extends ATrait {
// wrong, breaks the expected contract
def aMethod(x: String = null):String = {
val xVal = if (x == null) "asdf" else x
...
}
}
Действительно, в этом случае единственным способом сохранить исходное значение из ATrait
будет:
class AClass extends ATrait {
override def aMethod (x:String):String = if (x==null) aMethod() else {
... // x contains default value defined within ATrait
}
}
Однако в сценарии, когда имеется более одного или двух параметров по умолчанию, которые могут быть установлены на null
, шаблон начинает становиться довольно беспорядочным:
// two parameters
def aMethod (x:String = "Hello",y:String = "World"):String =
if (x==null) aMethod(y=y) else
if (y==null) aMethod(x=x) else {
// aMethod body ...
x + " " + y
}
// three parameters
def aMethod (x:String = "Hello",y:String = " ",z:String = "World"):String =
if (x==null) aMethod(y=y,z=z) else
if (y==null) aMethod(x=x,z=z) else
if (z==null) aMethod(x=x,y=y) else {
// aMethod body ...
x + y + z
}
При переопределении существующего контракта это может быть единственным способом почеркнуть исходные значения по умолчанию.