![](/img/trans.png)
[英]In scala, how to make inner type of an abstract type invariant to different instances?
[英]How to distinguish between objects with different abstract type fields?
假设,我们有一个带有抽象类型字段的抽象类:
abstract class A {type T}
现在假设我们有一个方法,该方法返回类型为A
对象,但类型字段T
可能有所不同。 我们如何区分这些对象?
我们可以尝试模式匹配:
object Test {
def tryToDistinguish(a: A) =
a match {
case b: A {type T = String} => println("String type")
case b: A {type T = Int} => println("Int type")
case b: A => println("Other type")
}
}
但是编译器会抱怨:
$>scalac -unchecked Test.scala
Test.scala:8: warning: refinement example.test.A{type T = String} in type patter
n example.test.A{type T = String} is unchecked since it is eliminated by erasure
case b: A {type T = String} => println("String type")
^
Test.scala:9: warning: refinement example.test.A{type T = Int} in type pattern e
xample.test.A{type T = Int} is unchecked since it is eliminated by erasure
case b: A {type T = Int} => println("Int type")
^
two warnings found
看来类型字段的类型将通过擦除来删除( 附带的问题:因为类型字段在Java中已转换为参数类型?)
因此,这将不起作用:
scala> Test.tryToDistinguish(new A {type T = Int})
String type
替代方法:我们可以交替创建一个枚举,并在A
类中放置一个附加字段,以区分对象。 但是,这意味着我们重新实现了类型系统。
问题:是否可以借助类型字段来区分对象的类型? 如果没有,什么是一个好的解决方法?
您可以使用<%<
,它在编译时告诉您两种类型是否兼容。
scala> class A { type T }
defined class A
scala> implicitly[A { type T=String } <%< A { type T=Int } ]
<console>:9: error: could not find implicit value for parameter e: <%<[A{type T = String},A{type T = Int}]
implicitly[A { type T=String } <%< A { type T=Int } ]
^
scala> implicitly[A { type T=String } <%< A { type T=String } ]
res1: <%<[A{type T = String},A{type T = String}] = <function1>
scala> implicitly[A { type T=String } <%< A ]
res3: <%<[A{type T = String},A] = <function1>
如您所料,抽象类型成员是编译时信息,在类型擦除期间将被擦除。 这是一种使用implicit
参数的解决方法。 调度是静态的。
scala> class A {
| type T
| }
defined class A
scala> implicit def distString(a: A { type T = String }) = "String"
distString: (a: A{type T = String})java.lang.String
scala> implicit def distInt(a: A { type T = Int }) = "Int"
distInt: (a: A{type T = Int})java.lang.String
scala> implicit def distOther[O](a: A { type T = O }) = "Other"
distOther: [O](a: A{type T = O})java.lang.String
scala> def distinguish(a: A)(implicit ev: A { type T = a.T } => String) = ev(a)
distinguish: (a: A)(implicit ev: A{type T = a.T} => String)String
scala> distinguish(new A { type T = String })
res2: String = String
scala> distinguish(new A { type T = Int })
res3: String = Int
scala> distinguish(new A { type T = Float })
res4: String = Other
其他方式:
scala> def dist(a: A)(implicit s: a.T =:= String = null, i: a.T =:= Int = null) =
| if (s != null) "String" else if (i != null) "Int" else "Other"
dist: (a: A)(implicit s: =:=[a.T,String], implicit i: =:=[a.T,Int])String
scala> dist(new A { type T = String })
res5: String = String
scala> dist(new A { type T = Int })
res6: String = Int
scala> dist(new A { type T = Float })
res7: String = Other
编辑:
如果上述解决方案不满足您的要求,并且您毕竟想在运行时对这种类型的信息进行验证和内省,则也可以使用称为Manifest
的方法来做到这一点。
scala> :paste
// Entering paste mode (ctrl-D to finish)
abstract class A {
type T
def man: Manifest[T]
}
object A {
def apply[X](implicit m: Manifest[X]) = new A { type T = X; def man = m }
}
// Exiting paste mode, now interpreting.
defined class A
defined module A
scala> def disti(a: A): String = a match {
| case _ if a.man <:< manifest[String] => "String"
| case _ if a.man <:< manifest[Int] => "Int"
| case _ => "Other"
| }
disti: (a: A)String
scala> disti(A.apply[String])
res14: String = String
scala> disti(A.apply[Int])
res15: String = Int
scala> disti(A.apply[Float])
res16: String = Other
附带问题:因为在Java中类型字段已转换为参数类型?
字节码中没有类型参数,因此类型字段无法转换为它们。 类型字段仅在编译期间存在。
替代方法:我们可以交替创建一个枚举,并在A类中放置一个附加字段,以区分对象。 但是,这意味着我们重新实现了类型系统。
您只能在运行时提出一种类型问题:这是什么类? Scala 2.10提供了更多信息,但仍然可以归结为该问题。
如果实际上是A
子类,则可以通过反射(在2.10上)获得该信息。 否则,我不这么认为-我已经在REPL上进行了测试,但是它不起作用,但是编译为类文件的内容会提供更多信息。
关于价值,还有其他任何问题。
问题:是否可以借助类型字段来区分对象的类型? 如果没有,什么是一个好的解决方法?
否,除非Scala 2.10,反射和子类。 好的解决方法? 使用值。 处理它的通常方法不是枚举,而是Manifest
,它可以隐式生成。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.