【问题标题】:Is actually possible to deprecate Scala case class fields?实际上可以弃用Scala案例类字段吗?
【发布时间】:2018-03-20 13:20:39
【问题描述】:

给定

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

我想弃用案例类字段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 被弃用,而且我找不到任何相关资源。

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

谢谢。


更新

正如 Mike 正确指出的,colour 是案例类的必填字段,因此将此属性设置为 deprecated 的意义值得商榷。我想弃用 colour ,因为我现在提供来自另一个构造的 colour 信息,我想让用户知道 colour 不是获取该信息的正确实例,我将删除它来自Fruit 在下一个版本中。

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


更新 2

正如 Alexey 所说,我在编译时确实有过时的警告。但是为什么我不能观察到我的 IDE 代码中的弃用以及我弃用的方法呢?我希望像下面这样的东西

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

我正在使用 IntelliJ。

【问题讨论】:

  • 感谢您的澄清!但是,我仍然有点困惑。您能否发布一个关于您如何使用其他构造提供颜色信息的表示?如果我们知道您希望未来的课程是什么样子,这将有助于我们制定更好的答案。
  • 关于更新:这似乎是一个 IntelliJ 错误,应该在那里报告。
  • 一般情况下,不要相信 IntelliJ 会在不编译检查的情况下为 Scala 正确报告错误和警告。
  • 好的@AlexeyRomanov;你能用后面的更新编辑你的答案吗?我会接受你的回答,因为你证明了弃用案例类字段实际上是可能的。我在 YouTrack here 上打开了一个问题。
  • @spi-x-i 已更新。

标签: scala case-class


【解决方案1】:

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

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 代码内联显示错误和警告是非常不可靠的,您应该始终通过实际构建项目来检查。

【讨论】:

  • 问题是,当您创建实例时,您不会收到弃用警告。
  • 为什么会有问题?创建实例不会访问已弃用的字段。如果您想要该警告,请弃用该类。
  • 是的。我想这是由OP来澄清的。但是,我希望 deprecated 案例类字段在初始化和访问时应该报告弃用警告。
  • 我可能可能看到不推荐使用unapply,因为它提供了对该字段的访问权限,如答案中所述。
  • 将某些东西标记为已弃用的全部意义在于告诉人们不再推荐它,并且(理想情况下)有更好或首选的替代方案。在这种情况下,从技术上讲,如果不需要为已弃用的字段指定值,就无法使用案例类根本。如果您可以在没有警告的情况下创建该字段的实例,那有什么意义呢?
【解决方案2】:

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

也就是说,正如您所指出的,有一些方法可以实现您想要做的事情。这是其中之一:

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 的属性。 (如果它必需的,它不应该被弃用。)它的工作原理是弃用使用@创建Fruit工厂方法 987654325@值。

如果这不起作用,您能否解释一下为什么需要弃用该字段

【讨论】:

  • 嗨,迈克。谢谢你的回答。我完全明白你的意思。我更新了我的问题以澄清我的用例。
【解决方案3】:

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

您可以删除该字段(从而将其从主构造函数中删除),但也可以使用旧签名创建一个已弃用的构造函数。另外,您需要在伴随对象上使用类似的 apply 方法。问题是,它将破坏 case 类的模式匹配(除非您覆盖 unapply)。也许你觉得合理,也许不合理。而且,如果您不想破坏阅读器,您还必须实现一个已弃用的 getter 存根。 (然而,返回什么值值得商榷。)

更保守的方法:弃用主构造函数,创建没有值的辅助(非弃用)构造函数。此外,弃用该字段。这似乎工作正常,并且不会破坏 unapply 方法。它似乎会根据您的需要提出不推荐使用的警告,除了定义还提出了不推荐使用的警告。看到这个sn-p:

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 的参数。

【讨论】:

    猜你喜欢
    • 2022-01-27
    • 1970-01-01
    • 2019-02-09
    • 2017-11-24
    • 1970-01-01
    • 2014-12-17
    • 2014-08-03
    • 2020-07-29
    • 1970-01-01
    相关资源
    最近更新 更多