提问人:Ostap Strashevskii 提问时间:4/3/2023 最后编辑:Dmytro MitinOstap Strashevskii 更新时间:4/3/2023 访问量:84
案例类和伴随对象共享命名空间和实现?
Case class and companion object sharing namespace and implementation?
问:
所以,这不是一个全球性的问题。但。。。这就像一个 DRY 问题。 让我们介绍一下。 如果我有一个带有某个抽象成员的特征
trait WithSomeValue {
val someValue: Int
}
以及一些具有案例类及其同伴的结构都继承了这种特征, 如果我必须做一些静态的东西,比如一个值并将其保存在 Static(companion) 中,就像这样:
object Foo extends WithSomeValue {
override val someValue: Int = 1
}
我也需要在 case 类中实现 trait 时分配它
case class Foo(val x: Int) extends WithSomeValue {
override val someValue: Int = Foo.somevalue// I don`t like to write this! I feel myself DRY and tired ;)
}
我能做些什么?我能做一些优雅的事情来避免一次又一次地写这个作业吗?只在一个地方写它?
我需要在静态上下文中具有可用的静态成员 我必须得到一个层次结构 - 特征>大小写类?但我不想写 DRY 代码。我希望存在一些技巧,以避免代码重复。
答:
1赞
Dmytro Mitin
4/3/2023
#1
例如,可以重写 case 类及其伴随对象的公共父级中的成员
trait WithSomeValue {
val someValue: Int
}
trait WithSomeValueImpl extends WithSomeValue {
override val someValue: Int = 1
}
object Foo extends WithSomeValueImpl
case class Foo(x: Int) extends WithSomeValueImpl
Foo.someValue // 1
Foo(42).someValue // 1
我不需要所有层次结构的一个静态值,而是每个对象伴侣一个值
怎么样
trait WithSomeValue {
val someValue: Int
}
trait FooLike extends WithSomeValue {
override val someValue: Int = 1
}
object Foo extends FooLike
case class Foo(x: Int) extends FooLike
trait BarLike extends WithSomeValue {
override val someValue: Int = 2
}
object Bar extends BarLike
case class Bar(x: Int) extends BarLike
?
实际上,这不是代码重复,因为 case 类及其伴随对象是完全不同的类
someValue
可以在其中完全不同地实现。
无论如何,您可以使用以下宏注释(请参阅下面的 sbt 设置)。它在从类中复制成员的伴随对象中生成成员。如果伴随对象不存在,则注释将创建它。
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
@compileTimeOnly("enable macro annotations")
class companionSomeValue extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro CompanionSomeValueMacros.macroTransformImpl
}
object CompanionSomeValueMacros {
def macroTransformImpl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
def modify(cls: ClassDef, obj: ModuleDef): Tree = (cls, obj) match {
case (
q"$_ class $_[..$_] $_(...$_) extends { ..$_ } with ..$_ { $_ => ..$stats }",
q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }"
) =>
val someValue = stats.collectFirst {
case t@q"$_ val someValue: $_ = $_" => t
}.get
q"""
$cls
$mods object $tname extends { ..$earlydefns } with ..$parents { $self =>
..$body
$someValue
}
"""
}
annottees match {
case (cls: ClassDef) :: (obj: ModuleDef) :: Nil => modify(cls, obj)
case (cls: ClassDef) :: Nil => modify(cls, q"object ${cls.name.toTermName} extends WithSomeValue")
}
}
}
// in a different subproject
trait WithSomeValue {
val someValue: Int
}
@companionSomeValue
case class Foo(x: Int) extends WithSomeValue {
override val someValue: Int = 1
}
object Foo extends WithSomeValue
@companionSomeValue
case class Bar(x: Int) extends WithSomeValue {
override val someValue: Int = 2
}
Foo.someValue // 1
Foo(42).someValue // 1
Bar.someValue // 2
Bar(42).someValue // 2
//scalac: {
// case class Foo extends WithSomeValue with scala.Product with scala.Serializable {
// <caseaccessor> <paramaccessor> val x: Int = _;
// def <init>(x: Int) = {
// super.<init>();
// ()
// };
// override val someValue: Int = 1
// };
// object Foo extends WithSomeValue {
// def <init>() = {
// super.<init>();
// ()
// };
// override val someValue: Int = 1
// };
// ()
//}
//scalac: {
// case class Bar extends WithSomeValue with scala.Product with scala.Serializable {
// <caseaccessor> <paramaccessor> val x: Int = _;
// def <init>(x: Int) = {
// super.<init>();
// ()
// };
// override val someValue: Int = 2
// };
// object Bar extends WithSomeValue {
// def <init>() = {
// super.<init>();
// ()
// };
// override val someValue: Int = 2
// };
// ()
//}
实现略有不同。批注在类中生成委派给伴随对象成员的成员的成员。
@compileTimeOnly("enable macro annotations")
class someValueFromCompanion extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro SomeValueFromCompanionMacros.macroTransformImpl
}
object SomeValueFromCompanionMacros {
def macroTransformImpl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
def modifyClass(cls: ClassDef): ClassDef = cls match {
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" =>
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self =>
..$stats
override val someValue = ${tpname.toTermName}.someValue
}
"""
}
def modify(cls: ClassDef, obj: ModuleDef): Tree = q"..${Seq(modifyClass(cls), obj)}"
annottees match {
case (cls: ClassDef) :: (obj: ModuleDef) :: Nil => modify(cls, obj)
}
}
}
@someValueFromCompanion
case class Foo(x: Int) extends WithSomeValue
object Foo extends WithSomeValue {
override val someValue: Int = 1
}
@someValueFromCompanion
case class Bar(x: Int) extends WithSomeValue
object Bar extends WithSomeValue {
override val someValue: Int = 2
}
//scalac: {
// case class Foo extends WithSomeValue with scala.Product with scala.Serializable {
// <caseaccessor> <paramaccessor> val x: Int = _;
// def <init>(x: Int) = {
// super.<init>();
// ()
// };
// override val someValue = Foo.someValue
// };
// object Foo extends WithSomeValue {
// def <init>() = {
// super.<init>();
// ()
// };
// override val someValue: Int = 1
// };
// ()
//}
//scalac: {
// case class Bar extends WithSomeValue with scala.Product with scala.Serializable {
// <caseaccessor> <paramaccessor> val x: Int = _;
// def <init>(x: Int) = {
// super.<init>();
// ()
// };
// override val someValue = Bar.someValue
// };
// object Bar extends WithSomeValue {
// def <init>() = {
// super.<init>();
// ()
// };
// override val someValue: Int = 2
// };
// ()
//}
在 Scala 中为 Case 类自动生成 Companion 对象(宏注释的 sbt 设置)
评论
0赞
Ostap Strashevskii
4/3/2023
我不需要所有层次结构都有一个静态值,而是每个对象伴侣一个值。(例如类 Bar,其中 someValue = 2)
0赞
Ostap Strashevskii
4/3/2023
有趣的解决方案,但我只能使用案例类,而不是特征 =/
评论
someValue