Misunderstanding Java generics - java

I am facing a confusing issue in my code.
I have one method which signature is
public <T extends Measure> void sendNewMeasure(Class<T> type, T measure);
In another class, I have this method, which calls the previous one :
public <T extends Measure> void onNewMeasure(NewMeasureEvent<T> event) {
T measure = event.getMeasure();
APIInterface.getInstance().sendNewMeasure(measure.getClass(), measure);
}
The error I get is Wrong argument type, found 'T', required <? extends com.blablabla.Measure> but I don't get why, as the measure object is of type T which extends Measure.
Is there any way to fix this, and most importantly, why is it not working ?
Thanks !
EDIT :
This is the implementation of the sendNewMeasure method :
public <T extends Measure> void sendNewMeasure(Class<T> type, T measure) {
String measureType = measure.getJSONMeasureTypeName();
List<T> measures = T.find(type, measure.timestamp, true, false);
measures.add(measure);
sendMeasures(siteId, sensorId, measureType, measures);
}
EDIT 2 : And this is the find method signature, the one I cannot change:
public static <T> List<T> find(Class<T> type, int timestamp, boolean includeStart, boolean inclueEnd);

The type of .getClass() is not what you think it is. .getClass() returns Class<? extends |X|>, where |X| is the erasure of the static type of the expression it's called on. In this case, measure has static type T, which has erasure Measure, so measure.getClass() has type Class<? extends Measure>, i.e. the type parameter is an unknown subtype of Measure, and .sendNewMeasure(measure.getClass(), measure) there is no way the compiler can guarantee that measure (of type T) is an instance of this unknown type.
Basically, the problem is that .getClass() loses type information. Its return type is not directly linked to the type of the thing it is called on, because the Java type system cannot express the concept of "the real runtime type of the thing it's called on". However, intuitively, you know that the call to the method with the current signature .sendNewMeasure(measure.getClass(), measure) is type-safe, because the type of measure.getClass() should really be Class<U> where U is the real runtime class of measure, and you know that measure is obviously an instance of that same type U, so there exists some type argument, this U (which is not necessarily the same as T) for which the call to .sendNewMeasure() type-checks, but the question is how to convince the compiler of this without using unchecked operations.
The problem is that the type returned by measure.getClass() is not sufficiently linked to the type of measure. One way to re-link them is to use the class to cast the object to its type (which will always succeed), using the class's method .cast(). But it doesn't help to do this with an expression of type Class<? extends Measure>, because the resulting of .cast() is ? extends Measure which just degrades to Measure, so we still don't have a link between the two types. We need a real name for the type, not a wildcard, for us to maintain this link. The way to turn a wildcard into a named type is capture, which requires passing it into a generic method:
public <T extends Measure> void onNewMeasure(NewMeasureEvent<T> event) {
T measure = event.getMeasure();
helper(measure.getClass(), measure);
}
private <U extends Measure> void helper(Class<U> clazz, Measure measure) {
U castedMeasure = clazz.cast(measure);
APIInterface.getInstance().sendNewMeasure(clazz, castedMeasure);
}

From Java API for
Class<?> getClass()
[...]
The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.
So the result of measure.getClass() is not Class<T> but Class<? extends T>
Your signature should be:
public <T extends Measure> void sendNewMeasure(Class<? extends T> type, T measure);

Related

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.

Why does Guava's TypeToken<T>.getRawType() return Class<? super T> instead of Class<T>

From Effective Java Second Edition, Item 28 : "Do not use wildcard types as return types. Rather than providing additional flexibility for your users it would force them to use wildcard types in client code."
public final Class<? super T> getRawType()
I've just been getting to grips with generic wildcards to understand the last unchecked cast warning I have in a piece of code I am writing and I don't understand why getRawType() returns a wildcard type.
class Base<T>{}
class Child<T> extends Base<T>{}
public <C> void test (TypeToken<? extends Base<C>> token) {
Class<? extends Base<C>> rawType = (Class<? extends Base<C>>) token.getRawType();
}
I have to cast token.getRawType() as it returns a
Class<? super ? extends Base<C>>
What if you have a TypeToken<ArrayList<String>> and you want to get Class<ArrayList> (that is the raw type). If it returned Class<T>, then it would return Class<ArrayList<String>> which is not Class<ArrayList> that you want.
If the generic type of "token" is a Type class (i.e. if S in
TypeToken<S>
is a java.lang.reflect.Type class), then TypeToken.getRawType() will return the raw type associated to S. It shall be a Class object, parent of S.
See the source code of TypeToken.
In some cases (like having multiple bounds), the strategy implemented won't work and having a raw type is Ok: the raw type will be Object.class
See for instance MoreTypes:
public static Class<?> getRawType(Type type) {
...
} else if (type instanceof TypeVariable) {
// we could use the variable's bounds, but that'll won't work if there are multiple.
// having a raw type that's more general than necessary is okay
return Object.class;
} else {
...
}

