Parce qu'il n'y a aucune garantie que this
est en fait une instance de classe T
ou étendent même pas.
Par exemple, considérons ceci:
public class T0 implements ExtendComparable<T0> {...}
public class T1 implements ExtendComparable<T0> {...}
En T0
respecte bien comme elle est conforme à la limite: T0 extends ExtendComparable<T0>
et T0 est super de T0. Dans ce cas this
est une instance de T0
ici donc vous allez bien; la distribution (T)this
(donc (T0)this
) a du sens.
Avec T1
la déclaration est correcte aussi parce que la limite est appliquée à T0
pas T1
, T
est substitué T0
. Cependant this
est T1
et T1
n'est pas super ni un enfant de T0
. Oui, les deux implémentent ExtendedCompatible<T0>
, mais vous ne pouvez pas convertir entre frères et sœurs. Par exemple Integer et Double extend Number mais (Integer) new Double(0.0)
échoue. De même, le cast (T)
converti en (T0)
échoue.
L'hypothèse que vous faites est que T
va être définie à la même que la classe qui a été déclarée et actuellement il n'y a aucun moyen de forcer ces sémantiques. J'espère que cela changera à un moment donné dans les futures versions du langage Java, mais il y a peut-être une raison réelle pour laquelle le "task force" du langage Java évite de le faire.
Il existe un moyen d'éviter totalement la conversion, mais il est préférable de créer ExtendedCompatible
une classe abstraite plutôt qu'une interface.
Vous pouvez déclarer un dernier champ de type T
dont la valeur serait fixée par un constructeur protégé par classe d'extension qui à son tour doit passer this
sa valeur:
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);
}
}
Le prix que vous payez est la mémoire supplémentaire la consommation d'avoir une référence idiote à lui-même et la charge de code/CPU supplémentaire de passer this
au constructeur parent.
Une autre solution serait de déclarer une méthode abstraite pour obtenir le T
(ce):
// Parent abstract class:
protected abstract T getThiz();
// Child class... for each class:
protected MyChildClass getThiz() { return this; }
Ce message peut être de votre aide: https://stackoverflow.com/a/25783345/4867374 –