说,我有一个这样的课:
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
}
isInstanceOf和asInstanceOf如何工作? Runtime没有关于Funky中包含的实际类型的信息。那么这段代码是如何工作的呢?有线索吗?
答案 0 :(得分:3)
让我们把它分开。
假设您以某种方式获取实例typA: Typable[A]
和typB: Typable[B]
,以便typA
有方法
def cast(a: Any): Option[A] = ...
,同样适用于typB
。如果参数确实是cast
类型,则Some[A]
方法应返回A
,否则返回None
。显然可以为原始类型Int
和String
构建这些实例(它们已经由库提供)。
现在,您要使用typA
和typB
为cast
实施Funky[A, B]
。
null
检查应该清楚,你可以对任何事情进行检查。但接下来是第一个isInstanceOf
:
else if (t.isInstanceOf[Funky[_, _]]) {
请注意Funky
的类型参数已被下划线替换。这是因为 Funky
的通用参数被删除,并且在运行时不可用。但是,我们仍然可以区分Funky[_, _]
和Map[_, _]
,因为参数化类型本身会被保留,即使参数已被删除。此外,isInstanceOf
甚至可以区分TreeMap
和HashMap
,即使两个实例都具有编译时类型Map
:运行时类型可用,它只是通用的遗忘的参数。
同样,一旦您知道t
属于Funky
类型,就可以将其投入
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
。 monadic绑定左侧的下划线表示您丢弃A
和B
类型的实例,这些实例将返回包含在Some
中。您只关心两个演员都不会返回None
:
for {
_ <- typA.cast(o.foo)
_ <- typB.cast(o.bar)
} yield /* ... */
如果其中一个强制转换失败并返回None
,则整个monadic表达式将计算为None
,因此该方法将返回None
,表示整体转换为Funky[A, B]
{1}}失败了。
如果两个演员都成功,那么我们知道o
确实属于Funky[A, B]
类型,因此我们可以将o
投射到Funky[A, B]
中:
o.asInstanceOf[Funky[A, B]]
您可能想知道“这怎么可能,我们在运行时对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[_, _]
有两个不同的功能。第一个(转换为具体类asInstanceOf[Funky[A, B]]
)在运行时具有副作用(转换可能失败并抛出异常)。第二个({{1}})仅用于在编译时满足类型检查阶段。
希望这有所帮助。
答案 1 :(得分:0)
您可以检查元素是特定的类型,例如:
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)
如果没有看到这些对象的实现,我猜他们会有某种TypeTag
或ClassTag
并使用它。这几乎总是推荐的解决方法。