Comme tout le monde le sait, vous pouvez avoir une classe générique dans Java en utilisant des arguments de type:
class Foo<T> {
T tee;
Foo(T tee) {
this.tee = tee;
}
}
Mais vous pouvez également avoir des constructeurs génériques , ce qui signifie des constructeurs qui reçoivent explicitement leurs propres arguments de type générique, par exemple:
class Bar {
<U> Bar(U you) {
// Why!?
}
}
J'ai du mal à comprendre le cas d'utilisation. Qu'est-ce que cette fonctionnalité me permet de faire?
Qu'est-ce que cette fonctionnalité me permet de faire?
Il y a au moins trois il vous permet de faire deux choses que vous ne pourriez pas faire autrement:
exprimer des relations entre les types d'arguments, par exemple:
class Bar {
<T> Bar(T object, Class<T> type) {
// 'type' must represent a class to which 'object' is assignable,
// albeit not necessarily 'object''s exact class.
// ...
}
}
<retiré>
Comme @Lino l'a observé en premier, il vous permet d'exprimer que les arguments doivent être compatibles avec une combinaison de deux types ou plus non liés (ce qui peut avoir du sens lorsque tous mais au plus un sont des types d'interface). Voir la réponse de Lino pour un exemple.
Le cas d'utilisation auquel je pense pourrait être que certains veulent un objet qui hérite de 2 types. Par exemple. implémente 2 interfaces
:
public class Foo {
public <T extends Bar & Baz> Foo(T barAndBaz){
barAndBaz.barMethod();
barAndBaz.bazMethod();
}
}
Bien que je ne l'ai jamais utilisé en production.
Il est clair dans l'exemple que vous avez fourni que U
ne joue aucun rôle dans le constructeur de la classe, car il devient effectivement un Object
au moment de l'exécution:
class Bar {
<U> Bar(U you) {
// Why!?
}
}
Mais disons que je voulais que mon constructeur accepte uniquement les types qui étendent une autre classe ou interface, comme indiqué ci-dessous:
class Foo<T extends Baz> {
<U extends Bar> Foo(U u) {
// I must be a Bar!
}
}
Notez que la classe a déjà un type générique différent en cours d'utilisation; cela vous permet d'utiliser un type générique distinct et sans rapport avec la définition de classe.
Certes, je n'ai jamais utilisé quelque chose comme ça, et je ne l'ai jamais vu en utilisation, mais c'est possible!
En fait, ce constructeur
class Bar {
<U> Bar(U you) {
// Why!?
}
}
est comme un méthode générique . Cela aurait beaucoup plus de sens si vous aviez plusieurs arguments de constructeur comme celui-ci:
class Bar {
<U> Bar(U you, List<U> me) {
// Why!?
}
}
Ensuite, vous pourriez appliquer la contrainte, qu'ils ont le même temps avec le compilateur. Sans faire de U un générique pour toute la classe.
Parce que ce type générique non lié s'efface en Object
, ce serait la même chose que de passer Object
dans votre constructeur:
public class Foo {
Object o;
public Foo(Object o) {
this.o = o;
}
}
... mais comme passer un Object
vierge, à moins que vous ne fassiez quelque chose intelligent, cela a peu de valeur pratique.
Vous voyez des avantages et des gains si vous passez à la place bound génériques, ce qui signifie que vous pouvez réellement avoir des garanties sur le type qui vous intéresse.
public class Foo<T extends Collection<?>> {
T collection;
public Foo(T collection) {
this.collection = collection;
}
}
En fait, il s'agit plus de flexibilité que de révolutionnaire. Si vous voulez que la flexibilité passe dans une catégorie spécifique de types, alors vous avez le pouvoir de le faire ici. Si vous ne le faites pas, alors il n'y a rien de mal avec les classes standard. C'est simplement ici pour votre commodité, et puisque l'effacement de type est toujours une chose, un générique non lié est le même (et a la même utilité) que le passage de Object
.