Understanding [[I.class referenced in some Java source - java

I'm looking at some Java reflection sourcecode that goes like this:
Method fixTransparentPixels = TextureAtlasSprite.class.getDeclaredMethod("fixTransparentPixels", new Class[] { [[I.class });
The method being referenced is declared like so:
private void fixTransparentPixels(int[][] p_147961_1_) {...}
What I do not understand is the [[I.class part. Now, I get that the actual Class[] array is to determine which form of the declared method you want (what parameter types etc.), but what does [[I.class actually mean?
Furthermore, when I try to write this reflection code myself, my IDE gives me syntax errors on the [[I.class bit. Can anyone give me any info on this?
Cheers.

When using getDeclaredMethod(String name, Class<?>... parameterTypes) the parameterTypes must be the class of the parameter (obviously). So in this case fixTransparentPixels require a int[][], so the parameterTypes will be int[][].class.
This will works :
TextureAtlasSprite.class.getDeclaredMethod("fixTransparentPixels", int[][].class);

[[I is the internal name of the class for int[][]:
System.out.println(int[][].class.getName()); outputs [[I
or Class.forName("[[I") == int[][].class.
However, it's illegal to write [[I.class in source code. You should write int[][].class instead.

Related

Java - How to pass the class of a generic type into a method

The method in JavaSparkContext.newAPIHadoopRDD takes class as a parameter.
In scala I was able to use the method like so:
sc.newAPIHadoopRDD(job.getConfiguration,
classOf[AvroKeyInputFormat[AvroFlumeEvent]],
classOf[AvroKey[AvroFlumeEvent]],
classOf[NullWritable])
How do i do that in java?
How do I pass the class of AvroKeyInputFormat<AvroFlumeEvent> into the method.
The closest I got was:
Class<AvroKeyInputFormat<AvroFlumeEvent>> inputFormatClass;
Class<AvroKey<AvroFlumeEvent>> keyClass;
JavaPairRDD<AvroKey<AvroFlumeEvent>, NullWritable> flumeEvents = sc.newAPIHadoopRDD(hadoopConf,
inputFormatClass,
keyClass,
NullWritable.class);
However, now it is complaining that inputFormatClass may not have been initialized. I think I'm missing something...
Variables in Java are either null, or an instance. Your variable inputFormatClass is neither null nor an instance, so you can't do anything to it until you initialize it. That's what it's complaining about.
As for passing the class in, you can do:
Class<AvroKeyInputFormat> clazz = AvroKeyInputFormat.class
Generic types are not stored at runtime - they are only used for verification. That's why you can't have a class of AvroKeyInputFormat<AvroFlumeEvent>

ClassLoad an Enum type

How would one go about instantiating an Enum type via a ClassLoader or similar mechanism? (I'm trying to keep everything under the same context classloader for a standalone server application).
I have something like:
ClassLoader loader = new CustomClassLoader(parent, libDir);
Thread.currentThread().setContextClassLoader(loader);
// trouble area
Class<?> containerClass = loader.loadClass("com.somepackage.app.Name$SERVER");
I had wrongly thought simply loading the Enum would be enough to kick it off (it's private constructor contains startup method calls and what-not).
Doing what I have above does not result in any exceptions, but the JVM just terminates after the last line and the server does not start up.
Obviously doing:
containerClass.newInstance();
Results in an exception being thrown.
To expand on my comment, I think the cleanest you'll get is something like this:
public static <T extends Enum<T>> T loadEnum(ClassLoader loader, String classBinaryName, String instanceName) throws ClassNotFoundException {
#SuppressWarnings("unchecked")
Class<T> eClass = (Class<T>)loader.loadClass(classBinaryName);
return Enum.valueOf(eClass, instanceName);
}
There is really no way to avoid the unchecked cast from Class<?> to a proper enum type. But at least the #SuppressWarnings is limited in scope.
Edit:
Upon further checking, there is actually a simpler way of achieving what you need, without needing to know the name of an instance and without warnings:
Class<?> containerClass = loader.loadClass("com.somepackage.app.Name");
containerClass.getEnumConstants()
Loading an enum doesn't cause it to initialize. You have to reference it through either a field reference or a method reference. So even a simple statement like Name name = Name.SERVER; or Name.SERVER.name(); would do the trick.
See section 5.5 Initialization in chapter 5. Loading, Linking, and Initializing of the Java Virtual Machine Specification.

need help translating a code snippet from java to C# equivalent

Here's the code snippet I'd like to translate from Java to C#. I'm not sure what's causing the error but I've never used ArrayLists and vectors before. Thanks in advance!!
//Java class definitions, constructors, fields, methods etc here.
//sphbasis is a Vector object.
public SphericalHarmonicDecomposition[] getSphericalHarmonicBasis() {
return (SphericalHarmonicDecomposition[])(sphbasislist.toArray(
new SphericalHarmonicDecomposition[sphbasislist.size()]));
}
I've tried doing the following in C#:
//C# class definitions, constructors, fields, methods etc here.
//sphbasis is a ArrayList object.
public SphericalHarmonicDecomposition[] getSphericalHarmonicBasis() {
return (SphericalHarmonicDecomposition[])(sphbasislist.ToArray(
new SphericalHarmonicDecomposition[sphbasislist.Count]));
}
I get the following errors. I'm using Mono and Xamarin studio on a mac.
Error CS1502: The best overloaded method match for
`System.Collections.ArrayList.ToArray(System.Type)'
has some invalid arguments (CS1502) (projectx)
and
Error CS1503: Argument `#1' cannot convert
`matdcal.engine.model.SphericalHarmonicDecomposition[]' expression
to type `System.Type' (CS1503) (projectx)
Please try the following. In Java you need to pass an array to the toArray method, but that's not correct in C# (.NET).
//C# class definitions, constructors, fields, methods etc here.
//sphbasis is a ArrayList object.
public SphericalHarmonicDecomposition[] getSphericalHarmonicBasis() {
return (SphericalHarmonicDecomposition[])(sphbasislist.ToArray());
}
References
Java ArrayList.toArray
C# List.ToArray

Generic List and reflection

I'd like to call via reflection the following method, but I have problem to specify the correct signature:
public void executeRule(List<Node> params, SomethingStrangeFound callMeBack) throws IOException
{
...
}
I tried something like this:
Class partypes[] = new Class[2];
partypes[0] = Class.forName("java.util.List");
partypes[1] = Class.forName("vp.SomethingStrangeFound");
Method meth = cls.getMethod("executeRule", partypes);
It doesn't work because I use "java.util.List" when it must be "List<Node>", but I have no idea how to specify it.
If I just use "java.util.List", I have the following error calling cls.getMethod("executeRule", partypes):
NoSuchMethodException: vp.RuleWebXmlContextParamFacesPortletRenderStyles.executeRule(java.util.List, vp.SomethingStrangeFound)
Any help?
P.S.
At debug time, I see "List<Node>" is resolved with:
(Ljava/util/List<Lorg/w3c/dom/Node;>;Lit/vp/SomethingStrangeFound;)V
but it doesn't help me.
If both parameter classes are available at compile time you can initialize parameter array like this:
Class partypes[] = new Class[] { List.class, SomethingStrangeFound.class };
Method meth = cls.getMethod("executeRule", partypes);
This will guarantee that you have not mistyped qualified class names.
If this still does not work, check available methods in your class using Class#getMethods() method:
for (Method method : cls.getMethods()) {
System.out.println(method);
}
I ran your code and it worked for me.
You have not provided the definition of cls, but I assume it is something like
Class cls = RuleWebXmlContextParamFacesPortletRenderStyles.class;
Since you get a NoSuchMethodException, Class.forName already passed successfully, so no typo there (unless you have multiple SomethingStrangeFound classes, which would mean that you got the package wrong).
Because of this the last thing that comes to my mind is that perhaps you compiled RuleWebXmlContextParamFacesPortletRenderStyles before adding the method in question. Clean your code and recompile.
With partypes of size 2, getMethod will search for a method called "executeRule" with 2 parameters, one of type "java.util.list" and other of type "vp.SomethingStrangeFound"
But your method has only 1 parameter. You need to add a second parameter of type "vp.SomethingStrangeFound" to your method or set partypes size to 1.

Getting the name of a method parameter

In Java 6, imagine I have the following method signature:
public void makeSandwich(Bread slice1, Bread slice2, List<Filling> fillings, boolean mustard)
I would like to know, at runtime, the value that was passed on to slice2 or any other parameter, the important bit here is that I want to get the value by parameter name.
I know how to get the list of parameter types with getParameterTypes or getGenericParameterTypes.
Ideally I would like to get a list of parameter names instead of types. Is there a way to do so?
Parameter names are available if you have told the compiler to include them (compile with debug information). Spring has ParameterNameDiscoverer which can help you obtain the names. The default implementation uses asm ClassReader to do so.
With javac you should include the -g argument to include debug information. With Eclipse I think it is there by default; it can be configured using the preferences: Java -> Compiler and then enable "Store information about method parameters (usable via reflection)" (see also this answer).
Some frameworks use this. For example spring-mvc has #RequestParam which defaults to the param name, if resolvable. It also supports explicit naming - #RequestParam("foo") in case no debug information is provided.
I have found another solution after marking this question as answered. The solution is Paranamer.
Example:
Method method = Foo.class.getMethod(...);
Paranamer paranamer = new CachingParanamer();
String[] parameterNames = paranamer.lookupParameterNames(method) // throws ParameterNamesNotFoundException if not found
// or ...
parameterNames = paranamer.lookupParameterNames(method, false) // will return null if not found
Since Java 1.8, this can be done as long as the parameter names are in the class files. Using javac this is done passing the -parameters flag. From the javac help
-parameters Generate metadata for reflection on method parameters
From IDEs you will need to look at the compiler settings.
If the parameter names are in the class files then here is an example of doing this
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class ParameterNamesExamples {
public static void main(String[] args) throws Exception {
Method theDoSomethingMethod = ExampleClass.class.getMethods()[0];
// Now loop through the parameters printing the names
for(Parameter parameter : theDoSomethingMethod.getParameters()) {
System.out.println(parameter.getName());
}
}
private class ExampleClass {
public void doSomething(String myFirstParameter, String mySecondParameter) {
// No-op
}
}
}
The output will depend on if the parameter names are in the class files. If they are the output is:
myFirstParameter
mySecondParameter
If not the output is:
arg0
arg1
More information on this from Oracle can be found at Obtaining Names of Method Parameters
In addition to this answer:
"Parameter names are available if you have told the compiler to include them"
If you're using Eclipse go to project -> properties -> Java Compiler -> check "Store information about method parameters (usable via reflection)
In Java parameter names are not available via reflection.
This is not possible. Class files do not contains the argument names, as you can see with your IDE's autocompletion when the source is not available.
Therefore, the reflection API is not able to give out parameter names.
You can simply assign the value of the parameter to another value
Bread slice2;
public void makeSandwich(Bread slice1, Bread slice2, List<Filling> fillings, boolean mustard) {
this.slice2 = slice2;
System.out.println(this.slice2.getSomething());
}
Do you own the code of the method? You could annotate the parameters and pass names as arguments #Param("slice1"). Later you will be able to get the annotation and extract parameter name from it.

Categories