Как привести объект к JsType? - PullRequest
0 голосов
/ 14 сентября 2018

Я объявляю следующий JsType для манипулирования GeoJson совместимыми данными:

@JsType
public class FeatureCollection extends GeoJson {

    @JsProperty
    private Feature[] features;

    public FeatureCollection() {
        super("FeatureCollection");
        features = new Feature[]{};
    }

    public Feature[] getFeatures() {
        return features;
    }

Иногда мне нужно передать мой FeatureCollection объект во внешние библиотеки (например, Turfs.js для выполнения преобразования единиц), которые получают доступ к данным через features свойства. Библиотека возвращает мне новый объект с такими же свойствами (они следуют за RFC GeoJson, как и мой JsType), но я не могу привести его обратно к FeatureCollection:

FeatureCollection fc = new FeatureCollection();
Object o = TurfUtils.toWgs84(fc); // Works and give an object which respect the FeatureCollection scheme (ie an array of Features) when I print it on the javascript console.
FeatureCollection featureCollection = TurfUtils.toWgs84(fc); // Throw a java.lang.ClassCastException

Библиотека Turf имеет JsInteroped:

@JsType(isNative = true, namespace = GLOBAL, name = "turf")
public class TurfUtils {

    public static native <T extends GeoJson> T toWgs84(T geojson);
}

Когда моя FeatureCollection является нативным JsType, она работает, но не позволяет мне использовать мой текущий конструктор, поэтому я ищу способ вернуть объект javascript в мой JsType.

Ответы [ 2 ]

0 голосов
/ 16 сентября 2018

Как объяснил Колин, у вас нет какого-либо типа для проверки объекта GeoJson, поэтому вы не можете использовать instanceof или другие методы ООП для приведения его обратно к безопасности определенного типа. Вы должны установить тип как native=true, name="Object", namespace=GLOBAL, а затем вы можете использовать Js.cast, чтобы привести его обратно к типу GeoJson.

Если вы хотите что-то более ООП, вы можете использовать шаблон посетителя и скрыть «ручную проверку типа» за этим посетителем, например:

import static jsinterop.annotations.JsPackage.GLOBAL;

import javax.annotation.Nullable;
import jsinterop.annotations.JsOverlay;
import jsinterop.annotations.JsType;

@JsType(namespace = GLOBAL, name = "Object", isNative = true)
class GeoJson {
    public String type;
    public final @JsOverlay Type getTypeEnum() { return Type.valueOf(type); }
    public final @JsOverlay void setTypeEnum(Type type) { this.type = type.name(); }

    public static @JsOverlay FeatureCollection featureCollection(Feature... features) {
        FeatureCollection o = new FeatureCollection();
        o.setTypeEnum(Type.FeatureCollection);
        o.features = features;
        return o;
    }

    public static @JsOverlay Feature feature(Geometry geometry) { return feature(null, geometry); }
    public static @JsOverlay Feature feature(@Nullable String featureId, Geometry geometry) {
        Feature o = new Feature();
        o.setTypeEnum(Type.Feature);
        o.id = featureId;
        o.geometry = geometry;
        return o;
    }

    public static @JsOverlay Point point(double x, double y) { return point(new double[] { x, y }); }
    public static @JsOverlay Point point(double[] coordinates) {
        Point o = new Point();
        o.setTypeEnum(Geometry.Type.Point);
        o.coordinates = coordinates;
        return o;
    }

    public static @JsOverlay Polygon polygon(double[][] coordinates) {
        Polygon o = new Polygon();
        o.setTypeEnum(Geometry.Type.Polygon);
        o.coordinates = new double[][][] { coordinates };
        return o;
    }

    public enum Type {Feature, FeatureCollection}

    @JsType(namespace = GLOBAL, name = "Object", isNative = true)
    public static final class Feature extends GeoJson {
        public @Nullable String id;
        public Geometry geometry;
    }

    @JsType(namespace = GLOBAL, name = "Object", isNative = true)
    public static class FeatureCollection extends GeoJson {
        public Feature[] features;
    }

