IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Voir le flux RSS

Grimly

Le type de retour "This" en Java

Noter ce billet
par , 12/11/2015 à 17h51 (979 Affichages)
Bonjour,

Pendant certains de mes développements en Java j'ai bien souvent pesté contre l'api java.nio et ses classes Buffer qui ne permettent jamais complètement un chaînage des commandes (ce qui est proposé reste très intéressant néanmoins).
J'ai tenté pas mal de fois de contourner ce problème jusqu'à trouver une astuce avec les types génériques.

Je vais décrire mon raisonnement ci-dessous avec une classe "Buffer" et une méthode "position" comme ce que propose java.nio.Buffer

La première étape est de choisir un nom pour le type générique. On prendra ici "This".
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
public class Buffer<This> {
    public This position(int position) { return null; }
}

Ensuite il faut définir ce This de façon à ce que le chaînage puisse se réaliser. On le fera alors étendre la classe Buffer elle même.
Afin de terminer la définition, le type This est réutilisé dans la classe parent de This.
On pourra alors terminer l'implémentation avec un retour de "this" au lieu de "null".
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
public class Buffer<This extends Buffer<This>> {
    public This position(int position) { return this; }
}

Dès lors on peut très facilement chaîner nos appels de méthodes.

Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
Buffer buffer = new Buffer();
buffer.position(0).position(5);

Le problème maintenant est sur la déclaration du composant générique à l'utilisation.
Ici, un IDE consciencieux vous dira que le type générique n'est pas défini et qu'il faut le préciser.

Le type Object ne réponds pas au critère car il n'hérite pas de Buffer.
Le type Buffer est correct (Buffer<Buffer>) mais on retrouve exactement le même problème sur la suite de la définition
En java le caractère "?" permet de définir un type générique non défini. Ici il convient parfaitement car bien que nous ne précisons pas de type précis, le compilateur sais au sein de la classe Buffer que "This" est au moins un type "Buffer<This>".

Le code client sera alors le suivant :
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
Buffer<?> buffer = new Buffer<>();
buffer.position(0).position(5);

Passons maintenant à l'héritage.
Imaginons un type ByteBuffer qui étends Buffer avec une nouvelle méthode "put".
L'astuce ici est de préciser plus finement le paramètre This pour maintenant étendre ByteBuffer et non Buffer.
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
public class ByteBuffer<This extends ByteBuffer<This>> extends Buffer<This> {
    public This put(byte b) { return this; }
}

Le code client ne change pas tellement si on garde le même type de départ, à savoir Buffer :
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
Buffer<?> buffer = new ByteBuffer<>();
buffer.position(0).position(5);
On remarquera que comme nous avons déclaré la variable "buffer" comme étant un "Buffer", nous n'avons pas accès à la méthode "put" et nous gardons la vision d'un objet Buffer.

Lors de l'utilisation du type ByteBuffer, nous pouvons chaîner les opérations de la classe ByteBuffer avec les opérations de la classe Buffer comme suit :
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
ByteBuffer<?> buffer = new ByteBuffer<>();
buffer.position(0).put((byte)5).position(5).put((byte)250);

Imaginons maintenant un nouveau type de Buffer qui prends en charge n'importe quel type, paramétré, d'élément, ObjectBuffer.
Sa déclaration sera fortement semblable que pour ByteBuffer :
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
public class ObjectBuffer<Element, This extends ObjectBuffer<Element, This>> extends Buffer<This> {
    public This put(Element e) { return this; }
}
Il est à noter ici que les autres types paramétrés sont répétés systématiquement. C'est ici que se trouve le coût de notre "This" dans la déclaration de notre classe.

Le code client devra à nouveau omettre le type "This" qu'il ne nous intéresse pas de définir :
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
ObjectBuffer<String, ?> buffer = new ObjectBuffer<>();
buffer.position(0).put("zero").position(5).put("cinq");

Cette notation permet aussi d'effectuer du prototypage au lieu d'un objet mutable. On peut imaginer de même qu'au lieu de l'instruction "return this;", l'objet retourné soit une copie.



Beaucoup de réflexion pour ce sucre syntaxique. Mais ce n'est pas la première fois que je relevais ce problème sur l'API java.nio et je pense que d'autres l'ont aussi noté.

Envoyer le billet « Le type de retour "This" en Java » dans le blog Viadeo Envoyer le billet « Le type de retour "This" en Java » dans le blog Twitter Envoyer le billet « Le type de retour "This" en Java » dans le blog Google Envoyer le billet « Le type de retour "This" en Java » dans le blog Facebook Envoyer le billet « Le type de retour "This" en Java » dans le blog Digg Envoyer le billet « Le type de retour "This" en Java » dans le blog Delicious Envoyer le billet « Le type de retour "This" en Java » dans le blog MySpace Envoyer le billet « Le type de retour "This" en Java » dans le blog Yahoo

Mis à jour 13/11/2015 à 10h15 par Grimly

Catégories
Java

Commentaires