-
Notifications
You must be signed in to change notification settings - Fork 195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Working with generic types in Java API calls #234
Comments
I'm looking closer into how numeric values behave when being passed via interop calls. I prepared small testing script: var numbers = new java.util.ArrayList();
var maxShort = java.lang.Short.MAX_VALUE;
numbers.add("-----------------------------" + maxShort);
numbers.add(maxShort);
numbers.add(new java.lang.Short(maxShort));
numbers.add(new java.lang.Integer(maxShort))
numbers.add(new java.lang.Long(maxShort));
numbers.add(new java.lang.Double(maxShort));
var maxInt = java.lang.Integer.MAX_VALUE;
numbers.add("-----------------------------" + maxInt);
numbers.add(maxInt);
numbers.add(new java.lang.Integer(maxInt));
numbers.add(new java.lang.Long(maxInt));
numbers.add(new java.lang.Double(maxInt));
var moreThanInt = java.lang.Integer.MAX_VALUE + 1;
numbers.add("-----------------------------" + moreThanInt);
numbers.add(moreThanInt);
numbers.add(new java.lang.Long(moreThanInt));
numbers.add(new java.lang.Double(moreThanInt));
var moreThanLong = java.lang.Long.MAX_VALUE + 1;
numbers.add("-----------------------------" + moreThanLong);
numbers.add(moreThanLong);
numbers.add(new java.lang.Double(moreThanLong));
Packages.test.Util.print(numbers); Here is the Util class: package test;
import java.util.ArrayList;
public class Util {
public static void print(ArrayList numbers){
for (Object num: numbers) {
System.out.println(num + " (" + num.getClass() + ")");
}
}
} And it prints:
I see some inconsistency here. Numbers greater than MAX_INT are double by default - when used as literals, but I can force them to java.lang.Long by using the constructor. This is not possible for smaller numbers. Is this expected effect? |
As you've already guessed, the problem is that we lose generic type information due to type erasure. In the For now, my advice would be to work around the issue on the Java side with specific type signatures, so that our interop can do the right value conversion when calling the method; e.g.: // utility method approach, works with any List
public static void add(List<Long> list, long value) {
list.add(value);
}
// subclass approach but override method(s) with the specific type
public static class LongGeneric extends ArrayList<Long> {
@Override
public boolean add(Long e) {
return super.add(e);
}
} |
Thanks for hints, I checked both. Subclass approach is OK, I will probably use it, where I can. I've also found another hacky way to force construct an ArrayList that contains only // assignment to array forces Integer -> Long conversion
var arr = java.lang.reflect.Array.newInstance(java.lang.Long.class, 2);
arr[0] = 123;
arr[1] = 124;
// array -> List is done on Java side, so Long-s won't be eagerly converted
var longList = new java.util.ArrayList();
java.util.Collections.addAll(longList, arr);
util.getObjects(longList); This is similar to your "utility method" approach, just more dirty. So same problem here, I cannot enforce this as an official way of calling methods that expect ArrayLists. And what about that possibility to obtain Then I could just teach my script writers - "this is how you obtain Long in Graal.js". |
If we don't have a type hint from the Java method signature, there's currently no guarantee that we'll pass the desired Number type. It's unfortunate that the explicit |
Yes, this I found another workaround I can do on Java side, which will be transparent from JavaScript perspective - use abstract So changing API from: public static void handle(ArrayList<Long> numbers){
for (Long num : numbers) {
//use num
}
} to this: public static void handle(ArrayList<Number> numbers){
for (Number num1 : numbers) {
Long num = num1.longValue();
// use num
}
} Here erasure works for my benefit, because from JavaScript there is no difference in API. So this would be backwards compatible. Rhino-style code that pass collection of Thanks for hints and help. |
@woess Another case in this area is that it is impossible to call a method that expects Given the method: public void process(Long x){
//...
} This works in graal.js:
For some reason this works: |
@tporeba JS does not have a |
Hi, I'm late to the party. I'm also trying to get a java.lang.Long into a host object that only has Object signature. MyObj. (In my case it's a 3rd party library) I have a test case trying to work it out https://github.com/warrenc5/graaljs-autobox I'm using small values < 10000 and the Long type was chosen for historical reasons. Was there anything I can do? I'm on 22.3.1. I don't see Java.to ( 5001, 'long' ) - TypeError: 5001 is not an Object |
I have a system based on Rhino and I am working on providing support for newest JavaScript by integrating graal.js as alternative engine/language. The system has a Java API that can be called from scripts. I cannot find a way to use Java API methods that expect generic collections of java primitives/wrappers or generic methods of classes typed with wrappers.
For example, lets take:
,
or one can used a class derived from generic:
,
Now, when using these in graal.js (version 19.2.1), I'm running into trouble in both cases, because this code does not pass
java.lang.Long
as argument to theadd()
method calls. It actually passesjava.lang.Integer
, because of eager graal.js conversions. This sooner or later ends with casting problems in my Java API code, for example getObjects() throws:java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
.In the first example I understand that the script does not know that I intend to use
ArrayList<Long>
. But still I if I could put aLong
somehow into that untyped collection, it would be great. However, I don't see any way to do that.In the second example the
LongGeneric
class is explicitly typed with , but still graal.js does not convertjava.lang.Integer
tojava.lang.Long
while callingadd()
. Maybe type erasure prevents graal from detecting the conversion possibility?Do you have any advice on how to overcome these problems?
Note: this looks similar to discussion from #97 especially this comment. Is there any progress on that
longValue()
subject?The text was updated successfully, but these errors were encountered: