Подтвердить что ты не робот

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.

4b9b3361

Ответ 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
}

При переопределении существующего контракта это может быть единственным способом почеркнуть исходные значения по умолчанию.