I have a method callMethod that takes arguments: methodName (String) and parameters(Object[]).
Now, everything seemed fine for me at the beginning, but I've stumbled upon a problem.
You have to know the type of the Object to use reflection. So far I was determining it in such way:
Class[] getParameterTypes(Object[] parameters) {
Class[] parameterTypes = new Class[parameters.length];
for (int i = 0; i < parameters.length; i++) {
parameterTypes[i] = parameters[i].getClass();
}
return parameterTypes;
}
The callMethod is used to invoke method from external source. And it seems to fail when those methods have primitive parameters as types, or interfaces (List etc.) and I know why.
My question is:
Is there any way around this to keep it that/similar way, or the only solution is to pass the type information (f.e Integer.TYPE for primitives etc.) to the method mentioned above:
callMethod(String methodName, Object[] parameters, Class[] parameterTypes);
Thanks for any help.
Your code would also fail if the caller expected overload resolution based on their variable types. For example:
void foo(String x) {}
void foo(Object x) {}
Object x = "text";
foo(x);
... will call foo(Object) rather than foo(String), but your reflection code will call foo(String).
Now, we don't really know enough about your use case to say whether that's a problem. If the caller does know exactly which method they want to call, with which parameter types, then it's best if they pass them.
If the above situation just wouldn't occur within your context, you may well want to perform some sort of rudimentary overload resolution within your reflection code:
Find all methods with the right name and the right number of parameters.
Check each method to see whether all the argument values are assignable to the corresponding parameter types (see Class.isAssignableFrom - you may need to handle primitives separately).
If there are multiple matching methods, apply some rules to determine which to call. Those rules don't have to be the same as Java, but you need to make sure they're clear.
Related
I just saw this kind of code ImmutableList<String> list= ImmutableList.<String>builder().build();
which really confused me. How to understand the diamond after ImmutableList.?
Most parameterized types in java show up on a type. This looks like so:
interface List<T> {
void add(T elem);
}
So, any List type is parameterized, and as generics is really just a mechanism to link things, what it links is that a List<String> has an add method that takes String objects, and a get(int) method that returns a String, etc.
But, methods themselves may also want this linking behaviour. For example, let's say I want to make a method that takes 2 arguments of the same type, and returns the first non-null one. Here too I want to link things: The types of the 2 argument, and the return type? All the same thing, caller's choice as to what it might be.
Java supports this: Methods can ALSO have generics:
public <T> T firstNonNull(T a, T b) {
return a == null ? b : a;
}
is valid java, and you can call it:
String a = firstNonNull("hello", "world!");
Compiles without requiring a cast.
Java will infer generics if it can; it does that in my previous example (the two arguments are both strings; java infers you meant T to be String there). But you can, if you want, be explicit about it. This is where this funky syntax comes in:
Number a = ClassContainingFNN.<Number>firstNonNull(null, null);
You need the dot to use this syntax, hence why I had to make the call a little longer. With the ImmutableList builder method, java can't (easily) infer what type you wanted, as the call to builder() itself doesn't let the compiler know that you're attempting to build a list of, say, strings. That's why forcing it by explicitly telling java what you want the type param to be is useful, thus, why the usual way to call this builder is:
ImmutableList.<String>builder().add(aString).add(anotherString).build();
Java will always try to infer something if you don't explicitly pick something, but it would just infer Object here. Unless you wanted a list of objects, you need the 'forcibly pick a type' option.
See java support jls-15.12 for supporting TypeArguments after entering Type.
MethodInvocation:
MethodName ( [ArgumentList] )
TypeName . [TypeArguments] Identifier ( [ArgumentList] )
The builder is generic method
public static <E> Builder<E> builder()
And because it's static you entered before method name the type using diamond operator
In case of new instance it'll be as you expected:
new ImmutableList.Builder<Color>()
In my book, there is not written how to invoke a generic method with generic arguments.
I have a method something like this.
public Vector<Double> VEKTOR( Vector <Double>num)
This method will return the vector's size.
To get a result, I need my main invokes that method .. but I don't know how to invoke a generic method and generic arguments because my book did not explain that.. Is it the same like an usual method? I read some questions about that but they were difficult to understand(for lacking english.. and almost of them were talking about skills which I have never learnt)
Yours is not a generic method; it is an ordinary method whose return is a specific realization of a generic type. If it were a generic method then that might complicate things under some circumstances (i.e. when the method's type parameter(s) cannot be inferred), but usually invoking generic methods is substantially the same as invoking ordinary methods.
Furthermore, there are no special rules about invoking methods, ordinary or generic, having a return type or argument type that is a specific realization of a generic type. The method must exist and be accessible. The actual arguments must be assignment-compatible with the declared argument types, and the return value must be used in a manner consistent with its type, if it is used at all. None of this is exciting or different.
In short, your book doesn't say anything about it because there isn't anything to say.
Example:
// ...
Vector<Double> argument = new Vector<>();
// ... maybe add some values ...
Vector<Double> result = VEKTOR(argument);
In my Java application I created methods that return Either<String, T> objects.
This is because in some places I invoke these methods as the parameter of (3rd party) methods that expect a String parameter as input.
While in other places I invoke these methods as the parameter of (3rd party) methods that expect some other parameter type (T) as input.
So depending on the place where I invoke the methods that I created, the code looks like:
their.thirdPartyExpectsString(my.calculateEither().getLeft());
their.thirdPartyExpectsString(my.calculateEither() + "");
or
their.thirdPartyExpectsDouble(my.calculateEither().getRight());
(I defined Either.toString() as Either.getLeft()).
Pay attention, I cannot change the 3rd party code (anyway not without bytecode manipulation), and I would like to keep my design in which I return Either from my methods.
Is there a way to simplify my code and make it look like
their.thirdPartyExpectsString(my.calculateEither());
their.thirdPartyExpectsDouble(my.calculateEither());
I.e., not having to add the getLeft()/getRight() or + "" all the time?
Actually, it does not bother me much if I will have to do
their.thirdPartyExpectsDouble(my.calculateEither().getRight());
because I don't have to do it often. But I would like to get rid of the need to call getLeft() or + "" when my.calculateEither() returns a Left (a String).
Given an either, it's not hard to see if it represents Right or Left, simply by checking which side has a null.
But the problem is with the type conversion, i.e. the compilation error when thirdPartyExpectsString() expects a String but gets an Either.
I was able to catch the return value of my.calculateEither() by AspectJ but I could not see a way how to use something like #AfterReturning advice to make the compiler understand that I want to return my.calculateEither().getLeft(), i.e a String....
Any ideas?
Add the following method to your implementation of the Either class:
#SuppressWarnings("unchecked")
public <T> T whichever() {
return (T) (isLeft() ? getLeft() : getRight());
}
Note that I'm purposefully ignoring the warning about the unchecked cast, and indeed will cause a ClassCastException if you use it in a place where the external API you interface with expects a left value but you invoke it on an Either instance which contains a right value. This method will do an implicit cast based on where you use it. It will cast to a T type where you pass it to another method which expects an argument of type T or you try to assign the method return value to a variable of type T.
So the following demo code:
Either<String, Double> containsString = Either.<String, Double>left("first");
Either<String, Double> containsDouble = Either.<String, Double>right(2d);
their.expectsString(containsString.whichever());
their.expectsDouble(containsDouble.whichever());
their.expectsDouble(containsString.whichever());
will work well in the first invocation and will cause a ClassCastException in the third invocation, just an one would expect, because we consider it as an illegal use case.
In conclusion, it's nice to know that it will work in all places where the T type to which we are implicitly casting is assignable to the actual value contained by the Either object. Unfortunately, you will only find out at run time, should this not be the case.
Add a helper method. Since you cannot add new methods to their, you have to add a static to a class of your own.
Pseudo-code:
public static void thirdParty(Their their, Either either) {
if (either.isLeft())
their.thirdPartyExpectsString(either.getLeft());
else
their.thirdPartyExpectsDouble(either.getRight());
}
You can now call:
MyHelper.thirdParty(their, my.calculateEither())
I have a function that should take an instance of anything extending Object, and just pass the casted instance to a function. I don't want to use a switch, as the function can accept a huge number of object types, so it would become a very big method.
public void attachBufferData(ContextConstant bufferType, Object<T> data, ContextConstant usage) {
glBufferData(bufferType.getGLType(), (T) data, usage.getGLType());
}
The code above doesn't work (as Object isn't a generic type), but it should get across what I'm trying to do.
----- EDIT -----
Ok, I tried this:
public void attachBufferData(ContextConstant bufferType, Object data, Class<?> dataType, ContextConstant usage) {
glBufferData(bufferType.getGLType(), dataType.cast(data), usage.getGLType());
}
But I get a compile error glBufferData(int, long, int) is not applicable for arguments (int, capture#1-of ?, int). I guess it's a massive switch statement then :(
You can't do it like this, I'm afraid. There are three things to consider. I think (2) is the one you really want, but I'm not absolutely certain, so I've left all three issues in there for you to think about.
What signature does glBufferData() have (if it's not overloaded)? If its second parameter is of type Object, then whatever you pass will end up being viewed as an Object there, even if it's a subclass, so you wouldn't achieve anything by having it cast. You might as well just have the type of data as the same type as the second parameter to glBufferData().
If glBufferData() is an overloaded method, and you want to be calling the right one, then you can't do it dynamically: you need some code to test the real type of the class at runtime, and then you choose the right version to call. Choice of overloaded method gets resolved at compile time, not runtime, so you can't just pass it a specific instance you don't know about at compile time and then have it select the right version.
If glBufferData() is a non-overloaded method you've written, contained within your class, then you do have another and better option, which is to make your class generic. If your class takes a type parameter of T, then you can have T data as the second parameter of attachBufferData(), and you can have T data as the second parameter of glBufferData(), so that the types match up.
The point about method overloading is that it's not nearly as clever as it looks. As far as the compiler is concerned, there's really no difference between these two cases.
Case 1:
public void f(int x);
public void f(String s);
Case 2:
public void f(int x);
public void g(String s);
Although we think of case 1 as having just one overloaded method, and case 2 as having two separate methods, as far as the compiler's concerned, in each case there are two distinct methods, and they're distinct because they have distinct signatures (ignoring return type). In both cases, the compiler can choose the right method to call based on the code you write, because it can look at the type of the arguments and the name of the method you've asked for, and find one that matches. The fact that two have the same name is of no more significance than having two methods with different names but the same parameter types.
There's no mechanism for choosing which method to call at runtime in case 1, any more than there is in case 2.
You can declare the type at class level and reuse it wherever required, below is an example.
public class ExceptionHolder<T>
{
private List<T> errors = new ArrayList<T>();
public void addError( T t )
{
errors.add( t );
}
public void addError( List<T> t )
{
errors.addAll( t );
}
}
In the calling code
ExceptionHolder<String> exHolder = new ExceptionHolder<String>();
String can be substitued for any object as needed.
I have two variables
Collection<Service> services = new ArrayList<Service>();
Collection<Subscription> subscriptions = new ArrayList<Subscription>();
and I have the following method, I was wondering how can I find the value of "?" in this method, or how can I find if services was passed or subscriptions was passed?
myMethod(Collection<?> myCollection) {
if (myCollection is of type Service) {
// process service
}
else if (myCollection is of type Subscription) {
// process subscription
}
}
Thanks.
You cannot. Java has erasure-based generics, which means that the type parameters are not available at runtime.
If your collection is not empty, you can do an instanceof on the first element or something, of course.
Using that if-else construct in a generic method defeats the purpose of generics. If you need to know the type of what is being passed in at runtime, it really shouldn't be generic and you should write a separate method for each type.
You can't (except by using reflection), since the generic type parameter gets erased during compilation.
And I would recommend you rethink your design rather than trying to solve it with generics. Passing arbitrary types of collections into the same method is a recipe for problems in the long run.
There is no Java way of doing exactly that. Type erasure gets in the way. And if you need to do so, it is also probably a design issue.
But if you must, there is horrible way to do almost that:
Change your variables to
Collection<Service> services = new ArrayList<Service>(){};
Collection<Subscription> subscriptions = new ArrayList<Subscription>(){};
Note the {} after the (). You are not creating an ArrayList, but a anonymous class that inherits from ArrayList. And type erasure does not apply there. So you can tell the real type by doing something like
private static Class<?> detectType(Collection<?> col) {
return (Class<?>) ((ParameterizedType) col.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
That method will return the actual class. It does work, it is disgusting. It is up to you.