JavaMethodWrapper
A known limitation of the MATLAB-to-Java external language interface is that is is impossible to pass an array of primitive type by reference to a Java method using the standard mechanism.This is discussed, for example, here:
The common motivating example is to be able to use the read(byte[],int,int) overload of java.io.InputStream. The straightforward approach would be:
fis = java.io.FileInputStream(filename)
buf = zeros(1,1024, 'int8');
count = fis.read(buf, int32(0), int32(1024));
However, this fails to do anything useful, because buf is converted into a byte[] by MATLAB, but there is no way to hold the reference to it on the MATLAB side before it "disappears" into the method. In effect, buf is passed by value and remains zero after the call.
This submission seeks to remedy this shortcoming by facilitating the method to be called by an alternative approach, namely the invoke method of the Java reflection API.
For those of you who decide the rest is TLDR, here is how to accomplish the function call by reference using the submission:
methodObj = JavaMethodWrapper(fis, 'read(byte[],int,int)');
[count, buf] = methodObj.invoke(fis, buf, int32(0), int32(1024));
The argument "buf" is passed into the Java method by reference and the new value returned from the MATLAB invoke method. (All the original args to the method can be returned in this way, but the others are uninteresting).
Now the gory details.
A reflection object to a method of a class may be obtained by way of
methodObj = o.getClass().getMethod(...)
where getMethod takes a java.lang.Class[].
In particular, the methodObj for read(byte[],int,int) may be obtained by
ta = javaArray('java.lang.Class',3);
ta(1) = java.lang.Class.forName('[B');
ta(2) = java.lang.Integer.TYPE;
ta(3) = java.lang.Integer.TYPE;
methodObj = fis.getClass().getMethod('read',ta)
Then the method may be invoked with
argsList = java.util.Arrays.asList({
zeros(1,1024,'int8')
int32(0)
int32(1024)
});
count = methodObj.invoke(fis, argsList.toArray())
Two tricks have occurred here. First of all, we have managed to place a byte[] inside an Object[] through the use of the Collections API. Note that the straightforward approach
args = javaArray('java.lang.Object', 3);
args(1) = zeros(1,1024,'int8');
will fail with a message that primitives cannot be placed in the array. This is because MATLAB assumes that it cannot place a primitive in the array without boxing. However, it WOULD be possible to place a primitive array by reference, but this is not supported by the interface.
The second trick is that, due to the signature of invoke(Object, Object[]), the Object[] containing the arguments is passed directly into "Java land" from MATLAB as a reference type. Therefore, the reference to the byte[] is retained inside the Object[] and is accessible later.
This is about as intuitive as quantum mechanics, especially the use of the internal identifier '[B' for byte[]. This process is a useful trick for overcoming the limitations of MATLAB, but it needs better usability.
Hence this submission - the class JavaMethodWrapper hides all the ugliness of the reflection interface from the user and allows simply:
methodObj = JavaMethodWrapper(fis, 'read(byte[],int,int)');
[count, buf] = methodObj.invoke(fis, buf, int32(0), int32(1024));
The MATLAB version of the invoke method hides all the Java reflection ugliness and allows the method to be looked up by the human-readable name returned from 'methods -full' or methodsview.
Other than working around the limitation of primitive array pass-by-reference, JavaMethodWrapper may be used to resolve a particular overload of a class method a-priori, rather than relying on MATLAB's implicit resolution.
For example:
methodObj = JavaMethodWrapper('java.lang.String', 'valueOf(double)');
%use [] for instance arg when calling static method
str = methodObj.invoke([], single(5.0)) %works
str = methodObj.invoke([], 5.0) %also works
methodObj = JavaMethodWrapper('java.lang.String', 'valueOf(float)');
str = methodObj.invoke([], single(5.0)) %works
str = methodObj.invoke([], 5.0) %fails - narrowing conversion
I hope you find this submission useful. I have tested it back to 2016a, but it is probably supported by versions earlier than that.
Cite As
Benjamin Davis (2024). JavaMethodWrapper (https://www.mathworks.com/matlabcentral/fileexchange/74308-javamethodwrapper), MATLAB Central File Exchange. Retrieved .
MATLAB Release Compatibility
Platform Compatibility
Windows macOS LinuxCategories
- MATLAB > Graphics > Graphics Objects >
Tags
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!Discover Live Editor
Create scripts with code, output, and formatted text in a single executable document.
Version | Published | Release Notes | |
---|---|---|---|
1.0.0 |