Как Js.cast() выполняет проверку типов?

Я использую GWT 2.9 с element2-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() в этом случае?

1 ответ

Решение

Js.cast()это способ немного обмануть и сделать что-то, что язык Java не позволяет, но может быть законным. Игнорируя "как это на самом деле работает", идея состоит в том, что теперь вы можете избавиться от проблем, на которые Java будет жаловаться, даже если это окажется законным.

Примером может служить java.lang.Double или double и хочу относиться к нему как к JsNumberтак что вы можете вызвать для него toPrecision(2). посколькуjava.lang.Double окончательно, приведение к несвязанному типу незаконно, но Java не знает, что в GWT Double - это просто js Number. Итак, вместо этого вы можете выполнить приведение с помощьюJs.cast(). Компилятор вставит туда проверку типа среды выполнения, проверяя во время выполнения, что ваш номер на самом деле является экземпляром номера JS.

Другим примером может быть попытка расширить некоторый собственный тип, который предоставляет elemental2, либо для реализации обходного пути для отсутствующей функции, либо для выполнения чего-то специфичного для браузера. Ваш новый класс может не расширять существующий класс - с точки зрения JS это нормально, вы просто описываете API, который, как вы знаете, будет существовать во время выполнения. Таким образом, нам нужно избегать проверки языка Java "имеет ли это приведение вообще смысла?" И просто сказать компилятору, чтобы он попробовал это.

С другой стороны, вы можете "солгать" компилятору с помощью Js.uncheckedCast(). Это используется в тех случаях, когда вы даже просите среду выполнения пропустить проверку и просто делать вид, что она сработает. Это может позволить вам делать странные вещи, например обрабатывать строки как массивы или решать кросс-фреймовые проблемы. Проверка времени выполнения не будет выполняться, поэтому вместо правильного ClassCastException вы можете просто получить TypeError, если метод / свойство отсутствует.


В elemental2-dom 1.0.0-RC1 есть класс под названием DocumentRange, но на самом деле это не имеет никакого смысла - он объявлен как класс, что означает, что его тип можно проверить в JS, но в спецификации браузера сказано, что это должен быть "интерфейс" (что в JS-land означает, что это просто описание типа, а не то, что вы можете проверить). https://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html

Эта ошибка унаследована от компилятора закрытия, который утверждает, что у него есть конструктор: https://github.com/google/closure-compiler/blob/6a418aa/externs/browser/w3c_range.js

Исправление предназначено для того, чтобы компилятор закрытия называл это интерфейсом, и чтобы вы могли его использовать в новом выпуске elemental2.


Здесь вы можете найти два обходных пути. Во-первых, обманутьJs.uncheckedCast(DomGlobal.document) и сказать "да, я знаю, что Document не является instanceof DocumentRange, но это потому, что не существует такого класса, как DocumentRange, так что просто представь, что это сработало, и я могу позвонить createRange() на нем ". Это то, что вы уже делаете - он скрывает факт наличия ошибки, но, в конце концов, работает.

"Правильный" ответ - заявить о своем DocumentRange, и сделать Js.cast()вместо этого. Это по-прежнему грубо - вы должны поддерживать свой новый интерфейс до тех пор, пока закрытие не будет исправлено, а затем выйдет elemental2, а затем вы должны не забыть очистить его.

В этом случае я бы посоветовал солгать GWT и использовать Js.uncheckedCast() - здесь есть только один метод, и вряд ли он существенно изменится.

Другие вопросы по тегам