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 :

Listes et interfaces


Sujet :

Langage Java

  1. #1
    Membre éprouvé
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Points : 1 066
    Points
    1 066
    Par défaut Listes et interfaces
    Salut à toutes et à tous

    En pleine création d'un petit logiciel de gestion de comptes, je suis confronté à un problème qui me parait un peu stupide... Soit je m'y prends mal, soit java peut être amélioré.

    Situons le contexte:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    package org.aspyct.mpc.accounting;
     
    public interface Movement {
       ...
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    package org.aspyct.mpc.accounting;
     
    class MovementImpl implements Movement {
        ...
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package org.aspyct.mpc.accounting;
     
    class AccountingImpl implements Accounting {
        private List<MovementImpl> movements;
     
        public Iterator<Movement> movements() {
            // La ligne suivante plante lamentablement, me disant "Incompatible types"
            return Collections.unmodifiableList(movements).iterator();
     
            // Pourtant, la suivante fonctionne, alors que c'est, en soi, la même histoire
            return new ArrayList<Movement>(movements).iterator();
        }
    }
    Alors... est-ce que je m'y prends comme un pied avec mes interfaces et mes listes, ou est-ce que vous trouvez aussi que c'est un comportement bizarre ?

    Je conçois bien que la première ligne retournerait quelque chose du type Iterator<MovementImpl>, mais puisque de MovementImpl à Movement on effectue une généralisation, ça devrait passer quand même...

  2. #2
    Membre expérimenté
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    1 252
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Mai 2004
    Messages : 1 252
    Points : 1 419
    Points
    1 419
    Par défaut
    Si j'étais toi, je déclarerais une List<Movement> plutôt qu'une List<MovementImpl>. As-tu vraiment besoin de savoir que ce que tu stockes sont des MovementImpl ? N'as-tu pas besoin uniquement des méthodes déclarées dans Movement ?

  3. #3
    Membre éprouvé
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Points : 1 066
    Points
    1 066
    Par défaut
    Oui, c'est de fait mieux une List<Movement>, mais c'est presque une chance: dans le cas des Account, j'ai bel et bien besoin des AccountImpl pour faire du travail sans cast à tout bout de champ...
    J'ai été induit en erreur par cette dernière nécessité, et c'est dors et déjà corrigé.

    Il n'empeche que le fait exposé plus haut est un peu étrange, non ?

  4. #4
    Membre expérimenté
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    1 252
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Mai 2004
    Messages : 1 252
    Points : 1 419
    Points
    1 419
    Par défaut
    Non : par défaut les génériques prennent des types exacts. Pour dire qu'on veut un type qui peut implémenter, il faut utiliser les wildcards.

    Je préfère éviter de raconter des bêtises. Aussi, je te redirige vers ce tutoriel à ce sujet.

  5. #5
    Membre éprouvé
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Points : 1 066
    Points
    1 066
    Par défaut
    Merci pour ce lien ! Mon problème est résolu.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public Iterator<? extends Movement> movements() {}
    Résolu, oui, mais nettement moins naturel, aussi.
    Je ne doute pas du fait qu'il y ait une bonne raison d'avoir forcé les développeurs à faire ça, mais je serais intéressé de connaître cette raison. A l'heure actuelle, elle m'échappe complètement.

    Edit: Non, en fait je me suis planté et j'ai fait n'importe quoi. La solution ci-dessus ne fonctionne pas plus que la première.
    Ceci, par contre, fonctionne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class AccountingImpl {
        private Map<String, AccountImpl> accounts;
     
        public Iterator<Account> accounts() {
            return Collections.unmodifiableCollection(
                    (Collection<? extends Account>) accounts.values()).iterator();
        }
    }
    C'est horrible, c'est atroce, mais ça fonctionne et c'est correct.

    Quelqu'un pourrait-il m'expliquer la différence entre les deux types suivants ? Pour moi, c'est exactement pareil, sauf que la version avec le "?" est juste plus sympa niveau compilation...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    List<? extends Account> accounts;
    List<Account> accounts;

    Peut-être vous demandez-vous pourquoi je tiens tant à stocker des AccountImpl et offrir des Account. Car de plus, c'est peut-être là que se situe ma réelle erreur, et vous sauriez m'aider à la corriger...
    Première chose, je voudrais n'exposer que des interfaces aux utilisateurs de mes packages.
    Ensuite, il existe deux méthodes sur ma classe AccountImpl dont je désire faire usage dans le package sans pour autant les divulger dans l'interface.
    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
    package org.aspyct.mpc.accounting
    public interface Account {
        String getName();
        BigDecimal getMoney();
        boolean isMine();
        void setMine(boolean);
    }
     
     
    package org.aspyct.mpc.accounting
    class AccountImpl implements Account {
        // Méthodes implémentées: getName, getMoney...
     
        // Méthodes à accessibilité package
        void deposit(BigDecimal amount) {}
        void withdraw(BigDecimal amount) {}
    }
    Il y a peut-être une meilleure méthode pour faire ce que je voudrais... Et peut-être connaissez-vous la solution.

  6. #6
    Expert confirmé

    Homme Profil pro
    SDE
    Inscrit en
    Août 2007
    Messages
    2 013
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : SDE

    Informations forums :
    Inscription : Août 2007
    Messages : 2 013
    Points : 4 327
    Points
    4 327
    Par défaut
    Bonjour,

    La différence entre List<MyType> et List<? extends MyType> est qu'avec la première version, uniquement des instance de type List<MyType> pouront être affecté à une référence de type List<MyType> (par exemple ArrayList<MyType> sera valide mais pas ArrayList<MySubType>, et ceci même si MySubType extends MyType).

    en ce qui concerne la seconde version, elle permet de faire ce qui est interdit dans le premier cas).

    Ici ce qui pourait résoudre ton problème, c'est d'avoir deux interfaces :

    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
    public interface Account {
        String getName();
        BigDecimal getMoney();
        boolean isMine();
        void setMine(boolean);
    }
     
    public interface AccountFull extends  Account{
        void deposit(BigDecimal amount);
        void withdraw(BigDecimal amount);
    }
     
    public class AccountFullImpl implements AccountFull {
        // toutes les implémentations.
    }
    Tu as donc un type générique qui fédère tous les types de compte. Ensuite tu as une autre interface qui te permet d'abstraire ton implémentation pour la maintenance du programme. Puis l'implémentation même.

  7. #7
    Membre chevronné
    Avatar de professeur shadoko
    Homme Profil pro
    retraité nostalgique Java SE
    Inscrit en
    Juillet 2006
    Messages
    1 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 75
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : retraité nostalgique Java SE

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 257
    Points : 1 855
    Points
    1 855
    Par défaut
    solution simple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
       return (Collections.<Movement>unmodifiableList(movements)).iterator();
    le problème est que le compilateur a un peu de mal à faire de l'inférence de type et qu'il faut l'aider...

    edit : et tant que j'y suis ...
    ça serait pas plus mal de garder la vision non modifiable comme variable membre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
      private List<Movement> vision = Collections.<Movement>unmodifiableList(movements) ;
     
        // et faire un objet Iterable<Movement>
        public Iterator<Movement> iterator() {
            return vision.iterator() ;
      }

  8. #8
    Membre chevronné
    Inscrit en
    Août 2009
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Août 2009
    Messages : 1 073
    Points : 1 806
    Points
    1 806
    Par défaut
    Citation Envoyé par Antoine_935 Voir le message
    Je conçois bien que la première ligne retournerait quelque chose du type Iterator<MovementImpl>, mais puisque de MovementImpl à Movement on effectue une généralisation, ça devrait passer quand même...
    La raison est simple : si tu renvoies une liste de Movement, tu peux dans du code rajouter dans cette liste des choses qui hériteraient de Movement, mais qui ne seraient pas du tout des MovementImpl.
    Or, ta liste peut être utilisée ailleurs en tant que liste de MovementImpl, et donc en extraire des objets et y appliquer des méthodes spécifiques à MovementImpl, ce qui déclencherait des erreurs au Runtime.

  9. #9
    Membre éprouvé
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Points : 1 066
    Points
    1 066
    Par défaut
    Citation Envoyé par Alain Defrance Voir le message
    en ce qui concerne la seconde version, elle permet de faire ce qui est interdit dans le premier cas
    Ok, c'est donc la seule différence. Je ne comprends honnêtement pas pourquoi ce n'est pas d'office autorisé, mais bon.

    Ici ce qui pourait résoudre ton problème, c'est d'avoir deux interfaces :
    Avoir deux interfaces ne fait que remettre mon problème un niveau plus loin. Je devrai quand même stocker des AccountFull et renvoyer des Account. De plus, ça introduit une interface publique qui n'est, au final, jamais exposée, et n'a donc pas de raison d'être.

    ça serait pas plus mal de garder la vision non modifiable comme variable membre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    private List<Movement> vision = Collections.<Movement>unmodifiableList(movements) ;
     
        // et faire un objet Iterable<Movement>
        public Iterator<Movement> iterator() {
            return vision.iterator() ;
      }
    Aaaah, parce que j'avais essayé
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Collections.unmodifiableList<Movement>(movements);
    Mais il ne l'a pas accepté, puisque ce n'est visiblement pas la bonne syntaxe.

    Merci à vous pour ces infos supplémentaires.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 10
    Dernier message: 07/05/2012, 00h03
  2. List et Interface
    Par farid_974 dans le forum C#
    Réponses: 3
    Dernier message: 19/08/2009, 13h51
  3. Réponses: 17
    Dernier message: 02/07/2009, 18h44
  4. ASIO liste d'interfaces
    Par Ferllings dans le forum Boost
    Réponses: 6
    Dernier message: 06/01/2009, 11h56
  5. Liste des interfaces
    Par NiamorH dans le forum C#
    Réponses: 2
    Dernier message: 22/10/2008, 17h42

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