在Scala中处理类型擦除匹配
这是示例代码:在Scala中处理类型擦除匹配,scala,Scala,这是示例代码: object GenericsTest extends App { sealed trait CommonResponse trait Requester[SomeType] { trait Response extends CommonResponse { def entity: SomeType } case class SomeResponse(entity: SomeType) extends Response //
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")
}
}
它打印“Get string response 42”而不是“Get integer response 42”
真正的代码是一个实现不同类型索引的服务,如果数据已经被索引或没有索引,它将返回不同的响应,等等
您可以在响应中公开对外部
请求者的引用:
trait Requester[SomeType] {
trait Response {
def requester: Requester.this.type = Requester.this
// ...
}
// ...
}
然后,您可以在响应上进行匹配。请求者:
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")
}
您可以在响应中公开对外部请求者的引用:
trait Requester[SomeType] {
trait Response {
def requester: Requester.this.type = Requester.this
// ...
}
// ...
}
然后,您可以在响应上进行匹配。请求者:
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")
}
由于内部trait响应
在运行时中没有用于路径相关
类型检查的实现外部
方法,这导致trait实际上是Java中的一个接口
因此,对于您的示例,您应该使用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
由于内部trait响应
在运行时中没有用于路径相关
类型检查的实现外部
方法,这导致trait实际上是Java中的一个接口
因此,对于您的示例,您应该使用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
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
的类型
在本例中,您定义了两个请求者的实例。
StringRequester
和IntegerRequester
。StringRequest
的响应类型是Requester[String]#response
,IntegerRequester
是Requester[String]#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 ---")
}
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
的类型
在本例中,您定义了两个请求者的实例。
StringRequester
和IntegerRequester
。StringRequest
的响应类型是Requester[String]#response
,IntegerRequester
是Requester[String]#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 ---")
}
正如我在评论中所说,将特质响应
替换为抽象类响应
修复了Scala 2.12中的问题。这将导致捕获$outer
指针并在模式匹配中检查它(您可以使用-Xprint:jvm
编译器参数查看它)
我找不到中指定的此更改,因此可能不是有意的。强烈建议使用单元测试来覆盖此更改。如我在评论中所述,将特征响应
替换为抽象类响应
修复了Scala 2.12中的问题。这将导致捕获$outer
指针及其being检查模式匹配(您可以通过使用-Xprint:jvm
编译器参数来查看它)
我找不到中指定的此更改,因此可能不是有意的。强烈建议使用单元测试来覆盖此更改。-未选中以警告外部指针。如果您使用的是scala>=2.12.0,则仅替换特征响应