Unchecked typecasting of generic class? - java

I am trying to write a method where I can convert from a string to an enum object at runtime, for a generic enum. I have a method signature:
public static <T extends Enum<T>> Enum<T> foo(String string, Class<T> clazz)
However, I am calling it from a class whose generic type parameter does not explicitly extend Enum. i.e.
class bar<X> {
private Class<X> clazz;
if (XIsAnEnum()) {
foo(string, clazz)
}
}
This does not compile because even though I know, from the logic of XIsAnEnum, that X extends Enum<X>, I don't explicitly state this in the generic type parameter definition, so it is not a valid argument.
Is there a way to do an unchecked cast from Class<X> to Class<X extends Enum<X>>, or will I have to make a new class bar2<X extends Enum<X>> specifically for when I want to use enums?

You can use Class#asSubclass(Class) to do the cast for you, like
foo("value", clazz.asSubclass(Enum.class));
This involves an actual verification that clazz is referring to a Class that is a subclass of Enum.
You're throwing out all the generic verification here though.

Related

Generics - how to define a method that takes only classes that implement a particular interface

I've got a method that currently looks like this:
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
At the moment it can take any type of class. I want to limit that (at compile time) so that it can only allow a limited set of classes.
So I'm thinking I can define an interface like this:
public interface GettableBean<T> {}
and change the code to this (which looks horrid with a cast):
public static <T> T getBean(GettableBean<T> clazz) {
return (T) applicationContext.getBean(clazz.getClass());
}
and what's more when I try and call it using a class that implements GettableBean
public class MyClass implements GettableBean<MyClass>
I get a compile error:
Error:(119, 27) java: method getBean in class BeanLocator cannot be applied to given types;
required: GettableBean
found: java.lang.Class
reason: cannot infer type-variable(s) T
(argument mismatch; java.lang.Class cannot be converted to GettableBean)
What am I doing wrong here ?
I don't know why you think GettableBean needs to be parameterized. It's just a marker interface.
Remove the redundant generic type parameter and add a constraint to the getBean method.
public interface GettableBean {}
public class MyClass implements GettableBean {}
public static <T extends GettableBean> T getBean(Class<T> clazz) {}
The name of this parameter should have been your clue that what you were trying was not really the right approach:
public static <T> T getBean (GettableBean<T> clazz)
A GettableBean is not a class. It's an instance. It should be called gettableBean. When you rename that, it becomes more clear that you're passing an instance only pretty much immediately disregard everything about the instance.
ONE:
The error message originates in the return statement: clazz.getClass() returns a Class<?>. You try to cast it to T. To successfully do so Class would have to be an implementation or extension of T. You could declare that by
public static <T super Class<?>> T getBean(...) ...
But that's most likely not what you want.
TWO
Actually from my understanding what you want is to allow only Class parameters for implementations of GettableBean<?>. That should look like this:
public static <T> T getBean(Class<? extends GettableBean<T>> clazz) {
return applicationContext.getBean(clazz);
}

Instantiate and apply generic method with unknown generic type - Java

