假设我的案例类如下所示。
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]
我想要做的是限制任何扩展trait Foo
的case类都需要def this(test: Test)
。如果它们中的任何一个没有它,那么它应该是编译错误
我的尝试
//Compile error
trait Foo[T] {
def a: String
def this(test: Test):T
}
有没有办法做到这一点?
提前致谢。
答案 0 :(得分:2)
不可能使用类型系统来强制某个类具有特定的构造函数。这不应该是一个惊喜,因为你已经使用反射来访问所述构造函数。使用反射调用,检查相应构造函数的唯一方法是使用更多反射 - 最好通过宏来邮件编译失败。
然而,几乎总是比使用反射更好的方法。在这种情况下,我们可以使用类型类来查找正确的方法,该方法可以从Foo
构建Test
(或其他任何东西)的子类型。
我们假设Test
看起来像这样:
case class Test(foo: String)
然后,我们定义了一个TestBuilder
类型类,它可以提供证据证明我们可以从A
构建Test
。
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))
}
请注意,我们不再需要备用构造函数,并依赖类型类实例使用Foo
构建apply
。
现在,您的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)))
^
答案 1 :(得分:1)
我认为你不能做你想要的。但也许这对你有用:
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,而无需指定第二个参数。
顺便说一句,我并没有完全遵循你的模板,因为我不知道 Test 应该是什么。答案 2 :(得分:0)
我认为没有办法直接这样做。但是人们通常会通过工厂构建案例类 - 它们的伴随对象 - 这使您可以灵活地以不同的方式做你想做的事。
定义
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
,因为无法单独定义相互依赖的定义。