我有一些类似的代码:
sealed trait Foo[A] {
def value: A
}
case class StringFoo(value: String) extends Foo[String]
case class IntFoo(value: Int) extends Foo[Int]
我想要一个函数,它可以在给定子类型的类型参数的情况下使用A
类型。
// Hypothetical invocation
val i: Int = dostuff[IntFoo](param)
val s: String = dostuff[StringFoo](param)
我不知道如何以一种有效的方式声明dostuff
。我能想到的最接近的东西
def dostuff[B <: Foo[A]](p: Param): A
但这不起作用,因为A
在那个位置是未定义的。我可以做一些类似的事情
def dostuff[A, B <: Foo[A]](p: Param): A
但我必须像dostuff[String, StringFoo](param)
那样调用它,这非常难看。
编译器似乎应该拥有将A
移动到返回类型所需的所有信息,我如何在标准scala或库中使其工作。如果这会影响答案的话,我目前使用的是scala2.10。我对2.11-唯一的解决方案持开放态度,如果在2.10 中可能但不可能
另一个选项是使用类型成员:
sealed trait Foo {
type Value
def value: Value
}
case class StringFoo(value: String) extends Foo { type Value = String }
case class IntFoo(value: Int) extends Foo { type Value = Int }
def dostuff[B <: Foo](p: Any): B#Value = ???
// Hypothetical invocation
val i: Int = dostuff[IntFoo](param)
val s: String = dostuff[StringFoo](param)
请注意,这两种解决方案主要围绕Scala中的语法限制工作,即不能修复列表的一个类型参数,而让编译器推断另一个。
如您所知,如果您有一个类型为Foo[A]
的参数,那么您可以仅在A
:中使该方法通用
def dostuff[A](p: Foo[A]): A = ???
由于情况可能并不总是如此,我们可以尝试使用一个隐式参数来表达A
和B
之间的关系。由于我们不能只将一些泛型参数应用于方法调用(泛型参数推断要么全有要么全无),因此我们必须将其分为两个调用。这是一个选项:
case class Stuff[B <: Foo[_]]() {
def get[A](p: Param)(implicit ev: B => Foo[A]): A = ???
}
您可以检查REPL:中的类型
:t Stuff[IntFoo].get(new Param) //Int
:t Stuff[StringFoo].get(new Param) //String
另一个类似的选项,但使用匿名类,是:
def stuff[B <: Foo[_]] = new {
def apply[A](p: Param)(implicit ev: B <:< Foo[A]): A = ???
}
:t stuff[IntFoo](new Param) //Int
在这里,我使用了apply
而不是get
,所以您可以更自然地应用该方法。此外,正如您的评论中所建议的,我在证据类型中使用了<:<
。对于那些想了解更多关于这种类型的广义类型约束的人,你可以在这里阅读更多。
您也可以考虑在这里使用抽象类型成员而不是泛型参数。在处理泛型类型推理时,这通常提供了一个优雅的解决方案。您可以在这里阅读更多关于抽象类型成员及其与泛型的关系的信息。