如何区分具有不同抽象类型字段的对象? [英] How to distinguish between objects with different abstract type fields?

查看:36
本文介绍了如何区分具有不同抽象类型字段的对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Assume, we have an abstract class with an abstract type field:

abstract class A {type T}

现在让我们假设,我们有一个方法,它返回 A 类型的对象,但类型字段 T 可能不同.我们如何区分这些对象?

Now let's assume, we have a method, which returns objects of type A, but the type field T may differ. How can we distinguish between these objects ?

我们可以尝试模式匹配:

We can try a pattern match:

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中被转换为参数类型?)

It seems the type of the type field will be removed by erasure (side question: Because type fields are translated to parameter types in Java ?)

因此,这将不起作用:

scala> Test.tryToDistinguish(new A {type T = Int})
String type

替代方案:我们可以替代地创建一个枚举并在 A 类中添加一个附加字段,以区分对象.但这有点味道,因为这意味着我们要重新实现类型系统.

Alternative : We can alternativley create an enumeration and put an additional field in the class A in order to distinguish the objects. But this smells, because it would mean, that we re-implement the type system.

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

Question : Is there a way to differ between the types of the objects with the help of the type field ? And if not, what will be a good workaround ?

推荐答案

如您所料,抽象类型成员是编译时信息,将在类型擦除期间擦除.这是一个使用 implicit 参数的解决方法.调度是静态的.

As you guessed, abstract type members are a compile time information that will be erased during type erasure. Here is a work around that uses implicit parameters. The dispatch is static.

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的东西来实现.

If the above solutions do not satisfy you, and you want to reify and introspect this type information at runtime after all, you can do that as well, using something called 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

这篇关于如何区分具有不同抽象类型字段的对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