实际上可以弃用 Scala case class 字段吗?

Is actually possible to deprecate Scala case class fields?

给定

case class Fruit(name: String, colour: String)

我想弃用 class 字段 colour

我试图通过同时使用 Scala 来实现前者 @deprecated

case class Fruit(name: String, @deprecated("message", "since") colour: String)

和Java @Deprecated注释

case class Fruit(
  name: String, 
  /* @Deprecated with some message here */
  @(Deprecated @field) 
  colour: String
)

不幸的是,无论如何我都无法使 colour 弃用,而且我找不到任何关于它的资源。

事实上,我可以使用其他方法来完成相同的任务,例如通过将我的案例 class 放宽到正常 class 并为 colour 提供 getter 并弃用后者,但我想知道是否弃用案例 class 字段实际上是不可能的 - 最终 - 原因。

谢谢。


更新

正如 Mike 正确指出的那样,colour 是案例 class 的必填字段,因此将此属性设置为 deprecated 的意义是有争议的。我想弃用 colour ,因为我现在提供来自另一个构造的 colour 信息,我想让用户知道 colour 不是正确的实例信息,我将在下一版本中将其从 Fruit 中删除。

到现在为止,我只想警告用户不要从 Fruit 属性中获取 colour 信息,而且暂时我真的不在乎他们是否可以创建带有颜色信息的水果实例。


更新 2

正如 Alexey 所说,我在编译时确实收到了弃用警告。但是,为什么我不能观察到 IDE 代码中的弃用以及我弃用的方法呢?我期待类似下面的内容

val fruit = Fruit("peer", "green")<br>val colour = fruit.<strike>colour</strike>

我正在使用 IntelliJ。

这是一个奇怪的用例。如果必填字段 deprecated,那么 class 本身也必须 deprecated,对吧?也就是说,假设您尝试做的事情是可能的,那么在没有收到弃用警告的情况下根本不可能使用 class 。 (这可能解释了为什么不可能。)

就是说,正如您所指出的,有多种方法可以实现您的目标。这是其中之一:

case class Fruit(name: String) {

  @deprecated("message", "since")
  val colour: String = "some default value"
}

object Fruit {

  // Deprecated factory method.
  @deprecated("message", "since")
  def apply(name: String,  colour: String): Fruit = Fruit(name)
}

显然,这假定 colour 不再需要作为 Fruit 的属性。 (如果 必需的,则不应 弃用 。)它通过弃用 工厂方法 来工作使用 colour 值创建 Fruit

如果这不起作用,您能否解释为什么需要 弃用

你的第一个版本似乎工作正常:如果你粘贴

case class Fruit(name: String, @deprecated("message", "since") colour: String)

println(Fruit("", "").colour)

https://scastie.scala-lang.org/,您会看到弃用警告。当然,你需要访问该字段,无论是直接还是通过提取器:

Fruit("", "") match { case Fruit(x, y) => y }

也会警告。

IDEA 没有显示 colour 已弃用,我认为这只是一个错误。一般来说,在 Scala 代码内联中显示错误和警告是非常不可靠的,你应该始终通过实际构建项目来检查。

这很棘手,它还取决于您想要的兼容性级别。

您可以删除该字段(从而将其从主构造函数中删除),但也可以使用旧签名创建一个已弃用的构造函数。此外,您还需要对伴随对象使用类似的应用方法。问题是,它会破坏 case class 的模式匹配(除非你重写 unapply)。也许你觉得合理,也许不合理。而且,如果您不想打扰读者,您还必须实施已弃用的 getter 存根。 (然而,return 的价值值得怀疑。)

一种更保守的方法:弃用主构造函数,创建一个没有值的辅助 (non-deprecated) 构造函数。另外,弃用该字段。这似乎工作正常,并且不会破坏 unapply 方法。它似乎会按照您的意愿提出弃用警告,除了定义也会引发弃用警告。请参阅此片段:

case class Foo @deprecated() (x: String, @deprecated y: Int){
  def this(x: String) = this(x, 0)
}

object Foo {
  def apply(x: String) = new Foo(x)
}

println((new Foo("x")).x)
println((new Foo("x", 66)).x)
println((Foo("x")).x)
println((Foo("x", 66)).x)

println((new Foo("x")).y)
println((new Foo("x", 66)).y)
println((Foo("x")).y)
println((Foo("x", 66)).y)

不幸的是,还有no @SuppressWarning

当然可以根据需要调整@deprecated参数。