IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Java Discussion :

method returning this : inherit + cast


Sujet :

Langage Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Par défaut method returning this : inherit + cast
    Bonjour,

    J'ai une classe Access ayant une méthode returnThis() qui renvoie this.
    Je voudrais que la méthode (new Access2()).returnThis() de la classe fille Access2 renvoie un Access2.
    Ci dessous un exemple GAccess avec des types génériques.

    Y'avait-il plus simple ? Puis-je éviter le Type safety warning lors du cast {return (T) this;} ?

    Merci d'avance.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
    public class demoSubCastReturnThis {
    	/* Usual class defining method returning this */
    	public static class Access {
    		public Access returnThis() {return this;}
    	}
    	/* Goal: How to make Access2().returnThis() return an Access2 */
    	public static class Access2 extends Access {		
    	}
     
    	/*  generic works, but Type Safety issues */	
    	public static class GAccess<T extends GAccess<?> > {
    	// public static class GAccess<T extends GAccess> {
    		public T returnThis() {return (T) this;}
    	}	
    	public static class GAccess2 extends GAccess<GAccess2> {		
    	}
     
    	public static void main(String... args) {
    		GAccess2 a2= new GAccess2();
    		GAccess2 returned2= a2.returnThis();
     
    		GAccess<GAccess> a= new GAccess<GAccess>();
    		GAccess<GAccess> returned= a.returnThis();
    	}
    }

  2. #2
    Membre Expert
    Avatar de eulbobo
    Homme Profil pro
    Développeur Java
    Inscrit en
    Novembre 2003
    Messages
    786
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2003
    Messages : 786
    Par défaut
    Alors, techniquement, si tu fais un new Access2, ta méthode getAccess renverra bien l'instance de Access2 (qui est aussi une classe Access puisqu'elle en hérite), mais il faudra effectivement faire un cast pour accéder aux méthodes spécifiques de Access2


    Vu la déclaration de Access : ce n'est pas possible sans redéclarer la méthode returnThis() de Access dans Access2. Et le problème c'est qu'il faudra utiliser une déclaration de variable de type Access2
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
            public static class Access2 extends Access {
    		public Access2 returnThis() {
    			return this;
    		}
    	}
    	public static void main(String... args) {
    		Access2 a2 = new Access2();
    		Access2 a3 = a2.returnThis();
     
    		Access a4 = a2.returnThis();
    		Access2 a5 = (Access2 ) a4.returnThis(); // erreur de compilation sur cette ligne sans le cast même si c'est toujours le même objet
    	}

  3. #3
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Billets dans le blog
    2
    Par défaut
    Salut,

    Quel intérêt de faire ça ?
    Au lieu de Access accessThis = access.returnThis();, tu peux faire simplement Access accessThis = access;

    Quoiqu'il en soit, tu peux faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class Truc {
     
    	public Truc getThis() {
    		return this;
    	}
     
    	public static class Truc2 extends Truc {
     
    		public Truc2 getThis() {
    			return (Truc2)super.getThis();
    		}
     
    	}
     
    	public static void main(String[] args) {
     
    		Truc truc = new Truc();
     
    		Truc2 truc2 = new Truc2();
     
    		Truc thisTruc = truc.getThis();
    		Truc2 thisTruc2 = truc2.getThis();
     
    		System.out.println(thisTruc);
    		System.out.println(thisTruc2);
     
     
    	}
     
    }
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  4. #4
    Membre Expert
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Par défaut
    Merci pour vos réponses.

    Quel intérêt de faire ça ?
    remplace returnThis par des setters. Ces setters renvoient this pour pouvoir etre chainés, par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     Truc t= (new Truc()).setField1(1).setField2("some value');
    Quoiqu'il en soit, tu peux faire :
    J'aurais aimé éviter d'alourdir la classe Truc2 - dans le cas des setters ca peut faire beaucoup de méthodes à redéfinir.
    L'héritage est censé être la pour ca non ?

    Si je change
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public static class GAccess<T extends GAccess<?> >
    en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public static class GAccess<T extends GAccess<T> >
    , je ne comprens pas pourquoi:
    - GAccess<GAccess> a= new GAccess<GAccess>(); ne marche plus (Bound mismatch)
    - return (T) this; provoque toujours un Type safety warning

  5. #5
    Membre Expert
    Avatar de eulbobo
    Homme Profil pro
    Développeur Java
    Inscrit en
    Novembre 2003
    Messages
    786
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2003
    Messages : 786
    Par défaut
    Pour respecter la norme JavaBeans, un setter doit être un void.

    Là, tu essayes de te simplifier la vie avec une méthode qui peut-être réalisée autrement : un constructeur !
    Ou un setter multivaleurs qui te permet de setter plusieurs valeurs en même temps

  6. #6
    Membre Expert
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Par défaut
    réalisée autrement : un constructeur
    Ce n'est pas la même chose (pouvoir set plusieurs fois après construction).
    un setter multivaleurs
    Oui, mais si je veux set un sous-ensemble, ce n'est pas adapté. Je ne vais pas définir un multi-setter pour toute combinaison d'attributs settable quand même, ni mettre des null ou des DontSetMe() partout. De plus, un setter multivarié devient vite illisible avec beacoup d'attributs (qui est qui dans les arguments ?)
    Les setters chaînables permettent de gagner en lisibilité et flexibilité, non ?

  7. #7
    Membre Expert
    Avatar de eulbobo
    Homme Profil pro
    Développeur Java
    Inscrit en
    Novembre 2003
    Messages
    786
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2003
    Messages : 786
    Par défaut
    Citation Envoyé par shaiHulud Voir le message
    Les setters chaînables permettent de gagner en lisibilité et flexibilité, non ?
    Franchement?
    Non.

    M'enfin, ça c'est mon point de vue, les éléments chaînés sont une fainéantise inutile quand elle n'est pas simplement dangereuse : tu rompt la norme javabeans, tu empêche le compilateur de pouvoir faire ses optimisations potentielles, ça détruit le principe de responsabilité....

    A toi de voir si tu en as BESOIN pour répondre à une problématique qui est organisationnelle et non logicielle.


    Et si tu veux pouvoir faire des set comme tu le veux, l'introspection est ton amie ! Y'a plein d'outils qui le permettent (mais c'est un peu lourd et ça réduit la lisibilité de ce que tu fais)

    Autre possibilité : faire des interfaces fonctionnelles avec à chaque fois un consumer et la valeur.

  8. #8
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 582
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 582
    Par défaut
    Citation Envoyé par shaiHulud Voir le message
    Les setters chaînables permettent de gagner en lisibilité et flexibilité, non ?
    C'est le cas avec le patron "builder." Mais les builders sont des objets séparés, pas les objets résultants qu'on va utiliser. Ils servent à construire l'objet qu'on veut obtenir, par des appels à différentes méthodes qui décrivent ce qu'on met dedans, puis un appel à la production de l'objet final.
    Lorsque des builders ont besoin d'une hiérarchie d'héritage, l'usage est en effet, soit de dupliquer toutes les méthodes, soit d'avoir un paramètre générique qui permet de renvoyer le bon type de builder à chaque fois (mais au moins c'est le builder qui a ça, pas l'objet lui-même).

    L'assurance de bien respecter le typage des choses, a quelques limites sur la liberté d'écriture, ce n'est pas neuf.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  9. #9
    Membre confirmé
    Homme Profil pro
    Architecte réseau
    Inscrit en
    Mars 2014
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Architecte réseau
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Mars 2014
    Messages : 21
    Par défaut
    @shailHulud

    Salut,

    Je comprends ton problème car j'aime beaucoup ce pattern (du typage de l'hérité) mais pour les return il faut avouer que c'est un peu compliqué.

    Ci dessous un bout de code, qui montre plein de possibilités (qui marchent ou qui ne marchent pas d'ailleurs).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    public abstract class GenericAccess<T extends GenericAccess<T>> {
     
    	public abstract T returnThis();
     
    	public abstract static class Access1<TT extends Access1<TT>> extends GenericAccess<TT>{
    	}
     
    	public static class Access2 extends GenericAccess<Access2>{
    		@Override
    		public Access2 returnThis() {
    			return this;
    		}
    	}
     
    	public static class Access3 extends GenericAccess<Access3>{
    		@Override
    		public Access3 returnThis() {
    			return this;
    		}
     
    	}
     
    	public static class Access4<TT extends Access4<TT>> extends Access1<TT>{
     
    		private final Class<TT> myClass; 
     
    		@SuppressWarnings("unchecked")
    		public Access4(){
    			myClass = (Class<TT>) this.getClass();
    		}
     
    		@SuppressWarnings("unchecked")
    		@Override
    		public TT returnThis() {
    			return this.getClass().cast(this); //Erreur cannot convert from ? extends GenericAccess to TT
    			return (TT) this;
    		}	
    	}
     
    	public static class Access5 extends Access4<Access5> {
     
    	}
     
    	public static class Access6 extends Access5 {
     
    	}
     
    	public static void main(String[] args) {
    		Access1 ac1 = new Access1().returnThis();  //Erreur cannot instantiate Access1 
    		Access2 ac2 = new Access2().returnThis(); 
    		Access3 ac3 = new Access3().returnThis(); 
    		Access4<?> ac4 = new Access4<>().returnThis();
    		Access4<Access4> ac4b = new Access4<Access4<Access4>>().returnThis();  // Erreur, d'ailleurs on doit s'arrêter où
    		Access5 ac5 = new Access5().returnThis(); 
    		Access6 ac6 = new Access6().returnThis();  //Erreur cannot convert from Access5 to Access6
    	}
    }
    En fait la solution qu' il m'arrive d'utiliser est d'avoir une méthode équivalente à returnThis() qui soit abstraite, et de faire toute une structure d'héritage en classes abstraites, avec des classes non abstraites qui sont finales (du coup non surchargeables), c'est parfois un peu lourd mais cela répond au specs, une autre solution est d'accepter le cast (T) dans le returnThis(), car si ton code n'est pas mal utilisé il ne devrait jamais y avoir de problèmes le cast devrait fonctionner à tout les coups.
    Je sais bien que ce n'est pas la panacé mais j'ai pas encore trouvé mieux...

    Pour répondre à quoi sert ce type de méthodes, une utilisation très pertinente du pattern de classe générique avec un type qui hérite de lui-même est très utile pour définir que des objets ont des opérations d'additions avec d'autres objets du même type, et que des objets qui supportent la multiplication sont des objets qui supportent l'addition, et cela permet aussi de dire que l'on redéfinit des structures (telles des tables spécifiques) qui ne prennent comme paramètres que des objets qui supportent l'addition.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    	public interface Addable<T extends Addable<T>> {
    		public void add(T toAdd);
    	}
     
    	public interface Multipliable<T extends Multipliable<T>> extends Addable<T> {
    		public void multiply(T toAdd);
    	}
     
    	public interface SummingTable<K,V extends Addable<V>> extends Map<K,V> {
    		default public V summingPut(K key, V val) {
    			V res = this.get(key);
    			if (res!=null) {
    				val.add(res);
    			}
    			this.put(key,val);
    			return res;
    		}
    	}
    d'ailleurs là on voit bien que si add fait un retour de type T cela peut-être aussi très pratique lors de la manipulation.

  10. #10
    Membre Expert
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Par défaut
    Merci à tous pour vos réactions !
    @Thelvin, @Eulbobo:
    le patron "builder."
    Vu qu'un setter et un constructeur sont différents (le 1er est appelable plusieurs fois), cela ferait sens d'étendre le patron builder en un patron setter. J'imagine qu'on aboutit alors à une interface fonctionnelle.
    interfaces fonctionnelles avec à chaque fois un consumer et la valeur.
    @Eulbobo:
    ça détruit le principe de responsabilité
    C'est-à-dire? Quelle différence entre les 2 versions suivantes ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    // chainée
    objet.setA(a).setB(b).setC(c);
    // non chainée
    objet.setA(a);
    objet.setB(b);
    objet.setC(c);
    @tac.p merci pour les exemples, je vais décortiquer ca.

Discussions similaires

  1. public function __toString() { return $this->toto; }
    Par aitiahcene dans le forum Doctrine2
    Réponses: 5
    Dernier message: 21/05/2012, 18h55
  2. [PHP 5.3] Erreur : "Can't use method return value in write context"
    Par FabaCoeur dans le forum Langage
    Réponses: 5
    Dernier message: 17/04/2012, 11h21
  3. Retourner l'objet en cours (return this)
    Par Gugelhupf dans le forum C++
    Réponses: 3
    Dernier message: 29/02/2012, 20h34
  4. Generics, héritage, et return this
    Par LeGritche dans le forum Général Java
    Réponses: 4
    Dernier message: 28/09/2011, 10h34
  5. Réponses: 8
    Dernier message: 19/10/2010, 18h58

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo