I'm struggling to get the type of second generic from an object.
The abstract class takes two Generic types T and S
abstract class Concept<T, S> {
public Concept() {
//do nothing
}
public final Class<?> getTypeParam() {
ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
Class<?> result = (Class<?>) parameterizedType.getActualTypeArguments()[0];
return result;
}
}
In this derivative class one (in this case T) generic is defined:
public class Decision<S> extends Concept<String, S>{
public Decision () {
super();
System.out.println(getTypeParam()); //returns the first parameterized type. How do I get the second one?
}
}
When I now run it I get the first parmerized generic back. Great. But how do I get out the second one?
public class Main {
public static void main(String[] args){
Decision<Boolean> myBooleanDecision = new Decision<>();
}
}
Your problem is based on the fact that you aren't clear on your requirements.
Your method signature looks like this: public final Class<?> getTypeParam()
But what you overlook: the number of type parameters with your classes isn't fixed. The base Concept class allows for two type parameters, but the Decision subclass "fixes" the first parameter to String.
Thus: you have to decide what you actually want/need. There are various solutions, like:
public final Class<?> getFirstParam() ... to return index 0, always
public final Class<?> getSecondParam() ... to return index 1, always (which obviously fails for classes that only have 1 generic type parameter)
public final List<Class<?>> getParams() ... to return a list with all entries
That is your option space. Which solution to pick solely depends on the purpose of that method (which we don't know anything about).
Personally, I would go for the third option, as that will work for 0, 1, ... n type parameters, without any changes.
Change the decleration:
Decision<Boolean> myBooleanDecision = new Decision<>("Binary Decision") {};
Now this will result in the second generic:
Class<?> result = (Class<?>) parameterizedType.getActualTypeArguments()[0];
Output is:
class java.lang.Boolean
Related
Question 1:
Suggest I have a example like this:
public <T> void methodA(T req) { // doSth }
psvm(){
ClassA aClass = new ClassA();
aClass.<String>methodA("hello world");
}
then can I get type String in methodA without actually passing String to it?
Question 2:
If the previous example is transformed to this:
public <T> void methodA(Predicate<T> req) { // doSth }
psvm(){
ClassA aClass = new ClassA();
aClass.<String>methodA(item -> "hello world".equals(item));
}
I mean what was passed into methodA is no more a simple object but a lambda expression, also, can I get type String in methodA?
No, it is not possible to determine the generic type of a generic method, due to type erasure. At compile time, the generic type of the method is discarded, and there is no way to get this information at runtime.
public class ClassA {
public <T> void methodA(T req) {
Class<T> genericType = ?; // Impossible to get e.g. Class<String>
// [...]
}
public static void main(){
new ClassA().methodA("hello world");
}
}
There are certain scenarios where generic type information is retained (such as extending a generic class with a specific generic type, e.g. class MyList implements List<String>). However, there is no way to do so for a generic method.
The best you could do, if you needed that information, would be to pass it in (which you stated you didn't want to do):
public <T> void methodA(T req, Class<T> type) {
// [...]
}
Another option, if all you needed was the type of the object passed in as opposed to the type of the generic method, would be to get the class of the object.
public <T> void methodA(T req) {
Class<?> type = req.getClass(); // Assuming req is non-null
// [...]
}
Note that with this option in this specific example, the generic type is no longer being used for anything, so it can be dropped from the method signature. This may not always be the case, such as if multiple parameters or a parameter and the return type needed matching generic types.
public void methodA(Object req)
I'm writing custom annotation processor. Though I'm able to process class type without generic type but unable to get actual types for generic types.
MyBase Interface
interface MyBase<T, S> {
void method1(S, T);
}
Example Class1
public class KpA{
...
}
Example Class2
public class KpB{
...
}
The Target Annotated class
#MyAnnotation
interface MyHandler extends MyBase<KpA, KpB>{
}
Processing code snippet
TypeElement[] getActualTypes(ProcessingEnvironment pEnv, TypeElement element){
List<TypeElement> myBaseElement = element.getInterfaces().stream().map(v-> (TypeElement) pEnv.getTypeUtils().asElement(v)).collect(Collectors.toList());
myBaseElement.get(0).getTypeParameters().stream().forEach(System.out::println);
return null;
}
When I Sysout the getTypeParameters I get output as S and T; how can I get KpA and KpB.
When you deal with type system, it is important to understand what you precisely want. Do you want those type parameters because their type variables are used in some field/method of base type? E.g. your parametrized base type is something like Collection<SomeItem> and you want to know return type of add? Or is MyBase some kind of marker interface like Clonable and you want precisely it's parameter?
Whatever; let's suppose, that your situation is exactly as described in your question: MyBase is some interface and you simply want it's arguments for unknown reasons. Here is an inflexible, dumb way to accomplish that:
// instance of javax.lang.model.util.Types
protected final Types types;
// instance of javax.lang.model.util.Elements
protected final Elements el;
// TypeMirror for java.lang.Object
protected final TypeMirror theObject = ...
private static final TYPE_REFINER = new DeclaredTypeRefiner();
private class DeclaredTypeRefiner extends TypeKindVisitor6<DeclaredType, TypeMirror> {
#Override
public DeclaredType visitDeclared(DeclaredType type, TypeMirror desirableParent) {
if (types.isSameType(types.erasure(type), desirableParent)) {
return type;
}
return null;
}
#Override
public DeclaredType visitUnknown(TypeMirror typeMirror, TypeMirror typeMirror2) {
return defaultAction(typeMirror, typeMirror2);
}
#Override
protected DeclaredType defaultAction(TypeMirror type, TypeMirror desirableParent) {
if (types.isSameType(type, theObject)) {
return null;
}
final List<? extends TypeMirror> superTypes = types.directSupertypes(type);
for (TypeMirror parent : superTypes) {
final DeclaredType discovered = visit(parent, desirableParent);
if (discovered != null) {
return discovered;
}
}
return null;
}
}
public TypeMirror getTypeArg(TypeMirror actual, TypeMirror base, int idx) {
DeclaredType found = TYPE_REFINER.visit(actual, types.erasure(base));
return found.getTypeArguments().get(idx);
}
To obtain the type of 2nd parameter (e.g. KpB):
// type mirror of MyHandler goes here
TypeMirror concrete = ...
// your base type goes here. Real code should cache it
TypeMirror base = types.getDeclaredType(el.getTypeElement("com.example.MyBase"));
// boom
TypeMirror arg = typeHelper.getTypeArg(concrete, base, 1);
Here is what goes on here:
Iterate over parent types looking for DeclaredTypes (e.g. classes and interfaces)
Check whether a declared type is our base interface
If we found it, return it to caller, so it can check the type args
The trick lies in call to directSupertypes: as explained in it's documentation, that method automagically resolved type arguments for you.
We compare type erasures (aka raw types) because they are expected to be comparable in meaningful way (it is more or less the same as comparing TypeElements). Please do not try to compare generic types with isSameType — this is guaranteed to produce surprising results due to the way type system works.
Note, that the code above might not always provide correct results due to multiple inheritance: MyHandler may inherit MyBase from multiple parents, leading to confusion about real identities of T and S. If those variables aren't used anywhere (e.g. we are speaking about a pure marker interface), it is going to remain a mystery: the compiler does not care, so nobody knows (I presume, that you don't want to manually compute upper/lower bounds, do you?) If those variables are used in some methods/fields, you should be able to get their types by examining those methods/fields on MyHandler (how to do that is a slightly tricky matter worth another question).
Let's say I have the following class:
public class MyClass<T> {
Class<T> type;
List<T> items;
public Class<T> getType() { return type; }
...
}
I need to store objects of the above class in a generic container:
public class MyContainer<T> {
Class<T> type;
List<MyClass<T>> myClasses;
public Class<T> getType() { return type; }
...
}
Let's say later in my code I'm given a generic object as an argument:
public class Main {
public static void main() {
List<MyContainer<?>> containers;
...
}
public void someMethod(MyClass<?> myClass){
...
}
}
I have type T as reflection. I need to cast MyClass<?> back to its intended type. So if it was declared as MyClass<String> I need to cast it back to MyClass<String>. I can't seem to do this with the reflection type because it is only capable of casting T, not MyClass<T>.
I could cast T by doing this for example:
myClass.getType().cast(myClass.getItem()); // Where getItem returns an object of the type T
But what I want is this:
myClass.getType().cast(myClass); // getType() returns T not MyClass<T>
So the question is, how do I cast a generic base type along with its reflection type, given the scenario above?
This is impossible due to type erasure. Reflection can't help you here because at runtime the generic type information isn't available. In other words at runtime there is no MyClass<String>, there is only MyClass. MyClass<String> is a pure compile-time creature.
If you have some piece of code that expects a certain type, declare that at compile time. This type expectation could be concrete or generic:
public void someMethod(MyClass<String> myClass)
or
public <T> void someMethod(MyClass<T> myClass)
If you know the type, you don't need cast(), you can just cast:
#SuppressWarnings("unchecked")
MyClass<String> myClassStr = (MyClass<String>)expression;
Since T doesn't exist at runtime, which you know, you can't use it. You also can't get a Class<List<T>>.
myClass.getType().cast(myClass.getItem()) doesn't really work for you because if you have a MyClass<?> then you also have a Class<?> which only returns Object. A Class<T> doesn't really help you either, because you need to actually know what T is.
You can pretty much do two things.
Use decisions:
if(myClass.getType() == String.class) {
#SuppressWarnings("unchecked")
final MyClass<String> myStrings = (MyClass<String>)myClass;
// use MyClass<String> ...
}
Use polymorphism:
MyClass<String> myClass = new MyClass<String>(...,
new BiConsumer<String, JTable>() { // (or lambda ...)
#Override
public void accept(String s, JTable table) {
// use String ...
// configure JTable ...
}
});
... elsewhere ...
public void someMethod(MyClass<?> myClass) {
someCaptureMethod(myClass);
}
private <T> void someCaptureMethod(MyClass<T> myClass) {
Consumer<T, JTable> consumer = myClass.getConsumer();
for(T theT : myClass.getList()) {
consumer.accept(theT, myTable);
}
}
Generally the second example is considered the "better" way but the code might be longer in versions of Java prior to 8.
I have a method whose signature is:
public static <T> T isA(Class<T> clazz);
So I can do this:
String str = isA(String.class);
Where I'm having trouble is if I want T to be Class<String>:
Class<String> cls = isA(???);
I'm not sure how to formulate the argument to isA(). Can anyone offer guidence?
In case you're wondering why I want to do this, I'm using EasyMock to mock a class that takes a Class<T> argument.
EDIT: I was asked to add an example of what I'm trying to do.
I'm trying to use EasyMock to mock Solr's SolrCore class as part of a test case. The signature of one of SolrCore's methods is:
public <T extends Object> T createInitInstance(PluginInfo info, Class<T> cast, String msg, String defClassName);
With EasyMock I can set up an expectation for that method. The construct isA(PluginInfo.class), for example, tells EasyMock to match any object of class PluginInfo:
QueryParserPlugin queryPlugin = createNiceMock(QueryParserPlugin.class);
SolrCore core = createNiceMock(SolrCore.class);
expect(core.createInitInstance(
isA(PluginInfo.class), isA(xxx),
anyString(), anyString())).andReturn(queryPlugin);
My problem is telling isA() to match any object of class Class<T>, where T in this case is QueryParserPlugin.
The issue with reflection and java is type erasure. You just need to give the compiler a hint.
Since the object you are expecting is of type T, and the method itself is generic, you kinda have to
let java know what type you really are working with.
All it needs is a bit of a hint, something during runtime that can be passed through a compiled method that holds that type information.
So at compile time you have a method that takes in:
Class<String>
the compiler only knows the compiled type, so has no clue that the type itself is a class definition, making it impossible to assign if you don't tell java what the type of the assignment is.
So this works:
Class<String> myVar = String.class;
Or this works:
Class<String> myVar = isA(String.class);
Or this works
public <T> T myMethod(Class<T> object)
Class<String> class = myMethod(String.class)
but this doesn't work
public <T> void myMethod(Class<T> object);
since we have no assignment of T for the generic.
so how do you let the compiler know that T really is a class?
public <T> void myClassWrapper(Class<? super T> object);
myMethod(myClassWrapper(String.class));
so by passing it through a method that accepts you let the compiler know that at minimum that this thing is a class and that it represents T at some part of T's own hierarchy, thus letting the method compile.
or of course you could always just do
myMethod((Class<String>)string.class));
but I think thats kinda hackish personally. I am not a fan of casts that are not explcit and wrapped in a method.
Since you cannot control the signature of the test framework, you can however let java know your intentions.
I am not sure how easy mock works, but heres a test to kinda help explain whats going on.
#Test
public void testCreation(){
Object integer = 5;
String myString = "A String";
int five = typeTheObject(Integer.class, integer);
Class<String> stringClass = typeTheObject(myString);
Class<Integer> myInt = typeTheObject(five);
Class<?> myClass = typeTheObject(String.class);
TypeValidator typeValidator = new TypeValidator(stringClass);
typeValidator.isA(typeTheObject(String.class));
}
public static class TypeValidator{
private final Object objectToValidate;
public TypeValidator(Object object){
objectToValidate = object;
}
public <T> T isA(T type){
if(objectToValidate.getClass().isAssignableFrom(type.getClass())){
return type;
}else{
Assert.fail();
return null; //cuase
}
}
}
public static <T> Class<T> typeTheObject(Class<? super T> type){
return (Class<T>)type;
}
public static <T> T typeTheObject(Class<T> type, Object object){
if(object.getClass().isAssignableFrom(type)){
return (T)object;
}
return (T)object;
}
public static <T> Class<T> typeTheObject(Object object){
return (Class<T>)((T)object).getClass();
}
Though one big drawback is paramaterized types. But those can be solved using a guice type literal.
(new TypeLiteral<List<String>(){}).getRawType();
since its annon the type holds during runtime.
I don't understand why this confuses the compiler. I'm using the generic type T to hold an object that's not related to the put and get methods. I always thought GenericClass and GenericClass<Object> were functionally identical, but I must be mistaken. When compiling the DoesntWork class I get incompatible types - required: String - found: Object. The Works class does what I expect. What's going on here?
public class GenericClass<T> {
public <V> void put(Class<V> key, V value) {
// put into map
}
public <V> V get(Class<V> key) {
// get from map
return null;
}
public static class DoesntWork {
public DoesntWork() {
GenericClass genericClass = new GenericClass();
String s = genericClass.get(String.class);
}
}
public static class Works {
public Works() {
GenericClass<Object> genericClass = new GenericClass<Object>();
String s = genericClass.get(String.class);
}
}
}
The thing about how raw types work -- generic types that you've left out the arguments for -- is that all generics for them and their methods are erased as well. So for a raw GenericClass, the get and put methods also lose their generics.
This is because when you work with a generic class without the extra type information you work with what is sometimes called the degenerate form of the class. The degenerate form has ALL generic type information removed.
Essentially - your class becomes something like:
public class GenericClass {
public void put(Class key, Object value) {
// put into map
}
public Object get(Class key) {
// get from map
return null;
}
...
}
The compiler response you are seeing is therefore expected behaviour.
It's mentioned in a Java Puzzlers.