I have a java array of String, and now I want to pass it into a JSNI function. I try to use JsArrayString in GWT, however I find that it can not be initialized directly, because it doesn't have a visible constructor. So how can I pass my String array into the JSNI function and use it in my javascript code? The code is looks like follows:
public void callJSNI() {
String[] stringArray = xxx;
//How should I convert the array into a JSNI-readable format here?
}
private native void JSNIMethod(JsArrayString array) /*-{
//some code to use the array in javascript
}-*/
The API does not provide an easy way to do it, you'd have to create a utility method that will:
Create a new JSNI array
Iterate over the Java array's arguments and populate the JSNI array
Something like this:
public static JsArrayString toJsArray(String[] input) {
JsArrayString jsArrayString = createEmptyJsArrayString();
for (String s : input) {
jsArrayString.push(s);
}
return jsArrayString;
}
private static native JsArrayString createEmptyJsArrayString() /*-{
return [];
}-*/;
As the OP suggested, we can, of course, skip the native initialization and use JsArrayString.createArray().
Now we can get rid of the native initialization, so our code reduces to this:
public static JsArrayString toJsArray(String[] input) {
JsArrayString jsArrayString = JsArrayString.createArray().cast();
for (String s : input) {
jsArrayString.push(s);
}
return jsArrayString;
}
The answer of Eliran Malka is a good starting point. But be aware that there is an extendend soloution that is much more efficient in some cases. That's why I create another answer.
The simple solution is this:
public static JsArrayString toJsArray(String[] input) {
JsArrayString jsArrayString = JsArrayString.createArray().cast();
for (String s : input) {
jsArrayString.push(s);
}
return jsArrayString;
}
The extended/more efficient solution needs some background knowledge...
The GWT compiler uses JS arrays when the Java code used Java arrays. So in this case you already have a JS array. But this can lead to side-effects that result in different behavior in Dev and Prod mode:
As we have real Java arrays in Dev mode, a conversion to a JS array by copying the values is always necessary. Manipulations in one of the arrays won't be visible to the other array.
In Prod mode if we use the Java array directly as JS array we would only have one array at all. So manipulations in either Java or JS code would affect the other world.
But if the following things apply to your use-case, you can use the array without creating another one:
One of these statements must be true
The array isn't manipulated by the JS code at all
The array isn't used by the Java code after it was given to the JSNI method
One of these statements must be true
The array is only used once in the JS code (the JS code doesn't create references to the array that last beyond the native method call)
The array isn't manipulated by Java code after the native call.
In this case you will not have side effects (different behavior in Dev and Prod mode) and the array can directly be used in the native code.
This kind of implementation is contained in GWT's JsArrayUtils. But unfortunately there is currently no implementation for String arrays. But the implementation will look like this:
public static JsArrayString readOnlyJsArray(String[] array) {
if (GWT.isScript()) {
return arrayAsJsArrayForProdMode(array).cast();
}
JsArrayString dest = JsArrayString.createArray().cast();
for (int i = 0; i < array.length; ++i) {
dest.push(array[i]);
}
return dest;
}
private static native JavaScriptObject arrayAsJsArrayForProdMode(Object array) /*-{
return array;
}-*/;
Finally I find the answer myself.
The JsArrayString can be "initiated" with JsArrayString.createArray(), and then you can do whatever manipulation as it is in javascript.
Related
The JSNI method does not accept any parameters but return a Java Object type:
public static native String nativeJSFuncGwt() /*-{
$wnd.console.log($wnd.someJSFunc());
return "" + $wnd.someJSFunc() + "" ;
}-*/;
//someJSFunc returns { abc:xcv, def:asd}
I can see the value getting printed in the javascript console but java side is not able to understand the casting.
Is it because the native method does not accept any parameters ?
String tokenFromNativeJS = nativeJSFuncGwt(); // String value is null
The documentation also is not clear enough in GWT.
Step one, avoid JSNI, you are better off defining a JsInterop method which provides the same API access. JSNI will still work in GWT2 but JsInterop is the way forward for GWT3, and is often much easier to read and write. This would look something like this:
#JsMethod(name = "someJSFunc", namespace = JsPackage.GLOBAL)
public static native String someJSFunc();
Step two, define a Java type which fits your expected JS return value. This will work with either JSNI or JsInterop. In JSNI you would make a JavaScriptObject subclass, and provide methods which access the fields (see http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html and other docs for more detail, but as per step one, I'm not going to go into more depth on this here). For your example object, this would look like this in JsInterop:
#JsType(isNative = true, name = "Object", namespace = JsPackage.GLOBAL)
public class SomeReturnObject {
public String abc;
public double def;
}
Obviously replace the field names and types with whatever is appropriate in your own project. Give this new type with the placeholder name, here's what your global someJsFunc would look like:
#JsMethod(name = "someJSFunc", namespace = JsPackage.GLOBAL)
public static native SomeReturnObject someJSFunc();
And you would use it like you expect in plain Java - no need to write JSNI any more:
SomeReturnObject object = someJSFunc();
DomGlobal.console.log(object.abc + ": " + object.def);
I am posting here what finally worked for due to GWT version(2.4) constraint
From GWT Doc:
Outgoing Java type:
Any other Java Object (including arrays)
What must be passed:
Java Object of the correct type that must have originated in Java
code; Java objects cannot be constructed from “thin air” in JavaScript
My code with modification would like:
public static native MyObject nativeJSFuncGwt(MyObject obj) /*-{
var xyz = $wnd.someJsFunc();
obj.#package.name::setter1(Ljava/lang/String;)(xyz);
return obj;
}-*/;
I wish documentation could have been more clear.
Short Question:
Passing a parameter from C++/C JNI to Java method who supposed to modify. Will the modified result available back in C/C++ JNI? Is there any way to do it? Other than return and a callback from java?
Continue if above is not clear:
Calling below function from C/C++ JNI with jcharArray. Below Java function is supposed to work on passed char array and store the final result in same. So that modified result will be available back in C/C++ JNI.
I have java function accepting a char[] like below
void GetName(char[] s)
{
String t = "Test";
// Work on t and store the result in s
s = t.toCharArray();
}
How I can achieve this ... I am getting all the examples that are another way around like Java calling C/C++.
Java only supports pass by value. If you want to return a reference you need to either
a) return it
char[] getName();
b) Pass a mutable object to reference it
void getName(char[][] nameArray) {
nameArray[0] = t.toCharArray();
}
c) Pass a call back
void getName(Consumer<char[]> listener) {
listener.accept(t.toCharArray());
}
So for my scenario, I modified the declaration like below
void GetName(char[] s) to void GetName(PersonName obj)
So now, I am creating an object of PersonName in JNI and passing that as a parameter to the java function. Later I am using the same object to call getter methods of the PersonName class to get the values char[] RetriveName();
I also found that using jfield also you can get the values of the class data members but then those members should be public
I have a method as
private void show(Object[] arr) {
for (Object o : arr) {
System.out.println(o);
}
}
I would like to call this method as
// belows are not valid but I'd like to achieve
show({1,2,3});
show(new String["a","b","c"])
but I don't want to create an array to call this method. (Please don't be suggest to change the signature of my show method.This is just an example.Actual method that I use is from 3rd party lib.)
How can I achieve this by utility classes or anything else?
You can either use varargs as mentioned in the comments or declare the array this way:
show(new String[] {"a","b","c"})
Create a varargs wrapper method:
private void myShow(Object... arr){
show(arr);
}
// No change to your existing 3rd party method:
private void show(Object[] arr) {
for (Object o : arr) {
System.out.println(o);
}
}
You can then call the wrapper method like this:
myShow("a","b","c");
myShow(1,2,3,4);
Hope this helps!
What you are looking for is not a way to pass an array to a method without declare it, you are looking for a "single line data instatiation for array"... or by the real name "in-line declare"
show(new Object[]{"a","b","c"});
You can accomplish this by using varargs. A simple edit to your function will not only allow an array of objects but allow you to accomplish what you are looking for.
Instead of using Object[] use Object...
In Java, or Groovy, say I have a String array like
myArray = ["SA1", "SA2", "SA3", "SA4"]
I want to call a different function based off of each string.
class Myclass{
public static void SA1() {
//doMyStuff
}
public static void SA2() {
//doMyStuff
}
...etc
}
I would love to be able to loop through my array and call the functions that they pertain to without having to compare the string or make a case statement. For example is there a way to do something like the following, I know it doesn't currently work:
Myclass[myArray[0]]();
Or if you have suggestions of another way I can structure something similar.
In groovy you can do:
Myclass.(myArray[0])()
In Java you can do:
MyClass.class.getMethod(myArray[0]).invoke(null);
In Groovy, you can use a GString for dynamic method invocation:
myArray.each {
println Myclass."$it"()
}
You can, for instance, declare an interface such as:
public interface Processor
{
void process(String arg);
}
then implement this interface, for example in singletons.
Then create a Map<String, Processor> where keys are your strings, values are implementations and, when invoking:
Processor p = theMap.containsKey(theString)
? theMap.get(theString)
: defaultProcessor;
p.process(theString);
I suggest you look at Reflection APIs, to call methods at runtime
check Reflection docs
Class cl = Class.forName("/* your class */");
Object obj = cl.newInstance();
//call each method from the loop
Method method = cl.getDeclaredMethod("/* methodName */", params);
method.invoke(obj, null);
Well I wrote some code and all I was doing was for loops, but changing which method I called. I tried using a for loop so it'd be a bit neater (and out of curiosity to see if it could be done), but it doesn't compile when I do it this way, because it doesn't recognize an item in an array as a method, I think. This is what I have:
String[] moveArray = {moveRight,moveDown,moveLeft,moveUp};
for (i = 0; i < 4; i++) {
while (myWumpus.moveArray[i]) {
myWumpus.moveArray[i];
generator.updateDisplay();
}
}
When I try compile I get
not a statement myWumpus.moveArray[i]();
';' expected myWumpus.moveArray[i]();
(It refers to the first statement in the while loop)
So, I think it's maybe because I'm making it an Array of type String? Is there a type Method? Is this at all possible? Any solutions welcome :). Also, I can get it to work using 4 while loops, so you don't need to show me that solution.
You cannot store methods directly in arrays. However you can store objects, which implement the same method differently. For example:
Mover[] moveArray = {new RightMover(), new DownMover() new LeftMover(), new UpMover() };
for (i = 0; i < 4; i++) {
while (myWumpus.moveArray[i]) {
moveArray[i].move();
generator.updateDisplay();
}
}
Yes, you can store methods in arrays using Reflection, however it is likely that what you actually want to do in this situation is use polymorphism.
As an example of polymorphism in relation to your problem - say you created an interface as follows:
public interface MoveCommand {
void move();
}
You can then create implementations as follows:
public class MoveLeftCommand implements MoveCommand {
public void move() {
System.out.println("LEFT");
}
}
etc. for the other move options. You could then store these in an MoveCommand[] or collection like a List<MoveCommand>, and then iterate over the array/collection calling move() on each element, for example:
public class Main {
public static void main(String[] args) {
List<MoveCommand> commands = new ArrayList<MoveCommand>();
commands.add(new MoveLeftCommand());
commands.add(new MoveRightCommand());
commands.add(new MoveLeftCommand());
for (MoveCommand command:commands) {
command.move();
}
}
}
Polymorphism is very powerful, and the above is a very simple example of something called the Command Pattern. Enjoy the rest of your Wumpus World implementation :)
You can't store methods in arrays in Java, because methods aren't first-class objects in Java. It's a reason some people prefer to use other languages like Python, Scheme, etc.
The work-around is to create an interface which contains one method, then create four classes implementing that interface - the MoveRight, MoveLeft, etc... classes. Then you can store instances of those classes in your array and call them all the same way.
You can't call methods like that. But you can using reflection:
Just change the first line in the while-loop to:
Method m = myWumps.getClass().getMethod(moveArray[i]); // if the method is void
m.invoke(myWumps);
(you will have to declare/catch a few exceptions)
But you'd better avoid reflection, and use the Command pattern instead.
Updated answer for Java 8 and onwards-
Since the introduction of lambda expressions and method references in Java 8, storing various methods in variables is now possible. One main issue is that arrays don't currently support generic objects in Java, which makes storing the methods in arrays less doable. However they can be stored in other data structures like a List.
So for some simple examples you can write something like:
List<Comparator<String>> stringComparators = new ArrayList<>();
Comparator<String> comp1 = (s1, s2) -> Integer.compare(s1.length(), s2.length());
stringComparators.add(comp1);
or
List<Consumer<String>> consumers = new ArrayList<>();
Consumer<String> consumer1 = System.out::println;
consumers.add(consumer1);
and then loop/iterate through the List to get the methods.