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;
}
}
Implementing generics into your code can greatly improve its overall quality by preventing unprecedented runtime errors involving data types and typecasting.
Java Generic Classes and Subtyping We can subtype a generic class or interface by extending or implementing it. The relationship between the type parameters of one class or interface and the type parameters of another are determined by the extends and implements clauses.
Some Java compilers suppress unchecked warnings by default. Let's make sure we've enabled the compiler's option to print “unchecked” warnings before we look into this “ unchecked cast ” warning. 2. What Does the “unchecked cast” Warning Mean? The “unchecked cast” is a compile-time warning .
The java.lang.Comparable interface is declared as a generic type with a single type parameter named T.
There are examples of unchecked casts in the API. Collections.emptyList () comes to mind. And would be more helpful if it showed how to do Class.cast for a generic type (which you cannot). Or provide some insight into how Class.cast can be remotely applicable in such situations.
The Java class can only extend a Java class. As Comparable<T> is an interface in Java, we must create a custom interface that will extend the Comparable Interface. The custom class will implement the custom interface.
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; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With