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.
Related
Why is the unsafe cast (T) needed in this generic interface? If T is comparable to itself, i.e. implements ExtendedComparable<super of T> which means also ExtendedComparable<T>, then why does type erasure require ExtendedComparable<T> to be cast to T?
/* #param <T> T must be comparable to itself or any of its superclass
* (comparables are consumers, thus acc. to the PECS principle
* = producer-extends,consumer-super we use the bounded wildcard type "super")
*/
public interface ExtendedComparable<T extends ExtendedComparable<? super T>> {
Comparator<? super T> getComparator();
default boolean greaterThen(T toCompare) {
return getComparator().compare((T) this, toCompare) > 0;
}
}
Because there is no guarantee that this is actually an instance of class T or even extend it.
For example consider this:
public class T0 implements ExtendComparable<T0> {...}
public class T1 implements ExtendComparable<T0> {...}
In T0 complies fine as it complies with the bound: T0 extends ExtendComparable<T0> and T0 is super of T0. In this case this is an instance of T0 here so you are fine; the cast (T)this (thus (T0)this) makes sense.
With T1 the declaration is correct also because the bound is applied to T0 no T1, T is substituted T0. However this is T1 and T1 is not super nor a child of T0. Yes, both implement
ExtendedCompatible<T0>, but you cannot cast between siblings. For example Integer and Double extend Number but (Integer) new Double(0.0) fails.
So too does the cast (T) translated to (T0) fail.
The assumption you are making is that T is going to be set to the same as the class that is been declared and currently there is no way to force those semantics. I hope this will change at some point in future releases of the Java language but perhaps there is actual reason why the Java language "task force" are avoiding to do so.
There is a way to avoid the cast altogether but is better when you make ExtendedCompatible an abstract class rather than an interface.
You can declare a final field of type T which value would be set by a protected constructor by extending class which in turn must pass this as its value:
public abstract class ExtendedCompatible<T extends ExtendedCompatible<? super T>> {
private final T thiz;
protected ExtendedCompatible(final T thiz) {
if (this != thiz) throw new IllegalArgumentException("you must pass yourself");
this.thiz = thiz;
}
...
public class MyExtendedCompatible extends ExtendedCompatible<MyExtendedCompatible> {
public MyExtendedCompatible() {
super(this);
}
}
The price you pay is the extra memory consumption of having a silly reference to itself and the added code/CPU burden of passing this to the parent constructor.
Another would be to declare an abstract method to get the T (this):
// Parent abstract class:
protected abstract T getThiz();
// Child class... for each class:
protected MyChildClass getThiz() { return this; }
Thanks. Valentin is right. Even if both types implement the same interface, this does not and should not make the cast between them to work. And yes, there is no mechanism in Java to enforce passing in T the same class as the class being declared.
Imagine the following code:
final Set<ConstraintViolation<User>> violations = validator.validate(user);
while User is defined as
public class User extends SuperModel {
Now, I want to pass the violations to a method like
private void checkViolations(final Set<ConstraintViolation<? extends SuperModel>> violations) {
// do something
}
So I want this helper method to be generic as I do not need to know that the ConstraintViolations in the Set are of the type User.
But the compiler says:
Error:(46, 25) java: incompatible types: java.util.Set<javax.validation.ConstraintViolation<my.package.model.User>> cannot be converted to java.util.Set<javax.validation.ConstraintViolation<? extends my.package.model.SuperModel>>
I do not really understand why this is impossible, because every User is a SuperModel. This should work IMO.
In contrast, here is something that works:
private void checkViolation(ConstraintViolation<? extends SuperModel> violation) {
// do something
}
private void otherMethod() {
[...]
final Set<ConstraintViolation<User>> violations = mValidator.validate(user);
for (ConstraintViolation<User> violation : violations) {
checkViolation(violation);
}
}
Does my first example not work because there are two "levels" of generics involved?
The issue is that ? extends SuperModel means anything that extends SuperModel can be used.
If you want to pass Set<ConstraintViolation<User>> to the wildcard it will not be accepted, because you break the anything extends SuperModel with User only.
Keep in mind that you can pass anything with the constraint ? extends SuperModel. So the Set might consist of a User, a SuperModel or something entirely different which extends SuperModel.
private void checkViolation(final Set<ConstraintViolation<? extends SuperModel>> violation) {
// you know that the Set consists of any object extending SuperModel.
}
Use this instead
private <T extends SuperModel> void checkViolations(final Set<ConstraintViolation<T>> violations) {
// you know the Set consists only of object of type T
}
By declaring the type T you ensure that ever item in the list is of type T. So the Set can only consist of User, SuperModel or any other type extending SuperModel.
Set<ConstraintViolation<User>> is not a subtype of Set<ConstraintViolation<? extends SuperModel>> even though ConstraintViolation<User> is a subtype of ConstraintViolation<? extends SuperModel>, for the same reason that Set<String> is not a subtype of Set<Object> even though String is a subtype of Object.
Basically, when a generic type is parameterized by type and not a wildcard, the parameter must match exactly. Foo<A> is only compatible with Foo<B> if A = B exactly. If you want to be polymorphic on the type parameter, the generic type must be parameterized by a wildcard, e.g. Foo<A> is a subtype of Foo<? extends B> if A is a subtype of B. (Note that in the case of Set<ConstraintViolation<? extends SuperModel>>, Set is parameterized by the type, not a wildcard. There is a wildcard somewhere inside the type ConstraintViolation<? extends SuperModel>, but Set only cares about the top level.)
If you want to be able to accept Set<ConstraintViolation<X>> and Set<ConstraintViolation<Y>>, etc., one way to do it is with a wildcard at the top level:
private void checkViolations(final Set<? extends ConstraintViolation<? extends SuperModel>> violations) {
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.
This is an existing interface:
public interface MyInterface<T, U> {
public T foo(U u);
}
I want to implement this interface under the assumption that T and U are the same type. I thought maybe I could leave the type parameters in as they are, and then as long as I only ever instantiate this particular implementation with two of the same type, that it might work:
public class MyOuterClass<A> {
public class MyClass<T, U> implements MyInterface<T, U> {
#Override
public T foo(U u) {
return u; //error here
}
//even though in the only instantiation of MyClass, T and U are the same
private MyClass<A, A> myInstance = new MyClass<A, A>();
}
But, perhaps unsurprisingly, this doesn't work, as types T and U are incompatible.
So then I thought maybe I could change MyClass to specify that its types would always be the same, by changing it to something like MyClass<A, A> implements MyInterface<A, A> or similar, but I get errors saying that T is already defined.
Is there a way to implement MyClass so that its two types will be the same?
(I'm more of a C++ guy than Java, so sorry if I'm missing something fundamental about Java's generic's here.)
Your myclass needs to look like this:
public class MyClass<T> implements MyInterface<T, T> {
#Override
public T foo(T in) {
return in;
}
}
Let's review what your suggested class definition does:
public class MyClass<T, U> implements MyInterface<T, U>
In this code, T and U do two things each:
in the first occurance they define a type variable of your MyClass class
in the second occurance they specify the concrete type of the MyInterface class
Since inside the body of your class T and U are unbounded type variables (i.e. nothing is known about the actual types), they are assumed to be incompatible.
By having only a single type variable in your MyClass you make your assumption explicit: there's only a single type, and I'm using it for both types of the interface.
Last but not least: remember that the compilation of a type is complete once the source is fully handled. In other words: contrary to what C++ does, "instantiation" of a generic type ("template type" or similar in C++; Sorry for my rusty terminology) does not handle. MyClass<Foo> and MyClass<Bar> are the same type, as far as the JVM is concerned (only the compiler actually distinguishes them).
Define a single type parameter for MyClass:
class MyOuterClass<A> {
public class MyClass<T> implements MyInterface<T, T> {
public T foo(T u) {
return u;
}
}
// Need only one 'A' here.
private MyClass<A> myInstance = new MyClass<A>();
}
When you say
public class MyClass<T> implements MyInterface<T, T> {
... you are defining one generic variable for MyClass and you are saying that it fulfills both the roles T and U in MyInterface.
I've always thought the following should work. I get an object which I know is a Class<X> where X extends some class Xyz. In order to make it type-safe I wanted to use Class.asSubclass like in the following method:
private Class<? extends Xyz> castToXyzClass(Object o) {
final Class<?> resultClass = (Class<?>) o;
final Class<? extends Xyz> result = Xyz.class.asSubclass(resultClass);
return result;
}
However, in Eclipse it doesn't work, the only solution I see is an unchecked cast. I'd bet the above code must work, I've used something like this already... no idea what's wrong here.
asSubclass() operates on the object it's called on, not on its parameter - not what one is used to, but it reads quite well. You just have to do this:
final Class<? extends Xyz> result = resultClass.asSubclass(Xyz.class);
The asSubclass is a bit of a confusing name because you're not obtaining a Class object representing the subclass, you're obtaining the same class object that is retyped to reflect that it is a subclass of some parent class.
In fact, this method is fairly single purpose (and I think you've found it)...it's to take a raw or wildcarded class and get a better type parameter with a runtime check. It's not needed when you don't have gaps in your type information:
class Super {}
class Sub extends Super {}
//...
Class<Sub> subClass = Sub.class;
//both work, but the latter introduces a redundant runtime check
Class<? extends Super> subOfSuper1 = subClass;
Class<? extends Super> subOfSuper2 = subClass.asSubclass(Super.class);