Java generics compile error involving Class<? extends T>

This program does not compile:
public class xx {
static class Class1<C> {
void method1(C p) {
}
}
static class Class2<T> extends Class1<Class<? extends T>> {
T object;
void method2() {
this.method1(this.object.getClass());
}
}
}
The error is:
xx.java:10: method1(java.lang.Class<? extends T>) in xx.Class1<java.lang.Class<? extends T>>
cannot be applied to (java.lang.Class<capture#215 of ? extends java.lang.Object>)
this.method1(this.object.getClass());
Why does this happen? Why does the compiler seemingly believe that object.getClass() returns Class<? extends Object> instead of Class<? extends T> ?
There is no upper bound set on T in your code, so ? extends T is really tantamount to ? extends Object. Just yesterday I played with a similar example and hit this barrier. I had
static <T> T newInstance(T o) throws Exception {
final Class<? extends T> c = o.getClass();
return c.newInstance();
}
and it complained with the same error. Consider this: the return type of Object.getClass() is Class<?> and the compiler will want to capture the ? into a concrete type. But instead, we would like not to capture the ?, but to "capture the upper bound" T -- and there is no such thing in Java's generics.
Object.getClass() is defined to return a Class<? extends |T|>, where T is the statically known type of the receiver (the object getClass() is called on). Take special note of the vertical bars, the erasure operator. The erasure of a type variable is the erasure of its leftmost bound. In your case that's the implicit bound Object. So you get back a Class<? extends Object>, not a Class<? extends T>.
Why is that?
Imagine T = List<Integer>, you could suddenly do the following without unchecked warning:
List<String> myStrings = new ArrayList<>();
List<Integer> myInts = new ArrayList<>();
List<Integer> myIntyStrings = myInts.getClass().cast(myStrings);
myIntyStrings.add(-1);
String myString = myStrings.get(0); // BANG!
But thankfully we do get a warning.. ;)
According to the documentation on getClass(), the returned object has type Class< ? extends |X| >, where |X| is the erasure of the type of the instance on which the method is called.
Therefore calling getClass() on an object of type T returns Class< ? extends Object >. We have no bound information about T in this API.
Usually APIs which use reflection on generic classes require that the client pass an additional argument of type Class< T > to the constructor or generic method in question.

Parameterized Type Parameters?

