Js.cast()如何执行其类型检查?

问题描述 投票:0回答:1

我正在使用GWT 2.9和elemental2-1.0.0-RC1。

以下代码在运行时抛出ClassCastException

DocumentRange documentRange = Js.cast(DomGlobal.document); // Fails
Range range = documentRange.createRange(); // Never reaches here

当我改为使用Js.uncheckedCast()时,它会成功:

DocumentRange documentRange = Js.uncheckedCast(DomGlobal.document);
Range range = documentRange.createRange(); // Works

Js.uncheckedCast()的文档说:

“您应该始终喜欢常规投射(除非您知道自己在做什么!)。”

我不知道为什么要使用它,所以我感到紧张。有人可以解释Js.cast()如何执行其类型检查以及为什么在这种情况下我需要使用Js.uncheckedCast()吗?

gwt gwt-jsinterop gwt-elemental
1个回答
0
投票

Js.cast()是一种作弊的方法,它可以执行Java语言不允许的操作,但实际上可能是合法的。忽略“它实际上是如何工作的”,其想法是,即使事实证明它是合法的,您现在也可以解决Java可能抱怨的问题。

[示例可能是您将java.lang.Doubledouble并想将其视为JsNumber,因此可以在其上调用tocision(2)。由于java.lang.Double是最终的,因此强制转换为不相关的类型是不合法的,但是Java不知道在GWT中,Double实际上只是一个js Number。因此,您可以使用Js.cast()执行转换。编译器将在其中插入运行时类型检查,在运行时验证您的号码实际上是JS Number实例。

另一个示例可能试图扩展elemental2提供的某些本机类型,以实现缺少功能的解决方法,或执行特定于浏览器的操作。您的新类可能不会扩展现有的类-从JS的角度来看,这是可以的,您只是描述了您知道在运行时将存在的API。因此,我们需要避免Java语言检查“这种转换是否有意义?”,而只是告诉编译器尝试一下。

另一方面,您可以使用Js.uncheckedCast()“撒谎”到编译器。如果您甚至要求运行时跳过检查,并假装它可以工作,则使用此方法。这可以让您做一些奇怪的事情,例如将字符串当作数组来对待,或者解决跨框架问题。不会发出运行时检查,因此,如果缺少方法/属性,您可能只会得到TypeError,而不是适当的ClassCastException。


在elemental2-dom 1.0.0-RC1中,有一个名为DocumentRange的类,但实际上没有任何意义-它被声明为一个类,这意味着可以在JS中进行类型检查,但是浏览器规格说应该是一个“接口”(在JS-land中,这意味着它只是对类型的描述,而不是可以进行类型检查的内容)。 https://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-DocumentRange-method-createRange

此错误是从closure-compiler继承的,后者声称它具有构造函数:https://github.com/google/closure-compiler/blob/6a418aa/externs/browser/w3c_range.js#L241-L251

此修复程序是用于闭包编译器将其称为接口,以及将要发行新版本的elemental2以便您可以使用的。


您可以在此处采取两种解决方法。首先是用Js.uncheckedCast(DomGlobal.document)作弊,然后说:“是的,我知道Document不是instanceof DocumentRange,但这是因为没有像DocumentRange这样的类,所以假装它可以工作,所以我可以叫[ C0]”。这就是您已经在执行的操作-它掩盖了存在错误的事实,但最终还是有效。

“正确”的答案是声明自己的createRange(),然后对此声明一个DocumentRange。这仍然很麻烦-您必须保留新接口,直到修复闭包,然后释放elemental2,然后您必须记住对其进行清理。

在这种情况下,我建议对GWT说谎并使用Js.cast()-此处只有一个方法,并且不太可能以有意义的方式进行更改。

© www.soinside.com 2019 - 2024. All rights reserved.