这是示例代码:
object GenericsTest extends App {
sealed trait CommonResponse
trait Requester[SomeType] {
trait Response extends CommonResponse {
def entity: SomeType
}
case class SomeResponse(entity: SomeType) extends Response
// More Responses here...
def getResponse(): Response
}
object StringRequester extends Requester[String] {
override def getResponse(): StringRequester.Response = SomeResponse("somestring")
}
object IntegerRequester extends Requester[Integer] {
override def getResponse(): IntegerRequester.Response = SomeResponse(42)
}
val response: CommonResponse = IntegerRequester.getResponse()
response match {
case r: StringRequester.Response => println(s"Got string response ${r.entity}") // hits here =(
case r: IntegerRequester.Response => println(s"Got integer response ${r.entity}")
case other => println(s"Got other $other")
}
}
打印“获得字符串响应42”而不是“得到整数响应42”
真实代码是一种实现不同类型索引的服务,如果数据已经编入索引,将返回不同的响应等。
答案 0 :(得分:1)
您可以在响应中公开对外部Requester
的引用:
trait Requester[SomeType] {
trait Response {
def requester: Requester.this.type = Requester.this
// ...
}
// ...
}
然后,您可以在response.requester
:
response.requester match {
case StringRequester => println(s"Got string response ${response.entity}") // hits here =(
case IntegerRequester => println(s"Got integer response ${response.entity}")
case _ => println(s"Got other $response")
}
答案 1 :(得分:1)
由于内部特征 Response
在运行时中没有用于outer
类型检查的实现Path dependent
方法,这导致特质实际上是 Java
interface
因此,对于您的示例,您应该使用SomeResponse
进行类型匹配,例如:
response match {
case r: StringRequester.SomeResponse => println(s"Got string response ${r.entity}") // hits here =(
case r: IntegerRequester.SomeResponse => println(s"Got integer response ${r.entity}")
case _ => println(s"Got other")
}
SomeResponse
拥有运行时输入检查的outer
方法。见:
public Test$Requester Test$Requester$SomeResponse$$$outer();
Code:
0: aload_0
1: getfield #96 // Field $outer:LTest$Requester;
4: areturn
答案 2 :(得分:1)
Scala在运行时擦除泛型类型。例如,List [String]和List [Integer]的运行时类型相同。因此,您的代码无法正常工作。
例如,
sealed trait Foo
case class Bar[T](value: T) extends Foo
val f1: Foo = Bar[String]("Apple")
val f2: Foo = Bar[Integer](12)
//Will print ---A even type of f2 is Integer.
f2 match {
case x: Bar[String]=> println("--- A")
case x: Bar[Integer] => println("--- B")
case _ => println("---")
}
上面将打印---A
,因为在scala中,泛型的类型在运行时被擦除,因此,编译器不会知道f2
的类型。
在您的情况下,您已定义了Requester
的2个实例。
StringRequester
和IntegerRequester
。 type
的回复StringRequest
为Requester[String]#Response
,IntegerRequester
为Requester[String]#Response
。
这里,Response
是路径依赖类型,即响应类型因不同实例而异。
例如,StringRequester1.Response
不等于StringRequester2.Response
。
但是,由于通用,上述条件将失败。因为,由于泛型中的类型擦除,类型SomeType
在运行时从Requester
中删除。
i.e. Requester[String]#Response will be Requester[_]#Response
and Requester[Integer]#Response will be Requester[_]#Response
StringRequester.getResponse().isInstanceOf[IntegerRequester.Response] //will print true.
//because both type of StringRequester.getResponse() and IntegerRequest.Response is Requester[_]#Response.
结果,两种类型都是相同的。因此,您的代码无法提供正确的结果。
response match {
case r: StringRequester.Response => println(s"Got string response ${r.entity}") // hits here =(
case r: IntegerRequester.Response => println(s"Got integer response ${r.entity}")
case other => println(s"Got other $other")
}
在上面的代码中,r
的两种情况类型在运行时都是Requester[_]#Response
,因此两者都匹配,并且Scala匹配第一个找到的情况,即StringRequester.Response
。如果你按如下方式交换地点,它将打印整数。
response match {
case r: IntegerRequester.Response => println(s"Got integer response ${r.entity}")
case r: StringRequester.Response => println(s"Got string response ${r.entity}") // hits here =(
case other => println(s"Got other $other")
}
以下是解决方法: 您可以使用反射类型检查,如下所示。
sealed trait Foo
case class Bar[T : TypeTag](value: T) extends Foo {
def typeCheck[U: TypeTag] = typeOf[T] =:= typeOf[U]
}
val f1:Foo = Bar[String]("apple")
val f2:Foo = Bar[Integer](12)
// Will print Integer type.
f2 match {
case x: Bar[String] if x.typeCheck[String] => println("Value is string type.")
case x: Bar[Integer] if x.typeCheck[Integer] => println("Value is Integer type.")
case _ => println("--- none ---")
}
答案 3 :(得分:1)
正如我在评论中所述,将trait Response
替换为abstract class Response
可解决Scala 2.12中的问题。这将导致捕获$outer
指针并在模式匹配中进行检查(您可以使用-Xprint:jvm
编译器参数来查看它。
我无法找到release notes for 2.12.0中指定的此更改,因此可能不是故意的。强烈建议用单元测试来覆盖它。