    @JsType(namespace = GLOBAL, name = "Object", isNative = true)
    public static abstract class Geometry {
        public String type;
        public final @JsOverlay Geometry.Type getTypeEnum() { return Geometry.Type.valueOf(type); }
        public final @JsOverlay void setTypeEnum(Geometry.Type type) { this.type = type.name(); }

        public final @JsOverlay <T> T accept(GeometryVisitor<T> fn) { switch (getTypeEnum()) {
            case Point: return fn.point((Point) this);
            case Polygon: return fn.polygon((Polygon) this);
            default: throw new UnsupportedOperationException("unexpected type " + type);
        } }

        public static @JsOverlay @Nullable Point isPoint(@Nullable Geometry g) {
            return g == null ? null : g.accept(new GeometryVisitor<Point>() {
                @Override public Point point(Point g) { return g; }
                @Override public Point polygon(Polygon p) { return null; }
            });
        }

        public static @JsOverlay @Nullable Polygon isPolygon(@Nullable Geometry g) {
            return g == null ? null : g.accept(new GeometryVisitor<Polygon>() {
                @Override public Polygon point(Point g) { return null; }
                @Override public Polygon polygon(Polygon p) { return p; }
            });
        }

        public enum Type {Point, Polygon}
    }

    @JsType(namespace = GLOBAL, name = "Object", isNative = true)
    public static class Point extends Geometry {
        public double[] coordinates;
        public final @JsOverlay double x() { return coordinates[0]; }
        public final @JsOverlay double y() { return coordinates[1]; }
    }

    @JsType(namespace = GLOBAL, name = "Object", isNative = true)
    public static final class Polygon extends Geometry {
        public double[][][] coordinates;
        public final @JsOverlay double[][] shell() { return coordinates[0]; }
    }

    public interface GeometryVisitor<T> {
        T point(Point g);
        T polygon(Polygon p);
    }
}

Пример, основанный на этот , который также включает аннотацию Джексона, поэтому он может быть и на стороне сервера.

0 голосов
/ 14 сентября 2018

@JsType и соответствующие аннотации не создают оболочки, которые пытаются понять, что вы имели в виду , но на самом деле они генерируют код JS, который максимально соответствует тому, что вы сделали делай. Это означает, что если вы говорите: «Я создаю новый не родной тип JS, и у него будет такой конструктор, как этот», GWT скажет «хорошо» и сделает это. И результатом будет тип в JS с конструктором, но объекты, созданные не с помощью этого точного конструктора по определению, не относятся к этому типу, и вы можете получить ошибку, если попытаетесь обработать их так, как если бы они были.

Вместо этого ваш FeatureCollection почти наверняка должен быть нативным типом, вероятно, простым Object в пространстве имен JsPackage.GLOBAL, и вместо конструктора у вас должен быть фабричный метод.

В качестве альтернативы, вы можете рискнуть, используя Js.uncheckedCast, чтобы сказать: «Поверьте мне, этот объект более или менее правильной формы (хотя это может быть неправильный тип), просто используйте его, как если бы он был того же типа», и до тех пор, пока у GWT нет оснований для дальнейшей проверки типов, это позволит вам сойти с рук. Это, вероятно, подходит для использования в вашем собственном коде приложения, но с очень четкими заметками о том, что вы делаете и когда это пойдет не так.


Дополнительное примечание - как правило, если у вас есть геттеры и сеттеры в не-нативном JsType, вы должны пометить их как @JsProperty вместо того, чтобы отмечать приватное поле, так что - если вы сделали поле финальным, другой JS мог бы назначить его позже, в любом случае, если вы заставите получатель или установщик выполнить некоторую проверку или кэширование, любой доступ из JS пропустит это. Помните также, что если типом является JsType, он автоматически экспортирует все свои открытые члены, поэтому вы можете достичь того же самого, просто удалив JsProperty и метод получения и сделав поле открытым.

...