I need to instantiate an object with an unknown generic type and then apply a generic method to it. Here is where I stand :
public static void main(String[] args)
{
BarIf bar = newRandomBarImpl();
Foo foo1 = newFooBar(bar.getClass()); // warning
Foo<?> foo2 = newFooBar(bar.getClass()); // error
Foo<? extends BarIf> foo3 = newFooBar(bar.getClass()); // error
foo1.doSomething(bar); // warning
foo2.doSomething(bar); // error
foo3.doSomething(bar); // error
}
static <T extends FooIf<S>, S extends BarIf> T newFooBar(Class<S> barClass){}
static <T extends BarIf> T newRandomBarImpl(){}
interface FooIf<T extends BarIf>
{
public void doSomething(T t);
}
interface BarIf{}
class Foo<T extends BarIf> implements FooIf<T>
{
public void doSomething(T t){}
}
The strange thing is that for foo2 and foo3, the newFooBar() method returns FooIf rather than Foo. I guess the type inference is messy. But I can't pass the method generic parameters since I don't know the Bar type.
What I would need is Foo<bar.getClass()>. Is there a way to do it?
I tried using TypeToken but I end up with a T type rather than the actual Bar type. Any chance using that?
First of all, a declaration like
static <T extends BarIf> T newRandomBarImpl(){}
is nonsense. It basically says “whatever the caller substitutes for T, the method will return it”. In other words, you can write
ArbitraryTypeExtendingBarIf x=newRandomBarImpl();
without getting a compiler warning. Obviously, that can’t work. newRandomBarImpl() doesn’t know anything about ArbitraryTypeExtendingBarIf. The method name suggests that you actually want to express that newRandomBarImpl() can return an arbitrary implementation of BarIf, but that’s an unnecessary use of Generics,
BarIf newRandomBarImpl(){}
already expresses that this method can return an arbitrary subtype of BarIf. In fact, since BarIf is an abstract type, this method must return a subtype of BarIf and it’s nowhere specified which one it will be.
The same applies to the declaration
static <T extends FooIf<S>, S extends BarIf> T newFooBar(Class<S> barClass){}
It also claims that the caller can choose which implementation of FooIf the method will return. The correct declaration would be
static <S extends BarIf> FooIf<S> newFooBar(Class<S> barClass){}
as the method decides which implementation of FooIf it will return, not the caller.
Regarding your other attempts to deal with FooIf, you can’t work this way using a type parametrized with a wildcard, nor can you fix it using Reflection. But you can write generic code using a type parameter:
public static void main(String[] args)
{
BarIf bar = newRandomBarImpl();
performTheAction(bar.getClass(), bar);
}
static <T extends BarIf> void performTheAction(Class<T> cl, BarIf obj) {
FooIf<T> foo=newFooBar(cl);
foo.doSomething(cl.cast(obj));
}
static <S extends BarIf> FooIf<S> newFooBar(Class<S> barClass){}
static BarIf newRandomBarImpl(){}
interface FooIf<T extends BarIf> {
public void doSomething(T t);
}
interface BarIf{}
The method performTheAction is generic, in other words, works with an unknown type expressed as type parameter T. This method can be invoked with an unknown type ? extends BarIf as demonstrated in the main method.
However, keep in mind, that every reference to a type X implies that the referred object might have a subtype of X without the need to worry about it.
You can simply use the base class BarIf here, regardless of which actual subtype of BarIf the object has:
BarIf bar = newRandomBarImpl();
FooIf<BarIf> foo=newFooBar(BarIf.class);
foo.doSomething(bar);
Note that when you want to use methods of the actual implementation type Foo, not specified in the interface, you will have to cast the FooIf to Foo. You can cast a FooIf<BarIf> to Foo<BarIf> without a warning as the generic type conversion is correct if Foo<X> implements FooIf<X>.
However, it can fail at runtime as the method newFooBar is not required to return an instance of Foo rather than any other implementation of FooIf. That’s why an explicit type cast is the only correct solution as it documents that there is an assumption made about the actual runtime type of an object. All other attempts will generate at least one compiler warning somewhere.

How do I use Realm with generic type?

I have a generic method and would like to retrieve objects using the generic type. This is my method:
public static <T extends RealmObject & IndentifierModel> void storeNewData() {
...
T item = realm.where(Class<T>) // Not compiling (Expression not expected)
.equalTo("ID", data.get(i).getID())
.findFirst();
}
The above isn't working for realm.where(Class<T>). How do I pass in my generic type to Realm?
You have to supply the generic parameter like so:
public static <T extends RealmObject & IndentifierModel> void storeNewData(Class<T> clazz) {
T item = realm.where(clazz)
.equalTo("ID", 123)
.findFirst();
}
Class<T> is not valid, since that's like saying realm.where(Class<List<String>>) or realm.where(Class<String>). What you need is an actual Class<T> instance. But you cannot use T.class either since T is not available at runtime due to type-erasure. At runtime, the method basically needs a Class<T> instance to work properly. Since you cannot get that from T, you will have to explicitly supply an argument of type Class<T>.

Cannot convert from not generic subclass to generic superclass

