禁止生成申请案例class
Prohibit generating of apply for case class
我正在编写类型安全的代码,并想用我自己的实现替换为 case class
生成的 apply()
。这是:
import shapeless._
sealed trait Data
case object Remote extends Data
case object Local extends Data
case class SomeClass(){
type T <: Data
}
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev: TT =:!= Data): SomeClass.Aux[TT] = new SomeClass() {type T = TT}
}
val t: SomeClass = SomeClass() // <------------------ still compiles, bad
val tt: SomeClass.Aux[Remote.type] = SomeClass.apply[Remote.type] //compiles, good
val ttt: SomeClass.Aux[Data] = SomeClass.apply[Data] //does not compile, good
我想禁止val t: SomeClass = SomeClass()
编译。除了不 SomeClass
成为 case class
之外,是否有可能以某种方式做?
如果您想提供一些智能构造函数,通常会使用一种解决方案,而默认构造函数会破坏您的不变量。要确保只有您可以创建实例,您应该:
- 防止使用
apply
- 防止使用
new
- 防止使用
.copy
- 防止在 child 可以调用构造函数的地方扩展 class
这是通过这个有趣的模式实现的:
sealed abstract case class MyCaseClass private (value: String)
object MyCaseClass {
def apply(value: String) = {
// checking invariants and stuff
new MyCaseClass(value) {}
}
}
这里:
abstract
防止生成 .copy
和 apply
sealed
阻止扩展此 class(final
不允许 abstract
)
private
构造函数阻止使用 new
虽然它看起来不漂亮,但它几乎是防弹的。
正如@LuisMiguelMejíaSuárez 指出的那样,在您的具体情况下这不是必需的,但通常可以使用智能构造函数来处理 case class
的边缘情况。
因此您可以将构造函数设为私有并确保 T
也不同于 Nothing
。
我相信确保构造函数是私有的最好方法(以及@MateuszKubuszok 展示的许多其他东西) 是使用(密封) 特质 而不是 class:
(如果你因为某种原因不能使用特质,请参考Mateusz的回答)
import shapeless._
sealed trait Data
final case object Remote extends Data
final case object Local extends Data
sealed trait SomeClass {
type T <: Data
}
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev1: TT =:!= Data, ev2: TT =:!= Nothing): Aux[TT] =
new SomeClass { override final type T = TT }
}
它是这样工作的:
SomeClass() // Does not compile.
SomeClass.apply[Remote.type] // Compiles.
SomeClass.apply[Data] // Does not compile.
可以看到运行here.
如果你想禁止使用案例的某些auto-generated方法class你可以手动定义方法(带有适当的签名)(然后它们不会被生成)并使它们private
(或private[this]
)。
尝试
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev: TT =:!= Data): SomeClass.Aux[TT] = new SomeClass() {type T = TT}
private def apply(): SomeClass = ??? // added
}
val t: SomeClass = SomeClass() // doesn't compile
val tt: SomeClass.Aux[Remote.type] = SomeClass.apply[Remote.type] //compiles
val ttt: SomeClass.Aux[Data] = SomeClass.apply[Data] //doesn't compile
原则上,方法(apply
、unapply
、copy
、hashCode
、toString
)不能由编译器本身生成,而是由编译器生成macro annotations。然后你可以选择它们的任何子集并根据需要修改它们的生成。
how to efficiently/cleanly override a copy method
也可以使用 Shapeless case classes a la carte 生成方法。然后你也可以根据需要切换on/off方法。
https://github.com/milessabin/shapeless/blob/master/core/src/test/scala/shapeless/alacarte.scala
我正在编写类型安全的代码,并想用我自己的实现替换为 case class
生成的 apply()
。这是:
import shapeless._
sealed trait Data
case object Remote extends Data
case object Local extends Data
case class SomeClass(){
type T <: Data
}
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev: TT =:!= Data): SomeClass.Aux[TT] = new SomeClass() {type T = TT}
}
val t: SomeClass = SomeClass() // <------------------ still compiles, bad
val tt: SomeClass.Aux[Remote.type] = SomeClass.apply[Remote.type] //compiles, good
val ttt: SomeClass.Aux[Data] = SomeClass.apply[Data] //does not compile, good
我想禁止val t: SomeClass = SomeClass()
编译。除了不 SomeClass
成为 case class
之外,是否有可能以某种方式做?
如果您想提供一些智能构造函数,通常会使用一种解决方案,而默认构造函数会破坏您的不变量。要确保只有您可以创建实例,您应该:
- 防止使用
apply
- 防止使用
new
- 防止使用
.copy
- 防止在 child 可以调用构造函数的地方扩展 class
这是通过这个有趣的模式实现的:
sealed abstract case class MyCaseClass private (value: String)
object MyCaseClass {
def apply(value: String) = {
// checking invariants and stuff
new MyCaseClass(value) {}
}
}
这里:
abstract
防止生成.copy
和apply
sealed
阻止扩展此 class(final
不允许abstract
)private
构造函数阻止使用new
虽然它看起来不漂亮,但它几乎是防弹的。
正如@LuisMiguelMejíaSuárez 指出的那样,在您的具体情况下这不是必需的,但通常可以使用智能构造函数来处理 case class
的边缘情况。
因此您可以将构造函数设为私有并确保 T
也不同于 Nothing
。
我相信确保构造函数是私有的最好方法(以及@MateuszKubuszok 展示的许多其他东西) 是使用(密封) 特质 而不是 class:
(如果你因为某种原因不能使用特质,请参考Mateusz的回答)
import shapeless._
sealed trait Data
final case object Remote extends Data
final case object Local extends Data
sealed trait SomeClass {
type T <: Data
}
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev1: TT =:!= Data, ev2: TT =:!= Nothing): Aux[TT] =
new SomeClass { override final type T = TT }
}
它是这样工作的:
SomeClass() // Does not compile.
SomeClass.apply[Remote.type] // Compiles.
SomeClass.apply[Data] // Does not compile.
可以看到运行here.
如果你想禁止使用案例的某些auto-generated方法class你可以手动定义方法(带有适当的签名)(然后它们不会被生成)并使它们private
(或private[this]
)。
尝试
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev: TT =:!= Data): SomeClass.Aux[TT] = new SomeClass() {type T = TT}
private def apply(): SomeClass = ??? // added
}
val t: SomeClass = SomeClass() // doesn't compile
val tt: SomeClass.Aux[Remote.type] = SomeClass.apply[Remote.type] //compiles
val ttt: SomeClass.Aux[Data] = SomeClass.apply[Data] //doesn't compile
原则上,方法(apply
、unapply
、copy
、hashCode
、toString
)不能由编译器本身生成,而是由编译器生成macro annotations。然后你可以选择它们的任何子集并根据需要修改它们的生成。
how to efficiently/cleanly override a copy method
也可以使用 Shapeless case classes a la carte 生成方法。然后你也可以根据需要切换on/off方法。
https://github.com/milessabin/shapeless/blob/master/core/src/test/scala/shapeless/alacarte.scala