1

假设,我们有一个带有抽象类型字段的抽象类:

abstract class A {type T}

现在假设,我们有一个方法,它返回 type 的对象A,但 type 字段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中放置一个附加字段以区分对象。但这很奇怪,因为这意味着我们重新实现了类型系统。

问题:有没有办法在类型字段的帮助下区分对象的类型?如果没有,什么是好的解决方法?

4

3 回答 3

2

<%<如果两种类型兼容,您可以使用which 在编译时告诉您。

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>
于 2012-07-03T12:13:22.810 回答
2

正如您所猜到的,抽象类型成员是在类型擦除期间将被擦除的编译时信息。这是一个使用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

编辑:

如果上面的解决方案不能满足你,并且你想在运行时具体化和反省这个类型信息,你也可以这样做,使用一个叫做Manifests.

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
于 2012-07-03T12:50:18.847 回答
1

附带问题:因为类型字段在 Java 中被转换为参数类型?

字节码中没有类型参数,所以类型字段不可能被翻译成它们。类型字段仅在编译期间存在。

替代方案:我们可以替代地创建一个枚举并在类 A 中放置一个附加字段以区分对象。但这很奇怪,因为这意味着我们重新实现了类型系统。

您只能在运行时提出一种类型问题:这是什么类?Scala 2.10 提供了更多信息,但它仍然归结为这个问题。

如果您实际上是子类A,则可以通过反射获取该信息(在 2.10 上)。否则,我不这么认为——我已经在 REPL 上进行了测试,但在那里它不起作用,但是编译到类文件的东西有更多关于它们的信息。

必须对价值观提出任何其他问题。

问题:有没有办法在类型字段的帮助下区分对象的类型?如果没有,什么是好的解决方法?

不,除非 Scala 2.10、反射和子类。好的解决方法?使用价值观。处理它的常用方法不是枚举,而是Manifest,它可以隐式生成。

查找有关如何在 Scala 中绕过类型擦除的问题

于 2012-07-03T16:15:01.067 回答