Scala 中的可空支持

Sah*_*wal 0 scala nullable gson

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

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

public int? Age {get; set;}
public string Name {get; set;}
public bool? isAdmin {get; set;}
Run Code Online (Sandbox Code Playgroud)

我可以创建 Scala 类,其中对象可以设置其中一些属性,而其他属性则不能吗?

我打算有多个构造函数用于不同的目的。我想确保空值意味着未设置该字段。

更新

例如我有以下案例类

case class Employee(age: Int, salary: Int, scaleNum : Int,
                            name: String, memberId: Int )
Run Code Online (Sandbox Code Playgroud)

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

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

我怎样才能通过选项或其他方式实现这一目标?

abd*_*nce 7

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

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

然后您可以轻松使用该类型:

val john = Employee( age = Some(10), name = "John" )
Run Code Online (Sandbox Code Playgroud)

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

JSON 支持

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

  • 播放 json
  • jsoniter-scala
  • 泡菜
  • 等等

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

为此选择合适的库非常重要,因为使用 Java JSON 库,您最终会得到 Java 风格的类和代码,以及与其他 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
}
Run Code Online (Sandbox Code Playgroud)

空合并运算符

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

直接的答案 - 语言本身没有。在 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") )) )
Run Code Online (Sandbox Code Playgroud)

应该避免这种风格:


if (john.address.isDefined) {
  if(john.address.zip.isDefined) {
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

你可以使用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
  ??
}

Run Code Online (Sandbox Code Playgroud)

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

for {
  address <- john.address
  zip <- address.zip
}
yield {
 // doing something with zip and address
 ??
}
Run Code Online (Sandbox Code Playgroud)

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