I'm trying to create library with a container that releases instances of its contained objects according to descriptors it is passed. I'd like to make it so the descriptor determines the type of the returned object, but the descriptor can specify a bounded type. How do I implement this? For example the closest I can get is:
/*Block 1 - First Attempt. Compiles, but forces user to cast*/
interface ItemDescriptor<I> {
Class<? extends I> getType();
}
interface ArchiveContainer<I, D extends ItemDescriptor<? extends I>> {
Iterable<? extends D> getDescriptors();
I getItem(D descriptor);
}
//Implementations
class ChannelItemDescriptor<I extends ByteChannel> implements ItemDescriptor<I>
{
final Class<? extends I> type;
ChannelItemDescriptor(Class<I> type) {
this.type = type;
}
#Override Class<? extends I> getType() {return type;}
}
class ChannelArchive implements ArchiveContainer<ByteChannel, ChannelItemDescriptor<? extends ByteChannel>> {
#Override ByteChannel getItem(ChannelItemDescriptor<? extends ByteChannel> descriptor) {...}
}
The above code compiles, but the problem is ChannelArchive's getItem can return SeekableByteChannels as well. The user of this library knows this at compile time (because they know the type parameter of the descriptor), so I'm trying to avoid adding a method parameter of type Class for forcing the user to explicitly cast the returned value to SeekableByteChannel when necessary. I can't figure out how to get getItem to return a specific subtype of ByteChannel without forcing the user to cast. I want to do this:
/*Block 2 - Test code*/
ChannelArchive archive = ...;
ChannelItemDescriptor<SeekableByteChannel> desc = ...;
ChannelItemDescriptor<ByteChannel> otherDesc = ...;
SeekableByteChannel sbc = archive.getItem(desc);
SeekableByteChannel sbc = archive.getItem(otherDesc); //Should fail to compile, or compile with warning
ByteChannel bc = archive.getItem(otherDesc);
I could add a Class<? extends I> parameter to each method, but the code for the method would completely ignore Class method parameter! It's only purpose would be to help the compiler infer types. I think it just obfuscates the code so much that it would be easier to just have the user use instanceof checks and casts.
I've tried this:
/*Block 3 - Failed attempt.*/
class ChannelArchive implements ArchiveContainer<ByteChannel, ChannelItemDescriptor<? extends ByteChannel>> {
//Won't compile, getItem doesn't override
#Override <II extends ByteChannel> II getItem(ChannelItemDescriptor<II> descriptor) {...}
}
but that doesn't work: ChannelArchive is not abstract and does not override abstract method getItem(ChannelItemDescriptor<? extends ByteChannel>) in ArchiveContainer. I assume this is because the second type parameter <II extends ByteChannel> has different type erasure than <? extends ByteChannel>?
I've also tried this, which compiles:
/*Block 4 - Almost specific enough*/
interface ArchiveContainer<I, D extends ItemDescriptor<? extends I>> {
Iterable<? extends D> getDescriptors();
<II extends I, DD extends ItemDescriptor<II>> II getItem(DD descriptor);
}
class ChannelArchive implements ArchiveContainer<ByteChannel, ChannelItemDescriptor<? extends ByteChannel>> {
#Override <II extends ByteChannel, DD extends ItemDescriptor<II>> II getItem(DD descriptor) {...}
}
Even though it compiles, it won't really work because I need a ChannelItemDescriptor inside that method, and the resulting cast would defeat the purpose of using the added type-safety of generics.
I don't see why I can't do it, because the right types are known at compile time. What I really need on that ArchiveContainer interface is a parameterized type parameter, like: <II extends I, DD extends D<II>>. What am I doing wrong?
NOTE: I don't actually use ByteChannel and SeekableByteChannel, but what I do use is quite similiar.
That's to ruakh, I settled on the code in block 4. In my case, its highly unlikely the user would send the wrong sublcass of ItemDescriptor in a call to a getItem, especially because the descriptors are all returned from the ArchiveContainer itself via getDescriptors!
I think this code, which is (almost?) the same as your third attempt, is as good as you're going to get:
// in ArchiveContainer:
<II extends I, DD extends ItemDescriptor<II>> II getItem(DD descriptor);
// in ChannelArchive:
public <II extends ByteChannel, DD extends ItemDescriptor<II>>
II getItem(DD descriptor)
{ ... }
Generics do offer a way to declare a type variable with two separate upper bounds:
public <T extends Foo & Bar> Foo fooBar(T t) { ... }
but apparently that's not allowed when one of the upper bounds is a type-parameter rather than a class or interface:
Type variables have an optional bound, T & I1 ... In. The bound consists of either a type variable, or a class or interface type T possibly followed by further interface types I1 , ..., In. […] It is a compile-time error if any of the types I1 ... In is a class type or type variable. [link]
(emphases mine). I don't know why this is.
But I don't think this should be a big problem. Note that, even after Map was genericized to Map<K,V>, its get method still took type Object. Naturally that method will always return null if you pass in a reference to an object that's not of type K (since such an object should never have been inserted into the map), but this doesn't harm type-safety.
I know that this is probably not what you want to hear, but even though Java generics look syntactically like C++ templates, they differ quite a bit in how they work.
Look up java type erasure in your favorite search engine.
Just because a type is known at compile-time does not, unfortunately, mean that the type is recoverable at runtime, or even during later compile phases.

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