Scala 中的 Nullable 支持

Nullablle support in Scala

我可以在 Scala 对象中使用 Nullable 参数吗?

类似于C#中的Nullable和语法糖:

public int? Age {get; set;}
public string Name {get; set;}
public bool? isAdmin {get; set;}

我可以创建 scala class 对象可以设置其中一些属性而其他属性不能设置吗?

我打算为不同的目的使用多个构造函数。我想确保空值表示未设置该字段。

更新

例如我有以下情况class

case class Employee(age: Int, salary: Int, scaleNum : Int,
                            name: String, memberId: Int )

我正在使用 GSON 序列化 JSON 中的 class。

然而,在某些情况下,我不想传递某些参数的值,这样 GSON 序列化程序将只包含具有非 NULL 值的参数。

如何使用选项或其他方式实现?

对于您的示例,Scala 中的一个常见声明是:

case class Employee(
   age: Option[Int], // For int? Age 
   name: String // For string Name
   // your other decls ...
)

那你就可以轻松使用类型了:

val john = Employee( age = Some(10), name = "John" )

虽然 Scala 2 允许引用类型(如 String 等)的空值,但它从 Scala 3 开始慢慢改变 (https://dotty.epfl.ch/docs/reference/other-new-features/explicit-nulls.html)。

JSON支持

Java 库(如 GSON)对 Scala 一无所知,因此您应该考虑使用其他库来获得支持 Scala 的 JSON 支持:

  • 圈子
  • 播放json
  • jsoniter-scala
  • uPickle
  • 等等

这些库不仅知道 class 定义中的 Option[],而且改进了对 Scala 集合、隐式、默认值和其他 Scala 语言功能的支持。

为此选择一个合适的库非常重要,因为使用 Java JSON 库你最终会得到 Java 风格的 classes 和代码,与其他 Scala 库的兼容性问题。

对于 Circe,您的示例将是:

import io.circe._
import io.circe.syntax._
import io.circe.parser._
import io.circe.generic.auto._

val john = Employee( age = Some(10), name = "John" )
val johnAsJson = john.asJson.dropNullValues

decode[Employee]( johnAsJson ) match {
   case Right(johnBack) => ??? // johnBack now is the same as john
   case Left(err) => ??? // handle JSON parse exceptions
}

空合并运算符

现在您可能正在寻找 Scala 中的空合并运算符(C# 中的??,Kotlin 中的?,...)在哪里。

直接答案 - 语言本身有 none。在 Scala 中,我们以 FP 方式使用 Option(以及其他单子结构或 ADT)。

这意味着,例如:

case class Address(zip : Option[String])

case class Employee(
   address: Option[Address]
)

val john = Employee( address = Some( Address( zip = Some("1111A") )) )

应该避免这种风格:


if (john.address.isDefined) {
  if(john.address.zip.isDefined) {
    ...
  }
}

您可以使用 map/flatMaps 代替:


john.address.flatMap { address =>
  address.zip.map { zip =>
     // your zip and address value
    ??
  }
}
    
// or if you need only zip in a short form:

john.address.flatMap(_.zip).map { zip =>
  // your zip value
  ??
}

for-comprehension 语法(基于相同的 flatMaps):

for {
  address <- john.address
  zip <- address.zip
}
yield {
 // doing something with zip and address
 ??
}

重要的部分是在 Scala 中解决这个问题的惯用方法主要基于 FP 的模式。