0

为了操作符合 [GeoJson][1] 的数据,我声明了以下 JsType:

@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。该库返回给我一个具有相同属性的新对象(它们像我的 JsType 一样遵循 GeoJson RFC),但我无法将其转换回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。[1]:https ://www.rfc-editor.org/rfc/rfc7946

4

2 回答 2

2

和相关的@JsType注解不会创建试图理解你想要做什么的包装器但它们实际上会生成尽可能接近你所做的事情的 JS 代码。这意味着,如果你说“我正在创建一个新的非本地 JS 类型,并且它将有一个像这样定义的构造函数”,GWT 会说“好的”并去做。结果将是 JS 中具有构造函数的类型,但根据定义未使用该确切构造函数创建的对象不是该类型,如果您尝试将它们视为它们,您可能会收到错误。

相反,您FeatureCollection几乎可以肯定是本机类型,可能ObjectJsPackage.GLOBAL名称空间中很简单,并且您应该有一个工厂方法,而不是构造函数。

或者,你可以冒险Js.uncheckedCast说“相信我,这个对象的形状或多或少是正确的(尽管它可能是错误的类型),只要像它是相同的类型一样使用它”,只要 GWT 没有有理由进一步进行类型检查,它会让你侥幸逃脱。这可能适合在您自己的应用程序代码中使用,但要非常清楚地说明您正在做什么以及何时会出错。


旁注 - 通常如果你在非本地有 getter 和 setter JsType,你应该将它们标记为@JsProperty而不是标记私有字段 - 如果你将字段设为 final ,其他 JS 可能会在稍后分配它,如果你做了 getter 或setter 做一些验证或缓存,任何来自 JS 的访问都会错过。还要记住,如果一个类型是 a JsType,它将自动导出其所有公共成员,因此您可以在这里通过删除JsProperty和 getter 来实现相同的目的,并使该字段公开。

于 2018-09-14T14:31:49.023 回答
1

正如 Colin 所解释的,您没有任何类型可以签入GeoJson对象,因此您不能使用instanceof或其他 OOP 技术将其转换回特定的类型安全。您必须将类型设置为native=true, name="Object", namespace=GLOBAL,然后您可以使用Js.cast将其转换回GeoJson类型。

如果您想要更多的 OOP,您可以使用访问者模式并将“手动类型检查”隐藏在此访问者后面,例如:

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);
    }
}

基于的示例还包括杰克逊注释,因此它也可以在服务器端。

于 2018-09-16T15:45:58.087 回答