在实现它的 case 类中使用 Scala trait 或抽象类的默认值

Use default value for Scala trait or abstract class in a case class that implements it

提问人:Michael K 提问时间:10/31/2023 最后编辑:Michael K 更新时间:10/31/2023 访问量:47

问:

有没有办法让这段代码的版本使用特征中定义的默认值?

trait A {      // alternately `abstract class A`
 def x: String
 def y: Int = 1
}

final case class B(x: String, y: Int) extends A

val b = B("ok") // -> errors out
// I'd like this to turn into a B("ok", 1), 
// by using the default y value from A, but this doesn't work
// and similarly something like
object B {
 def apply(x: String): B = {B(x, A.y)}
} 
// doesn't work either
Scala 抽象类 特征

评论


答:

1赞 Gastón Schabas 10/31/2023 #1

基于您除了该代码之外没有提供任何其他内容,我只能建议如何对其进行编译,但我认为设计并不好。

对于第一种方法

trait A {
  def x: String
  def y: Int = 1
}

object DefaultA extends A {
  def x = ??? // you need something here, which means a default impl for this singleton
}

final case class B(x: String, override val y: Int = DefaultA.y) extends A

val b = B("ok") // this will compile

对于第二种情况

trait A {
  def x: String
  def y: Int = 1
}

final case class B(x: String, override val y: Int) extends A

object B {
  def apply(x: String): B =
    // here you create an anonymous instance of the trait but again 
    // you have to provide an implementation for the other method
    B(x, (new A { override def x: String = ??? }).y)

}

如果方法和没有关系,则可以在不同的特征/类/单例中使用xy

评论

0赞 Michael K 10/31/2023
是的,这两个都很聪明,但我同意每个选项都感觉不对。我不想覆盖 --我想接受默认值并使其具体化,但这似乎不起作用。yB
3赞 Mateusz Kubuszok 10/31/2023
@MichaelK 从技术上讲,由于构造函数参数列表中的 s 从...好吧,构造函数参数,你可以调用的是不可能的 - 你创建值,你将传递给构造函数,然后传递它们,然后你把这些值实例化为构造函数的主体中的s,然后你运行超级构造函数(这意味着你的是在初始化之后计算的),然后你从类/特征/等的主体中初始化 vals。构造函数的默认值基本上是静态方法,因此它们无法访问在超级构造函数中创建的值valvalA.yB.yy
2赞 Mateusz Kubuszok 10/31/2023
至少这是 Scala 正在建模的语义,因为在内部,字节码必须执行 JVM 规定的任何操作,因此将使用 尽管计算的值没有访问 .尽管如此,直觉仍然成立,即使在字节码级别,示例中的默认方法用法也会生成,因此像@GastónSchabas使用的技巧是相当必要的(至少如果您坚持将其设置为非抽象)。B.yA.yA.ynew B(x, B.apply$default$2())def y