![](/img/trans.png)
[英]Why scala serializability differs in case classes with same constructor parameter types?
[英]How to restrict that case classes have specific parameter types constructor in Scala?
假设我的案例类如下。
trait Foo {
def a: String
}
case class Bar(a: String,b: Option[Int]) extends Foo{
def this(test: Test) = this(test.foo,None)
}
case class Buzz(a: String,b: Boolean) extends Foo{
def this(test: Test) = this(test.foo,false)
}
我通过反射使用构造函数def this(test: Test)
并按预期工作。
我使用构造函数的方法签名是这样的
def test[T <: Foo: ClassTag](cb: (String) => Future[T]): Future[Result]
我想做的是限制任何扩展特征Foo
案例类都必须具有def this(test: Test)
。如果他们中的任何一个案例类都没有,那就应该是编译错误。
我的尝试
//Compile error
trait Foo[T] {
def a: String
def this(test: Test):T
}
有什么办法吗?
提前致谢。
无法使用类型系统来强制类具有特定的构造函数。 这并不应该让您感到意外,因为您已经在使用反射来访问所述构造函数。 使用反射调用,检查合适的构造函数的唯一方法是使用更多反射-最好通过宏来发送编译失败的消息。
但是,几乎总是有比使用反射更好的方法。 在这种情况下,我们可以使用类型类来找到正确的方法,该方法可以从Test
构造Foo
的子类型(或其他任何东西)。
假设Test
看起来像这样:
case class Test(foo: String)
然后,我们定义一个TestBuilder
类型类,该类可以提供证据证明我们可以从Test
构建A
trait TestBuilder[A] {
def build(test: Test): A
}
// Convenience method for creating type class instances
object TestBuilder {
def apply[A](f: Test => A): TestBuilder[A] = new TestBuilder[A] {
def build(test: Test): A = f(test)
}
}
然后,我们定义Foo
,每个都有一个TestBuilder[A]
的实例,其中A
是每个Foo
的类型:
trait Foo {
def a: String
}
case class Bar(a: String, b: Option[Int]) extends Foo
object Bar {
implicit val builder = TestBuilder(test => Bar(test.foo, None))
}
case class Buzz(a: String, b: Boolean) extends Foo
object Buzz {
implicit val builder = TestBuilder(test => Buzz(test.foo, false))
}
请注意,我们不再需要备用构造函数,而是依靠类型类实例使用apply
来构建Foo
。
现在,您的test
方法可能如下所示。 我更改了返回类型,因为您没有定义任何实现或Result
是什么,但是想法是相同的。
def test[T <: Foo : ClassTag : TestBuilder](cb: String => Future[T]): Future[T] = {
val test = Test("abc")
// use the implicitly resolved type class to build `T` from a `Test`
val t = implicitly[TestBuilder[T]].build(test)
Future(t).andThen {
case Success(x) => cb(x.a)
}
}
现在,类似这样的东西会编译:
// T is Bar
scala> test((s: String) => Future(Bar(s, None)))
res0: scala.concurrent.Future[Bar] = scala.concurrent.impl.Promise$DefaultPromise@56f2bbea
并且使用其他类型的Baz
,如果没有TestBuilder[Baz]
的实例,将失败。
case class Baz(a: String) extends Foo
scala> test((s: String) => Future(Baz(s)))
<console>:29: error: could not find implicit value for evidence parameter of type TestBuilder[Baz]
test((s: String) => Future(Baz(s)))
^
我认为您无法完全满足您的需求。 但这也许对您有用:
trait Foo {
def a: String
def create(a: String): Foo
}
case class Bar(a: String,b: Option[Int]) extends Foo{
def create(a: String) = Bar(a,None)
}
case class Buzz(a: String,b: Boolean) extends Foo{
def create(a: String) = Buzz(a,false)
}
然后,您无需指定第二个参数就可以构造Bar或Buzz。
顺便说一句,我不完全遵循您的模板,因为我不知道测试应该是什么。
我认为没有办法直接做到这一点。 但是通常是通过工厂(它们的伴随对象)构造案例类的,这使您可以灵活地以不同的方式执行所需的操作。
限定
trait Test {
def foo : String = ???
}
abstract class Foo[T <: Foo[T]]()(implicit ev : FooMaker[T]) {
def a: String
}
trait FooMaker[T <: Foo[T]] {
def apply( test : Test ) : T
}
implicit object Bar extends FooMaker[Bar] {
def apply(test: Test) = Bar(test.foo,None)
}
case class Bar(a: String,b: Option[Int]) extends Foo[Bar]
implicit object Buzz extends FooMaker[Buzz] {
def apply(test: Test) = Buzz(test.foo,false)
}
case class Buzz(a: String,b: Boolean) extends Foo[Buzz]
但是,如果您尝试在所需的伴随对象中定义不带工厂方法的Foo,则:
case class Barf(a : String, b : Short ) extends Foo[Barf]
你会看到的
scala> case class Barf(a : String, b : Short ) extends Foo[Barf]
<console>:12: error: could not find implicit value for parameter ev: FooMaker[Barf]
case class Barf(a : String, b : Short ) extends Foo[Barf]
将伴随对象与所需的工厂一起添加,一切都很好
implicit object Barf extends FooMaker[Barf] {
def apply(test: Test) = Barf(test.foo,0.toShort)
}
case class Barf(a : String, b : Short ) extends Foo[Barf]
在REPL中:
scala> :paste
// Entering paste mode (ctrl-D to finish)
implicit object Barf extends FooMaker[Barf] {
def apply(test: Test) = Barf(test.foo,0.toShort)
}
case class Barf(a : String, b : Short ) extends Foo[Barf]
// Exiting paste mode, now interpreting.
defined object Barf
defined class Barf
请注意,要在REPL中编译这些内容,您将需要使用:paste
因为相互依赖的定义不能单独定义。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.