在Scala中的泛型类型上使用asInstanceOf

在Scala中的泛型类型上使用asInstanceOf,scala,shapeless,Scala,Shapeless,比如说,我有一门课是这样的: class Funky[A, B](val foo: A, val bar: B) { override def toString: String = s"Funky($foo, $bar)" } def cast(t: Any): Option[Funky[A, B]] = { if (t == null) None else if (t.isInstanceOf[Funky[_, _]]) { val o = t.asInstanceOf[

比如说,我有一门课是这样的:

class Funky[A, B](val foo: A, val bar: B) {
  override def toString: String = s"Funky($foo, $bar)"
}
def cast(t: Any): Option[Funky[A, B]] = {
  if (t == null) None
  else if (t.isInstanceOf[Funky[_, _]]) {
    val o = t.asInstanceOf[Funky[_, _]]
    for {
      _ <- typA.cast(o.foo)
      _ <- typB.cast(o.bar)
    } yield o.asInstanceOf[Funky[A, B]]
  } else None
}
使用如下所示的某种方法:

class Funky[A, B](val foo: A, val bar: B) {
  override def toString: String = s"Funky($foo, $bar)"
}
def cast(t: Any): Option[Funky[A, B]] = {
  if (t == null) None
  else if (t.isInstanceOf[Funky[_, _]]) {
    val o = t.asInstanceOf[Funky[_, _]]
    for {
      _ <- typA.cast(o.foo)
      _ <- typB.cast(o.bar)
    } yield o.asInstanceOf[Funky[A, B]]
  } else None
}
def cast(t:Any):选项[Funky[A,B]={
如果(t==null)无
else if(t.isInstanceOf[Funky[,]]{
val o=t.asInstanceOf[Funky[,]]
为了{

_您可以检查元素是否为特定类型,例如:

Funky(1, 2).foo.isInstanceOf[String] // false
Funky(1, 2).foo.isInstanceOf[Int] // true
但是,如果您尝试检查泛型类型,它将不起作用。例如:

def check[A](x: Any) = x.isInstanceOf[A]
check[String](1) // true
check[String](Funky(1, 2).foo) // true
编译器会给您一条警告消息,解释错误:

抽象类型A未选中,因为它是通过擦除消除的

但是,您展示的代码似乎通过其他方法解决了这个问题:

_ <- typA.cast(o.foo)
_ <- typB.cast(o.bar)

\up>让我们把它拆开

假设您以某种方式获得实例
typA:Typable[A]
typB:Typable[B]
,这样
typA
就有了一个方法

def cast(a: Any): Option[A] = ...
同样,对于
typB
,如果参数确实是
A
类型,则
cast
方法将返回
Some[A]
,否则返回
None
。显然,可以为基本类型
Int
String
构造这些实例(它们已经由库提供)

现在,您需要使用
typA
typB
Funky[A,B]
实现
cast

null
检查应该是清除的,您可以对任何东西执行它。但是接下来是第一个
isInstanceOf

  else if (t.isInstanceOf[Funky[_, _]]) {
请注意,
Funky
的类型参数已替换为下划线。这是因为
Funky
的泛型参数已被删除,并且在运行时不可用。但是,我们仍然可以区分
Funky[\uu,\u]
和,例如,
Map[\uu]
,因为参数化类型本身被保留,即使参数被删除。此外,
isInstanceOf
甚至可以区分
TreeMap
HashMap
,即使这两个实例都有编译时类型
Map
:运行时类型可用,它只是至少,我们被遗忘了

类似地,一旦您知道
t
属于
Funky
类型,就可以将其转换为 时髦的

    val o = t.asInstanceOf[Funky[_, _]]
用存在类型替换泛型参数。这意味着:您只知道有一些类型
X
Y
,因此
o
属于
Funky[X,Y]类型
,但您不知道那些
X
Y
是什么。然而,现在您至少知道
o
有方法
foo
bar
(即使您不知道它们的返回类型是什么)

但是现在您可以将
o.foo
o.bar
输入
typA.cast
typeB.cast
。一元绑定左侧的下划线意味着您放弃类型
A
B
的实例,它们以
Some
的形式返回。您只关心这个both强制转换不返回
None

    for {
      _ <- typA.cast(o.foo)
      _ <- typB.cast(o.bar)
    } yield /* ... */
您可能想知道“这怎么可能,我们对运行时的
A
B
一无所知!”,但这是可以的,因为这个
asInstanceOf
只是为了满足编译器的类型检查阶段。它在运行时不能做任何事情,因为它只能检查
Funky
部分,而不能检查已删除的参数
A
B

以下是对这一现象的简短说明:

val m: Map[Long, Double] = Map(2L -> 100d)
val what = m.asInstanceOf[Map[Int, String]]
println("It compiles, and the program does not throw any exceptions!")
只需将其保存为脚本并将其提供给
scala
解释器。它将编译并运行,不会产生任何抱怨,因为
asInstanceOf
Map
之外的任何内容都是盲的。因此,第二个
asInstanceOf
只是为了说服类型检查器返回的值确实是
Funky类型[A,B]
,程序员有责任不做出任何无意义的声明

总而言之:
isInstanceOf
是在运行时执行某些操作的东西(它检查实例是否符合某个具体类型,并返回运行时值
true
-
false
)。
asInstanceOf
有两个不同的函数。第一个函数(强制转换到具体类
Funky[\uu,\u]
)在运行时有副作用(强制转换可能会失败并引发异常)。第二个(
asInstanceOf[Funky[a,B]]]
)仅用于满足编译时的类型检查阶段


希望这能有所帮助。

你看过
asInstanceOf
操作符的源代码了吗?@BobDalgleish至少在这里:你看不到太多。大多数“有趣”的方法都是作为
sys.error(“”)实现的
。该类非常基本,字节码不是从任何源代码编译的,而是由编译器直接生成的。源代码主要是用来生成ScalaDoc的。我不明白你想说什么!我想知道编译器如何解释泛型asInstanceOf?我给出了一个cast方法,它是missing!好的,我试着编辑并回答了更多的问题。感谢您包含了更多的方法,但它仍然调用了一些
typA.cast
方法crucial@JoeK我的猜测是,这些东西都是可打字的typeclass的实例。。。