【问题标题】:How does Js.cast() perform its type checking?Js.cast() 如何进行类型检查?
【发布时间】:2020-05-26 08:50:01
【问题描述】:

我正在使用 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】:

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

    一个例子可能是您使用java.lang.Doubledouble 并希望将其视为JsNumber,以便您可以对其调用toPrecision(2)。由于java.lang.Double 是final,所以强制转换为不相关的类型是不合法的,但是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

    这个 bug 继承自闭包编译器,它声称 this 有一个构造函数:https://github.com/google/closure-compiler/blob/6a418aa/externs/browser/w3c_range.js#L241-L251

    解决方法是让闭包编译器将其称为接口,并制作 elemental2 的新版本,以便您可以使用它。


    您可以在此处进行两种解决方法。第一个是用Js.uncheckedCast(DomGlobal.document)作弊并说“是的,我知道Document不是instanceof DocumentRange,但那是因为没有DocumentRange这样的类,所以就假装它有效,这样我就可以打电话给@ 987654337@就可以了”。这就是您已经在做的事情 - 它隐藏了存在错误的事实,但最终它可以工作。

    “正确”的答案是声明您自己的DocumentRange,然后改为使用Js.cast()。这仍然很糟糕——你必须保留你的新界面,直到闭包得到修复,然后 elemental2 被释放,然后你必须记住清理它。

    在这种情况下,我建议对 GWT 撒谎并使用Js.uncheckedCast() - 这里只有一个方法,不太可能以有意义的方式改变。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-23
    • 2021-08-03
    • 2019-11-20
    • 2017-11-29
    • 1970-01-01
    • 2019-10-22
    • 2019-11-22
    相关资源
    最近更新 更多