texts; public Set getTexts()" /> texts; public Set getTexts()" /> texts; public Set getTexts()"/>

Where to find LinkedHashSet in jsinterop?

155 Views Asked by At

I have the following entity in GWT

@JsType(namespace = "my.entities")
public class MyEntity {
   private Set<String> texts;
   public Set<String> getTexts(){
       if(this.texts==null)
           this.texts=new LinkedHashSet<String>();
       return this.texts;
   }

   public void setTexts(Set<String> texts){
       this.texts=texts;
   }

}

When I call myEntityVar.getTexts() in Javascript the returned object is a HashSet. It seems like jsinterop converts the java implementation of HashSet to JavaScript. But how can I create a new HashSet in JavaScript in order to use myEntityVar.setTexts(texts)? I tried an array for the "texts" param, but that doesn't work. So I somehow need to use HashSet in JavaScript. However, I can't figure out, where to find it. Any idea?

1

There are 1 best solutions below

0
Colin Alworth On

The short answer is that you can't - but then again, you also can't create a plain HashSet in JS either!

The reason that this works at all is that you've enabled -generateJsInteropExports, and while there is a JsInterop annotation on your MyEntity type, there is also one on java.util.Set (and a few other base JRE types). This allows for your code to return emulated java Sets without issue - any method which is compatible with running in JS is supported.

There are a few downsides:

  • Compiled size increases somewhat, since even if you don't use a method, it must be compiled in to your app this way, just in case JS uses it
  • Some methods are not supported - JS doesn't really have method overloading, so toArray() and toArray(T[]) look like the same method in JS. GWT solves this by not supporting the second method at all. (java.util.List has the same issue with remove(T) and remove(int), but it resolves it by renaming remove(int) to removeAtIndex(int) when compiled.)

If you never return these types, you'll probably want to disable this feature entirely - the -excludeJsInteropExports and -includeJsInteropExports flags to the compiler let you control what is exported.


To answer your question more directly, you have a few options that I can see:

  • Allow the setTexts method to be passed something else from JS, such as a JsArrayLike so that you could let users pass in a plain JS array of strings, or even a JS Set. You could go further and accept Object, and then type-check it to see what was passed in. You could even leave the Set override so it could be called from your own Java if necessary, but mark it as @JsIgnore so that GWT doesn't break when you attempt to export overloaded methods.
  • Create a factory method somewhere to create the Set implementation you would like your JS users to use. Since add and remove are supported, the calling JS code can build up the set before passing it in. Something like this:

    @JsMethod(namespace = "my.Util")
    public static <T> LinkedHashSet<T> createSet() {
      return new LinkedHashSet<>();
    }
    

    Now they can call my.Util.createSet(), append items, and then pass it to your setTexts method.