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 :

classe anonyme et paramétre final


Sujet :

Langage Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Avatar de natoine
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Décembre 2007
    Messages
    393
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Chercheur en informatique

    Informations forums :
    Inscription : Décembre 2007
    Messages : 393
    Par défaut classe anonyme et paramétre final
    Bonjour à tous,
    alors voilà je pose la question suivante :
    Pourquoi est-ce que les variables locales externes à l'instance de la classe anonyme doivent être finales ?

    Je sais que ça garantit que la classe anonyme ne puisse pas modifier ces variables mais je ne comprends pas l'intérêt.

    C'est encore pire pour le paramétre passé en appel de la méthode dans laquelle est définie ma classe anonyme. Je ne comprends pas à quoi ça sert qu'il soit final puisque de toute façon si on modifiait la valeur de ce paramétre dans la méthode, on ne ferait que modifier une copie du paramétre ...

    Un exemple de code avec classe anonyme.

    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
    public class WithAnonymousClass 
    {
        int b=80;
     
        void method(final int a){
            final int r = a + 1 ;
            ForAnonymousClass t = new ForAnonymousClass()
            {
                int c = a;
                public void doStuff()
                {
                    c++;
                    b = r + 1;
                    System.out.println("doStuff de ForAnonymousClass, valeur de b :" + b + " valeur de c : " + c);
                }
                public void doSomethingElse()
                {
                    b ++ ;
                    System.out.println("doSomethingElse de ForAnonymousClass");
                }
            } ; 
            t.doStuff();
            //t.doSomethingElse(); //inconnu car non défini dans l'interface, pourtant la méthode est public ...
            OtherClass other_class = new OtherClass()
            {
                int k = 7 ;
                public void doOtherStuff() 
                {
                    super.doOtherStuff();
                    System.out.print("doOtherStuff dans re définition de OtherClass"); 
                } ;
            };
            other_class.doOtherStuff() ;
        }
     
        public static void main(String[] args)
        {
            WithAnonymousClass b = new WithAnonymousClass();
            int param = 0;
            b.method(param);
        }
    }
    www.natoine.fr
    natoine.developpez.com
    Principalement du Java avec un soupçon de réseaux sociaux.

  2. #2
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Salut,


    Lorsque tu accèdes à une variable locale depuis une classe anonyme, tu accèdes en réalité à une copie qui est implicitement passé en paramètre au constructeur de ta classe anonyme.

    Du coup les modifications apportées dans la classe anonyme ne sont pas visible par le reste de la méthode, ce qui pourrait être source d'erreur. Pour éviter de tel ambiguïté ces variables doivent être déclarées final.

    a++

  3. #3
    Membre éclairé
    Avatar de natoine
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Décembre 2007
    Messages
    393
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Chercheur en informatique

    Informations forums :
    Inscription : Décembre 2007
    Messages : 393
    Par défaut
    Merci pour ta réponse.

    hum mais attends là, chaque fois que je définis une méthode en java, je sais que dans le corps de ma méthode je vais travailler sur une copie de mes paramétres et que du coup, mes modifications ne seront pas visibles en dehors de ma méthode.
    Pourquoi alors, avec la même logique que celle que tu m'énonces, je ne devrai pas déclarer tous mes paramétres final ?
    www.natoine.fr
    natoine.developpez.com
    Principalement du Java avec un soupçon de réseaux sociaux.

  4. #4
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    C'est un choix qui a été fait c'est tout...


    Lorsque tu appelles un méthode, le passage de paramètre est explicite dans le code.

    Lorsque tu utilises une variable locale dans une classe anonyme, le passage de paramètre est implicite et n'apparait pas dans le code. Du coup cela peut prêter à ambiguïté.


    a++

  5. #5
    Membre éclairé
    Avatar de natoine
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Décembre 2007
    Messages
    393
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Chercheur en informatique

    Informations forums :
    Inscription : Décembre 2007
    Messages : 393
    Par défaut
    Ok. Bien reçu. Là je comprends.
    www.natoine.fr
    natoine.developpez.com
    Principalement du Java avec un soupçon de réseaux sociaux.

  6. #6
    Membre chevronné
    Inscrit en
    Novembre 2006
    Messages
    362
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 362
    Par défaut
    Citation Envoyé par natoine Voir le message
    Pourquoi est-ce que les variables locales externes à l'instance de la classe anonyme doivent être finales ?
    Bonjour,

    Il est probable que je me trompe, mais ce problème pourrait selon moi n'avoir aucun rapport avec le fait que la classe soit anonyme (cf capture d'écran).

    Any local variable, formal method parameter or exception handler parameter used but not declared in an inner class must be declared final. Any local variable, used but not declared in an inner class must be definitely assigned (§16) before the body of the inner class
    cf ici

    Toutes mes excuses si je fais erreur.

    Cela ne remet pas du tout la réponse d'AdiguBa en question, puisqu'elle est toujours valable si on remplace "anonyme" par "inner".

    Le problème ne vient que du fait que la classe continue à exister hors de la portée de la méthode, et donc hors de la durée de vie de la variable locale. Il n'est donc pas possible de conserver une référence sur cette variable à l'intérieur de la classe.

    Merci d'avoir lu.
    Images attachées Images attachées  

  7. #7
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Une classe anonyme est une inner-classe... donc je ne vois pas ce que ca change

    a++

  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
    Bah, comme il le dit, ça n'a pas à voir avec le fait que la classe soit anonyme... Mais avec le fait que ça soit une inner class .
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  9. #9
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Dans 99% des cas, les inner-classes qui accèdent à des variables locales sont des classes anonymes.

    J'ai rarement vu des inner-classes non-anonymes déclarées à l'intérieur d'une méthode...

    a++

  10. #10
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    bien vu elvin, grâce à toi, je viens de découvrir qu'on pouvait écrire cette horreur qui, je dois l'admettre, me permettra peut-être un jour de palier à l'absence de constructeur dans une anonymous inner class (bon prendre un rendez-vous chez un psy maintenant, j'ai raté mon jet de SAN)


    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
    public class TestInner {
     
        Object o;
        public void test(String a){
            final String test = a;
            class Inner {
                String param;
                public Inner(String param){
                    this.param=param;
                }
                public String doTest(){
                    return test+" -> "+param;
                }
            }
            String test2 = "12345";
            Inner in = new Inner(test2);
            o = in;
            System.out.println(in.doTest());
        }
        public static void main(String[] argv){
            try {
                TestInner in = new TestInner();
                in.test("blablabla");
                System.out.println(in.o.getClass());
                Constructor c = in.o.getClass().getDeclaredConstructors()[0];
                Method m = in.o.getClass().getMethod("doTest");
                System.out.println(m.invoke(c.newInstance(in,"444","719")));
            } catch (InstantiationException ex) {
                Logger.getLogger(TestInner.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalAccessException ex) {
                Logger.getLogger(TestInner.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalArgumentException ex) {
                Logger.getLogger(TestInner.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InvocationTargetException ex) {
                Logger.getLogger(TestInner.class.getName()).log(Level.SEVERE, null, ex);
            } catch (NoSuchMethodException ex) {
                Logger.getLogger(TestInner.class.getName()).log(Level.SEVERE, null, ex);
            } catch (SecurityException ex) {
                Logger.getLogger(TestInner.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
     
    }
    Au fond de moi, quelque chose viens de mourir

  11. #11
    Membre Expert
    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 : 76
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : retraité nostalgique Java SE

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 257
    Par défaut
    Citation Envoyé par Feriaman Voir le message
    Le problème ne vient que du fait que la classe continue à exister hors de la portée de la méthode, et donc hors de la durée de vie de la variable locale. Il n'est donc pas possible de conserver une référence sur cette variable à l'intérieur de la classe.
    si la classe interne liée à l'instance courante(quelle quelle soit) garde une référence vers une variable membre de l'instance englobante on n'a pas cette nécessité.
    Si c'est quelque chose alloué sur la pile c'est pas la même histoire
    Dans un cas le nouvel objet peut accéder par reférenceEnglobante.champ dans l'autre c'est une copie qui est passée et donc il ne faut pas espérer qu'une modification dans le code englobant soit répercuté dans le nouvel objet.
    C'est ça?

  12. #12
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Citation Envoyé par professeur shadoko Voir le message
    C'est ça?
    Ben oui, d'ailleurs si tu regarde le code que j'ai donné à se flinguer, par "réflection", le constructeur prend 3 paramètres et non pas 1. Sont rajoutés au constructeur: le paramètre final ainsi que l'instance englobante Le final ne serait théoriquement pas une nécessité, tant qu'on garde conscience qu'il y a une copie. Mais dans ce genre de code, alors, il y aurait je pense ambiguité :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        public void test(String a){
            String test = a;
            class Inner {
                public String doTest(){
                    return test+" -> "+param;
                }
            }
            test = "444719";
            new Inner().doTest(); // quelle valeur de "test" serait passée au constructeur?
        }

  13. #13
    Invité
    Invité(e)
    Par défaut
    Une solution qui peut être mise en place pour pallier à ce problème est d'analyser si la variable n'est pas finale et est de type immuable, de passer un object Wrapper contenant la variable et de traduire toutes les utilisations de cette variable par des utilisations du wrapper (dans la méthode comme dans la classe anonyme). Ainsi la classe anonyme peut changer la valeur de la variable mais aussi répondre aux changements dans la méthode.

    Sinon tchize_: pour pallier à l'absence de constructeur pour les classes anonymes, tu peux toujours utiliser un bloc d'initialisation, j'ai déjà vu un truc comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    Map<String, String> map = new HashMap<String, String>() {
      {
        put("test", "test");
        put("hop", "yep");
      ...
      }
    };
    C'est pas top mais ça n'utilise pas la réflection

  14. #14
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    oui je connais, mais on reste sur le problème de base alors, pas question d'utiliser autre chose que des finals. Hors à un constructeur tu peux lui passer autre chose :p

  15. #15
    Membre chevronné
    Inscrit en
    Novembre 2006
    Messages
    362
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 362
    Par défaut
    Citation Envoyé par professeur shadoko Voir le message
    si la classe interne liée à l'instance courante(quelle quelle soit) garde une référence vers une variable membre de l'instance englobante on n'a pas cette nécessité.

    Si c'est quelque chose alloué sur la pile c'est pas la même histoire

    Dans un cas le nouvel objet peut accéder par reférenceEnglobante.champ dans l'autre c'est une copie qui est passée et donc il ne faut pas espérer qu'une modification dans le code englobant soit répercuté dans le nouvel objet.
    C'est ça?
    Cette description me semble correcte.

    La seule chose sur laquelle je ne m'engagerais pas c'est sur le terme "copie". Il me semble que c'est AdiGuba qui a introduit se terme, et je pense qu'on peut lui faire confiance.

    Moi j'imaginais plutôt que que les variables finales étaient en réalité stockées dans un espace à part qui leur est propre.
    En tout cas, c'est le cas pour les programmes C++ compilés (le Data segment).
    Et en effet, il ne semble pas très intéressant de se balader avec trente copies de la même variable finale en mémoire, alors que justement elle est finale.

    Cela dit, cet article sous-entend que c'est plus compliqué que cela dans la JVM. Du coup, je n'ai aucune certitude.

    Citation Envoyé par George7
    Sinon tchize_: pour pallier à l'absence de constructeur pour les classes anonymes, tu peux toujours utiliser un bloc d'initialisation, j'ai déjà vu un truc comme ça :
    Je ne comprends pas pourquoi vous voulez absolument utiliser des classes anonymes, et bidouiller un constructeur, alors que le Java propose justement le mécanisme des Inner Classes pour répondre à cela.
    Pour moi, une inner classe définie dans une fonction fait exactement comme une classe anonyme, mais avec un constructeur.

    [EDIT]D'ailleurs, c'est précisément le point sur lequel adiGuba insistait plus haut : les classes anonymes sont des inner classes sans nom et avec des constructeurs implicites[/EDIT]

  16. #16
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Feriaman Voir le message
    La seule chose sur laquelle je ne m'engagerais pas c'est sur le terme "copie". Il me semble que c'est AdiGuba qui a introduit se terme, et je pense qu'on peut lui faire confiance.
    Les variables locales sont passées implicitement au constructeur de l'inner-classe. Le passage de paramètres se fait par copie. Bien sûr si on passe un objet on ne copie que la référence et non pas l'objet en lui-même.

    Citation Envoyé par Feriaman Voir le message
    Moi j'imaginais plutôt que que les variables finales étaient en réalité stockées dans un espace à part qui leur est propre.
    En tout cas, c'est le cas pour les programmes C++ compilés (le Data segment).
    Et en effet, il ne semble pas très intéressant de se balader avec trente copies de la même variable finale en mémoire, alors que justement elle est finale.
    La copie ne concerne que la référence et non pas l'objet en lui-même.
    Après il faut distinguer les variables finales et les constantes qui sont stocké directement dans la classe.

    Citation Envoyé par Feriaman Voir le message
    Cela dit, cet article sous-entend que c'est plus compliqué que cela dans la JVM. Du coup, je n'ai aucune certitude.
    Heu... on n'est plus dans le même sujet là non ?


    Citation Envoyé par Feriaman Voir le message
    Je ne comprends pas pourquoi vous voulez absolument utiliser des classes anonymes, et bidouiller un constructeur, alors que le Java propose justement le mécanisme des Inner Classes pour répondre à cela.
    Si tu accèdes à des variables locales, cela signifie que tu dois déclarer la classe dans le corps d'une méthode. Dans ce cas là on utilise généralement une classe anonyme...

    Citation Envoyé par Feriaman Voir le message
    Pour moi, une inner classe définie dans une fonction fait exactement comme une classe anonyme, mais avec un constructeur.
    Et tu le fait souvent ? Perso c'est le genre de code que je vois très peu !!!

    a++

  17. #17
    Membre chevronné
    Inscrit en
    Novembre 2006
    Messages
    362
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 362
    Par défaut
    Citation Envoyé par adiGuba Voir le message
    Les variables locales sont passées implicitement au constructeur de l'inner-classe.
    Tu l'affirmes, et encore une fois : a priori, je te fais confiance.
    Mais ça m'intéresse de savoir où tu as trouvé cette information.

    Citation Envoyé par adiGuba Voir le message
    Heu... on n'est plus dans le même sujet là non ?
    Au contraire : dans la question 6b, l'initialisation de la variable statique j a visiblement lieu avant le constructeur de "Derived", alors que la règle dit qu'il devrait avoir lieu après.

    J'en déduis que les variables finales sont initialisées différemment des variables non-finales (ce qui est confirmé par la règle puisqu'elles ont lieu avant).

    Une des raisons que j'imagine (et là tu as raison, il ne s'agit que de mon imagination), c'est qu'elles sont pré-initialisées avant le lancement du programme. Et elles sont pré-initialisées parce qu'elles ne sont pas rangées au même endroit que les autres en mémoire.

    J'imagine ceci, parce qu'en C++, c'est comme ça que ça marche.


    Citation Envoyé par adiGuba Voir le message
    Dans ce cas là on utilise généralement une classe anonyme...
    Et tu le fait souvent ? Perso c'est le genre de code que je vois très peu !!!
    Je ne comprends pas cet argument.
    Java prévoit cette solution pour gérer ce besoin.
    Pour moi la question ne se pose pas : si j'ai ce besoin j'utilise cette solution.

    La question : est-ce que d'autres gens l'utilise beaucoup ou pas beaucoup n'a rien à voir là-dedans.
    La bonne pratique n'est pas un concours de popularité.

  18. #18
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Citation Envoyé par Feriaman Voir le message

    Je ne comprends pas pourquoi vous voulez absolument utiliser des classes anonymes, et bidouiller un constructeur, alors que le Java propose justement le mécanisme des Inner Classes pour répondre à cela.
    Pour moi, une inner classe définie dans une fonction fait exactement comme une classe anonyme, mais avec un constructeur.
    Si vous lisiez mon message, vous verriez que je mensionne justement que j'ignorais qu'on pouvais mettre une Inner class dans une méthode qui ne soit pas anonymé, d'ou mes problèmes par le passé pour initialiser mes classes anonymes

Discussions similaires

  1. Serialization et classe anonyme
    Par vincent63 dans le forum Langage
    Réponses: 6
    Dernier message: 07/07/2006, 19h03
  2. Variable d'instance et classe anonyme
    Par zoullou dans le forum AWT/Swing
    Réponses: 7
    Dernier message: 21/05/2006, 12h30
  3. [Débutant]Passer une classe abstraite en paramètre
    Par Invité dans le forum Débuter
    Réponses: 2
    Dernier message: 06/01/2006, 17h56
  4. Réponses: 5
    Dernier message: 16/08/2005, 10h30
  5. [classe anonyme] implementant une interface
    Par stanilas dans le forum Langage
    Réponses: 4
    Dernier message: 30/11/2004, 00h18

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