I'm trying to get interface instance depending on what type T is. Place or something else that extends BaseDictionary.
public static <T extends BaseDictionary> IDictionaryDataSource<T> getEntityDataSourceInstance(Class<T> clazz,
Context cxt) {
if (Place.class.isAssignableFrom(clazz)) return (IDictionaryDataSource<T>) new PlaceDataSource(cxt);
//here some other types, same lines
return null;
}
public abstract class BaseDictionary{}
public class Place extends BaseDictionary{}
public interface IDictionaryDataSource<T extends BaseDictionary>{}
public abstract class BaseDictionaryDataSource<T extends BaseDictionary> implements IDictionaryDataSource<T>{}
public class PlaceDataSource extends BaseDictionaryDataSource<Place>{}
And I get
Type mismatch: cannot convert from PlaceDataSource to IDictionaryDataSource<T>
or
Type safety: Unchecked cast from PlaceDataSource to IDictionaryDataSource<T>
if I cast it like above.
Can you explain why do compile error and warning occur?
It will be called here
public static <T extends BaseDictionary> DictionaryElementPickerFragment<T> newInstance(Class<T> clazz, Context cxt){
//somecode here
fragment.setDataSource(DictUtils.getEntityDataSourceInstance(clazz, cxt));
}
I've tried to find answer here and in google but no success.I would appreciate any help.
Now I think like this
There is no helper method to work around the problem, because the code is fundamentally wrong.
Thanks in advance.
This more concrete example illustrates your problem which is one of type parameter variance.
void foo(List<String> stringList, Integer anInteger) {
List<Object> objList = (List<Object>) stringList;
objList.add(anInteger); // Violation -- adding an object to a list of strings
// could cause someone getting a "String" to get an
// Integer stead
}
so a List<String> is not a List<Object> although it is a List<? extends Object>.
In your specific instance you can't cast
PlaceDataSource to IDictionaryDataSource<T>
PlaceDataSource is an IDictionaryDataSource<Place>, but the only thing we know about <T> is that it extends BaseDictionary which is a super-class of BaseDictionary.
So you can cast a PlaceDataSource to
an IDictionaryDataSource<Place> or to
an IDictionaryDataSource<? super Place> or to
an IDictionaryDataSource<? extends BaseDictionary>
but not to an IDictionaryDataSource<T> because T is not guaranteed to be Place, and doing so would lead to a mismatch between the actual type parameter Place and the formal type parameter T.
That is not secure implementation and it is dangerous to cast because (Place.class.isAssignableFrom(clazz) saves you here):
T is a generic type and you're replacing it with definite Place.
What if I have
public class Home extends BaseDictionary{}
public class HomeDataSource extends BaseDictionaryDataSource<Home>{}
And then I invoke getEntityDataSourceInstance with Home class but get PlaceDataSource which cannot be cast to HomeDataSource (IDictionaryDataSource) which I expect. So I'll end up having ClassCastException.
It looks like getEntityDataSourceInstance should be an instance method in the BaseDictionary class, not a static method.
A subclass will know which type of DictionaryDataSource to create.
Try #SuppressWarnings("unchecked")

Generics: Compiler seems incapable of recognizing that the type passed to an argument is the same as what is returned - why?

Let's say I have several POJOs which all extend a common supertype, BaseObject.
I have a GenericDao which is declared as public interface GenericDao<T>.
For each type-specific DAO, I have an interface which extends the generic type and restricts it to a concrete type (public interface UserDao extends GenericDao<User>) and then an implementation of the type-specific DAO.
In a class that attempts to use a number of GenericDao implementations, I have a method that looks like
public <T extends BaseObject> long create(T object) {
return getDao(object.getClass()).save(object);
}
If I implement getDao() so that it's parameter is a Class object, such as
private <T extends BaseObject> GenericDao<T> getDao(Class<T> clazz) { ... }
Then the call to getDao(object.getClass() in the create() method fails to compile - the compiler appears to interpret the return type of getDao() as
GenericDao<? extends BaseContractObject>
rather than recognizing that getDao(Class<T>) is going to return me a GenericDao of the same type T.
Can someone explain why this is? I understand that repeated appearances of the same type bound or wildcard don't necessary refer to the same type; however it seems like the compiler should recognize from the signature of getDao(Class<T>) that the T passed in should be the same T returned (but obviously it isn't capable of recognizing this, the why is the part I fail to grasp).
If I instead define getDao's signature to be
private <T extends BaseContractObject> GenericDao<T> getDao(T obj) { ... }
Then there is no issue in compiling a create() implementation which looks like
public <T extends BaseContractObject> long create(T object) {
return getDao(object).save(object);
}
So why is the compiler capable of recognizing in this case that the T argument passed to getDao(T) is the same T in the return type, whereas it couldn't recognize this when the argument was Class<T>?
The expression object.getClass(), where object is of type T extends BaseObject, returns a Class<? extends BaseObject>, not a Class<T> as one might expect. So, getDao() is returning a DAO of the same type it receives; it's just not receiving the expected type.
This is a classic type erasure issue. getClass() has the following signature:
public final native Class<? extends Object> getClass();
If you have a String and do a getClass() on it, the class you get is Class<? extends String>. The javadocs read:
* #return The <code>java.lang.Class</code> object that represents
* the runtime class of the object. The result is of type
* {#code Class<? extends X>} where X is the
* erasure of the static type of the expression on which
* <code>getClass</code> is called.
You will need to force the following cast to get it to work:
#SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>)object.getClass();
return getDao(clazz).save(object);
That works for me.
I think this should explain why the constraint is not doing what you expect:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#FAQ206

Categories

Resources