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 :

[génériques] instanciation de tableaux d'une classe interne


Sujet :

Langage Java

  1. #1
    Membre confirmé Avatar de Satch
    Homme Profil pro
    Hypnothérapeute - Magicien
    Inscrit en
    Mars 2004
    Messages
    498
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Suisse

    Informations professionnelles :
    Activité : Hypnothérapeute - Magicien

    Informations forums :
    Inscription : Mars 2004
    Messages : 498
    Points : 645
    Points
    645
    Par défaut [génériques] instanciation de tableaux d'une classe interne
    Bonjour,
    Je rencontre un problème avec les génériques qui peut se résumer à ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    public class Test<E> {
    	class Foo{
    	}
    	void bar(){
    		Foo[] foos = new Foo[10];
    	}
    }
    Le compilateur me dit qu'il ne peut pas créer un tableau générique de type Test<E>.Foo.
    J'ai du mal à comprendre le mécanisme qui fait que ce n'est pas possible.
    Je comprends très bien pourquoi ceci ne marcherait pas:
    Ici à la compilation le es serait converti en tableau d'Objects et donc incompatible avec le type E[].

    Mais dans l'exemple plus haut, je ne vois vraiment pas.

    Quel est donc ce mécanisme qui m'échappe ?

  2. #2
    Expert éminent sénior
    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
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,


    Ta classe Foo étant une classe interne, elle conserve un lien fort avec sa classe conteneur, et y partage même son type paramétré.

    La preuve tu peux tout à fait utiliser un type E dans Foo :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Test<E> {
    	class Foo{
    		public void method(E myArgs) {
    			// ...
    		}
    	}
    }

    Ainsi ton code n'est qu'une simplification pour ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Foo[] foos = new Foo[10];
    // équivalent à
    Test<E>.Foo[] foos = new Test<E>.Foo[10];
    le paramétrage est bien présent et on se retrouve avec le même problème : il est perdu à l'exécution et on peut donc potentiellement se retrouver avec un code non-sécurisé (parce que les Generics sont vérifiés à la compilation et les tableau à l'exécution seulement...)


    Perso je vois deux solutions :
    • Soit ta classe interne n'a pas besoin d'être lié aussi fortement à sa classe parente, et il suffit alors de la déclarer static :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      class Test<E> {
      	static class Foo{
      	}
       
      	void bar(){
      		Foo[] foos = new Foo[10];
      		// équivalent à
      		Test.Foo[] foos = new Test.Foo[10];
      	}
      }
      Foo n'est plus lié à une instance de Test, et se comporte comme une classe standard non paramétré. Elle conserve toutefois des rapports privilégiés avec Test (comme un accès à toutes ses méthodes/attributs, même privées).

    • Soit tu as besoin de ce lien avec l'instance courante. Dans ce cas il faut "supprimer" le type Generics lors de la déclaration du tableau, et tu utilises un @SuppressWarnings pour supprimer le warning qui te prévient du danger :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      class Test<E> {
      	class Foo{
      	}
       
      	void bar(){
      		@SuppressWarnings("unchecked")
      		Foo[] foos = new Test.Foo[10]; // warning !
      	}
      }
      Mais il faudra faire attention en manipulant tes Foo[]



    a++

  3. #3
    Membre confirmé Avatar de Satch
    Homme Profil pro
    Hypnothérapeute - Magicien
    Inscrit en
    Mars 2004
    Messages
    498
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Suisse

    Informations professionnelles :
    Activité : Hypnothérapeute - Magicien

    Informations forums :
    Inscription : Mars 2004
    Messages : 498
    Points : 645
    Points
    645
    Par défaut
    Je suis bien au courant qu'il suffit de passer la classe en static pour régler le truc.
    Mais je n'ai pas de problème particulier, je teste juste quelques petites choses, et je suis tombé sur l'exemple cité au début, que je ne comprends pas.

    On est d'accord que si on pouvait écrire dans une classe générique qqch comme
    ça poserait un problème à l'execution puisque la variable "e" serait transformée en Object[] à la compilation, puis castée en E[], et pouf, planté.

    Mais je n'arrive pas à formuler ce qui se passerait dans l'exemple de mon premier post.
    Citation Envoyé par adiGuba
    le paramétrage est bien présent et on se retrouve avec le même problème : il est perdu à l'exécution et on peut donc potentiellement se retrouver avec un code non-sécurisé (parce que les Generics sont vérifiés à la compilation et les tableau à l'exécution seulement...)
    Je ne trouve pas d'exemple concret dans lequel on aurait un code non sécurisé dans le cas d'un tableau d'une classe interne.

  4. #4
    Expert éminent sénior
    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
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Satch Voir le message
    On est d'accord que si on pouvait écrire dans une classe générique qqch comme
    ça poserait un problème à l'execution puisque la variable "e" serait transformée en Object[] à la compilation, puis castée en E[], et pouf, planté.
    Non justement le problème ne vient pas de là : à l'exécution on aurait deux Object[] donc ce serait bon !

    En fait le problème vient du fait que du coup on pourrait mettre n'importe quoi dans le tableau, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    E[] e = new E[10];
     
    Object[] o = e; // Autorisé par le langage
     
    o[0] = new Integer(0);
    Cette dernière ligne passera à la compilation, et ne génèrera aucune erreur à l'exécution puisque le type réel du tableau est Object[]. Pourtant on y met un Integer là où on ne connait pas le type précis...

    Mais comme la vérification est effectué à l'exécution, on ne peut pas vérifier le type paramétré puisqu'il est perdu


    Exemple :
    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
    class Test<E> {
     
    	private final E value;
     
    	public Test(E value) {
    		this.value = value;
    	}
     
    	public E get() {
    		return this.value;
    	}
     
    	public static void main(String[] args) {
    		Test<String>[] tests = new Test[10]; // warning
     
    		Object[] objects = tests;
     
    		objects[0] = new Test<Integer>(0); // En théorie cela devrait planter, mais ce n'est pas le cas
     
    		System.out.println("Ce code fonctionne sans erreur");
    		System.out.println("On met pourtant un Test<Integer> dans un tableau de Test<String>");
    		System.out.println();
    		System.out.println("En fait cela plantera lorsqu'on lira le tableau :");
    		String s = tests[0].get(); // ClassCastException, alors que ce code ne devrait pas générer d'exception
     
    	}
    }
    Ici on met un Test<Integer> dans un tableau de Test<String>.
    C'est normal que cela passe à la compilation car on a upcaster le tableau en Object[]. Par contre avec des tableaux standards cela devrait générer une ArrayStoreException.

    Avec les Generics on n'a pas d'exception puisque les types sont "compatible" après avoir perdu leurs paramétrages...


    Le gros problème étant qu'on se retrouve avec une erreur beaucoup plus loin, sur un code qui ne devrait jamais remonter d'exception...


    Cela à l'air un peu tiré par les cheveux, mais les tableaux sont souvent upcaster vers Object[] donc ce type "d'erreur" peut tout à fait arriver.
    De plus le danger c'est que l'erreur ne survient pas lorsqu'on modifie le tableau mais seulement lorsqu'on le lit par la suite... si l'intervalle entre ces deux actions est assez long, il peut être très complexe de trouver l'origine du problème


    Dans ton cas c'est le même problème, puisque tu pourrais mettre dans un Test<String>.Foo un objet de type Test<Integer>.Foo...



    Je te conseillerai donc plutôt d'utiliser une List<Test<E>.Foo> à la place du tableau...


    a++

Discussions similaires

  1. Instancier une classe interne depuis l'extérieur
    Par kimjoa dans le forum Général Java
    Réponses: 7
    Dernier message: 08/09/2010, 13h47
  2. Réponses: 4
    Dernier message: 08/03/2006, 19h07
  3. [heritage] etendre une classe interne en dehors du package
    Par ChristopheH dans le forum Langage
    Réponses: 2
    Dernier message: 11/10/2004, 14h15
  4. [Débutant][Conception] Erreur avec une classe interne
    Par Devil Redneck dans le forum Général Java
    Réponses: 5
    Dernier message: 11/06/2004, 15h45
  5. [Thread] Erreur dans une classe interne
    Par totof2308 dans le forum Général Java
    Réponses: 5
    Dernier message: 03/06/2004, 08h15

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