简体   繁体   English

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

[英]Using asInstanceOf on Generic Types in Scala

Say, I have a class like this: 说,我有一个这样的课:

class Funky[A, B](val foo: A, val bar: B) {
  override def toString: String = s"Funky($foo, $bar)"
}

With some method that looks like this: 使用一些看起来像这样的方法:

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
}

How does the isInstanceOf and asInstanceOf work? isInstanceOf和asInstanceOf如何工作? The Runtime has no information on the actual types that are contained in Funky. Runtime没有关于Funky中包含的实际类型的信息。 So how is this code working? 那么这段代码是如何工作的呢? Any clues? 有线索吗?

Let's just take it apart. 让我们把它拆开。

Suppose that you somehow obtain instances typA: Typable[A] and typB: Typable[B] , so that typA has a method 假设您以某种方式获取实例typA: Typable[A]typB: Typable[B] ,以便typA有一个方法

def cast(a: Any): Option[A] = ...

and similarly for typB . 同样适用于typB The cast method is expected to return Some[A] if the argument is indeed of type A , and None otherwise. 如果参数确实是A ,那么cast方法应返回Some[A]否则返回None These instances can obviously be constructed for primitive types Int and String (they are already provided by the library). 显然可以为原始类型IntString构造这些实例(它们已经由库提供)。

Now you want to use typA and typB to implement cast for Funky[A, B] . 现在,您要使用typAtypB实现castFunky[A, B]

The null check should be clear, you can perform it on anything. null检查应该是清楚的,你可以在任何事情上执行它。 But then comes the first isInstanceOf : 但接下来是第一个isInstanceOf

  else if (t.isInstanceOf[Funky[_, _]]) {

Note that the type arguments of Funky have been replaced by underscores. 请注意, Funky的类型参数已被下划线替换。 This is because the generic parameters of Funky are erased, and not available at runtime. 这是因为Funky 的通用参数被擦除,并且在运行时不可用。 However, we still can differentiate between Funky[_, _] and, for example, Map[_, _] , because the parameterized type itself is preserved, even though the parameters are erased. 但是,我们仍然可以区分Funky[_, _]和例如Map[_, _] ,因为参数化类型本身被保留,即使参数被擦除。 Moreover, isInstanceOf could even differentiate between a TreeMap and a HashMap , even if both instances had compile-time type Map : the runtime type is available, it's only the generic parameters that are forgotten. 此外, isInstanceOf甚至可以区分TreeMapHashMap ,即使两个实例都具有编译时类型Map :运行时类型可用,它只是被遗忘的泛型参数。

Similarly, once you know that t is of type Funky , you can cast it into Funky[_, _] , 同样,一旦你知道tFunky类型,你可以把它投入Funky[_, _]

    val o = t.asInstanceOf[Funky[_, _]]

replacing the generic parameters by existential types. 用存在类型替换泛型参数。 This means: you know only that there are some types X and Y such that o is of type Funky[X, Y] , but you don't know what those X and Y are. 这意味着:你只知道有一些类型XY ,使得o类型为Funky[X, Y] ,但你不知道那些XY是什么。 However, now you at least know that o has methods foo and bar (even though you don't know what their return types are). 但是,现在你至少知道o有方法foobar (即使你不知道它们的返回类型是什么)。

But now you can take o.foo and o.bar and feed them into the typA.cast and typeB.cast . 但是现在你可以把o.fooo.bar带到typA.casttypeB.cast The underscores on the left hand side of the monadic bind mean that you discard the instances of type A and B , which are returned wrapped in Some . monadic绑定左侧的下划线表示您丢弃AB类型的实例,这些实例以Some包装的方式返回。 You only care that both casts don't return a None : 您只关心两个强制转换都不返回None

    for {
      _ <- typA.cast(o.foo)
      _ <- typB.cast(o.bar)
    } yield /* ... */

If one of the casts failed and returned a None , the entire monadic expression would evaluate to None , and so the method would return None , signifying that the overall cast into Funky[A, B] has failed. 如果其中一个强制转换失败并返回None ,则整个monadic表达式将计算为None ,因此该方法将返回None ,表示整体转换为Funky[A, B]失败。 If both casts succeed, then we know that o is indeed of type Funky[A, B] , so we can cast o into Funky[A, B] : 如果两个演员都成功,那么我们知道o确实是Funky[A, B] ,所以我们可以将o投入Funky[A, B]

o.asInstanceOf[Funky[A, B]]

You might wonder "how is this possible, we don't know anything about A and B at runtime!", but this is ok, because this asInstanceOf is there only to satisfy the type-checking stage of the compiler. 您可能想知道“这怎么可能,我们在运行时对AB一无所知!”,但这没关系,因为asInstanceOf只是为了满足编译器的类型检查阶段。 It cannot do anything at runtime , because it can only check the Funky part, but not the erased parameters A and B . 在运行时无法执行任何操作 ,因为它只能检查Funky部分,而不能检查已擦除的参数AB

Here is a shorter illustration of the phenomenon: 以下是该现象的简短说明:

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!")

Just save it as a script and feed it to the scala interpreter. 只需将其保存为脚本并将其提供给scala解释器即可。 It will compile and run without complaints, because the asInstanceOf is blind for anything beyond Map . 它会在没有投诉的情况下编译和运行,因为asInstanceOfMap之外的任何东西都是盲目的。 So, the second asInstanceOf is there only to persuade the type-checker that the returned value is indeed of the type Funky[A, B] , and it is the responsibility of the programmer not to make any nonsensical claims. 因此,第二个asInstanceOf只是为了说服类型检查器返回的值确实是Funky[A, B] ,并且程序员有责任不做出任何荒谬的声明。

To summarize: isInstanceOf is the thing that does something at runtime (it checks that instances conform to some concrete type, and returns runtime-values true - false ). 总结一下: isInstanceOf是在运行时执行的操作(它检查实例是否符合某些具体类型,并返回runtime-values true - false )。 The asInstanceOf has two different functions. asInstanceOf有两个不同的功能。 The first one (which casts to a concrete class Funky[_, _] ) has a side effect at runtime (the cast can fail and throw an exception). 第一个(转换为具体类Funky[_, _] )在运行时具有副作用(强制转换可能会失败并抛出异常)。 The second one ( asInstanceOf[Funky[A, B]] ) is there only to satisfy the type-checking phase at compile time. 第二个( asInstanceOf[Funky[A, B]] )只在编译时满足类型检查阶段。

Hope this helps somewhat. 希望这有点帮助。

You can check that the elements are specific types, with something like: 您可以检查元素是否为特定类型,例如:

Funky(1, 2).foo.isInstanceOf[String] // false
Funky(1, 2).foo.isInstanceOf[Int] // true

But if you try to check for a generic type it will not work. 但是,如果您尝试检查泛型类型,它将无法正常工作。 For example: 例如:

def check[A](x: Any) = x.isInstanceOf[A]
check[String](1) // true
check[String](Funky(1, 2).foo) // true

And the compiler gives you a warning message that explains the error: 编译器会给出一条警告消息,说明错误:

abstract type A is unchecked since it is eliminated by erasure 抽象类型A是未选中的,因为它是通过擦除来消除的

However, the code you showed seems to be working around this by some other method here: 但是,您展示的代码似乎是通过其他方法解决此问题:

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

Without seeing the implementation of those objects, my guess is they have some sort of TypeTag or ClassTag and use that. 没有看到这些对象的实现,我猜他们有某种TypeTagClassTag并使用它。 That is almost always the recommended way to work around erasure. 这几乎总是推荐的解决方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM