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 :

Typage : Object vers MonObjet


Sujet :

Langage Java

  1. #1
    Membre éclairé
    Homme Profil pro
    Développeur
    Inscrit en
    Juin 2006
    Messages
    645
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Juin 2006
    Messages : 645
    Points : 709
    Points
    709
    Par défaut Typage : Object vers MonObjet
    Bonjour,

    J'utilise une sérialisation AMF pour échanger des objets entre un module Flex et une application Java.
    Lors de la désérialisation, j'obtiens un objet de type Object que je caste en MonObjet (connu mais pouvant être n'importe quoi héritant de Object). Lors de ce cast, j'obtiens un warning tout ce qu'il y a de plus compréhensible ("Type safety: Unchecked cast from Object to T")... mais que je ne vois pas du tout comment corriger.

    Ça fonctionne, mais s'il y a un warning, c'est qu'il y a probablement quelque chose de plus élégant à faire.

    Y a-t-il une solution de "blindage" générique ?

    Le code de la méthode incriminée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    try {
        final byte[] input = dec.decode(amf);
        final InputStream bIn = new ByteArrayInputStream(input);
        final Amf3Input amf3Input = new Amf3Input(getSerializationContext());
        amf3Input.setInputStream(bIn);
        T obj = (T) amf3Input.readObject(); // Et là, ça couine. 
        return obj;
    } catch (final IOException e) {
        // Hop hop.
    } catch (final ClassNotFoundException e) {
        // Hop hop aussi.
    }
    Merci !

    Alban
    « Se demander si un ordinateur peut penser est aussi intéressant que de se demander si un sous-marin peut nager. »
    -- Edsger Dijkstra

  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,


    Le problème vient du fait que les casts s'associent mal avec les Generics.

    Les Generics te garantissent un code sans erreur de type, grâce au compilateur qui a la charge de vérifier la cohérence du typeage de ton code.

    Or les casts peuvent engendrer des erreurs à l'exécution. De plus comme le code des Generics est "perdu", il y a certains casts qui n'en sont pas réellement et qui sont en fait exécuté plus tard.

    Bref si tu mélanges des casts et du code Generics, le compilateur ne peut plus assurer la cohérence de l'ensemble, et il t'indique cela via ce warning.



    Si tu es parfaitement sûr de toi, tu peux utiliser l'annotation @SuppressWarnings pour indiquer au compilateur que tu comprends le problème et que tu prends cela en charge :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    @SuppressWarnings("unchecked")
    T obj = (T) amf3Input.readObject();

    a++

  3. #3
    Membre éclairé
    Homme Profil pro
    Développeur
    Inscrit en
    Juin 2006
    Messages
    645
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Juin 2006
    Messages : 645
    Points : 709
    Points
    709
    Par défaut
    Merci pour les précisions.

    Pour tout dire, je suis sûr de l'utilisation que je vais faire de ce code, je suis sûr du code lui-même (il est testé et éprouvé)... mais j'aimerais autant éviter à mes gentils collègues de mettre le pied dans un piège à loup si je peux l'éviter.

    Ceci dit, je suis prêt à ne pas utiliser les génériques s'il y a un autre moyen (simple ou presque) de le faire. Sinon, je taperai dans le suppressWarnings... ça ne sera pas la première fois (même si ça me fait toujours mal au coeur ).
    « Se demander si un ordinateur peut penser est aussi intéressant que de se demander si un sous-marin peut nager. »
    -- Edsger Dijkstra

  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
    Il faudrait voir le code de la méthode entière... mais en général ce n'est pas bien méchant.

    Ici le problème est déplacé de quelque ligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        T obj = (T) amf3Input.readObject();
        return obj;
    En cas de type incorrect, il n'y aura pas de ClassCastException sur le cast, mais uniquement après le retour de la méthode...

    a++

  5. #5
    Membre éclairé
    Homme Profil pro
    Développeur
    Inscrit en
    Juin 2006
    Messages
    645
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Juin 2006
    Messages : 645
    Points : 709
    Points
    709
    Par défaut
    En fait, toute la méthode est là. C'est une méthode utilitaire qui se tape la conversion.

    Ma requête HTTP contient l'objet en AMF (encodé en base 64), c'est le paramètre d'entrée de la méthode (String amf) et je renvoie l'objet désérialisé de type T :
    Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
    MonObjet obj = AmfUtil.<MonObjet> fromAmf(amf); // Avec la-dedans le code ci-dessus
    « Se demander si un ordinateur peut penser est aussi intéressant que de se demander si un sous-marin peut nager. »
    -- Edsger Dijkstra

  6. #6
    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
    Je voulais avoir toute la méthode, avec sa signature exacte.


    Je suppose que tu as quelques chose comme cela :
    Le problème c'est qu'en cas d'usage incorrect, tu peux te trimballer un T avec un type incorrect, et qui risque de te péter à la figure bien plus loin dans le code, et de manière totalement inattendu.

    Pour sécuriser cela il faudrait rajouter le type Class<T> en paramètre, afin de faire un cast sécurisé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public <T> T method(Class<T> baseClazz)
    Puis :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        T obj = baseClazz.cast( amf3Input.readObject() );
        return obj;
    Ce qui a le mérite de te péter à la gueule tout de suite en cas de soucis


    a++

  7. #7
    Membre éclairé
    Homme Profil pro
    Développeur
    Inscrit en
    Juin 2006
    Messages
    645
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Juin 2006
    Messages : 645
    Points : 709
    Points
    709
    Par défaut
    Voilà quelque chose qui me plaît bien
    C'est implémenté, ça marche et finis les warnings.

    Merci !
    « Se demander si un ordinateur peut penser est aussi intéressant que de se demander si un sous-marin peut nager. »
    -- Edsger Dijkstra

  8. #8
    Membre éclairé
    Homme Profil pro
    Développeur
    Inscrit en
    Juin 2006
    Messages
    645
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Juin 2006
    Messages : 645
    Points : 709
    Points
    709
    Par défaut
    Ah ah ah, presque ! Ca marche bien... sauf pour les generics.
    Un appel comme celui-ci ne fonctionne pas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    final List<MonObjet> maListe = AmfUtil.<ArrayList<MonObjet>> fromAmf(source, ?????);
    J'ai bien essayé de tricher avec le typage :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    final List<MonObjet> maListe = AmfUtil.<ArrayList<MonObjet>> fromAmf(source, (new ArrayList<MonObjet>()).getClass());
    m'enfin c'est pas mieux.

    The parameterized method <ArrayList<MonObjet>>fromAmf(String, Class<ArrayList<MonObjet>>) of type AmfUtil is not applicable for the arguments (String, Class<capture#1-of ? extends ArrayList>)
    La méthode utilisée :
    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 static <T> T fromAmf(final String amf, final Class<T> baseClass) throws SystemException {
      final Base64 dec = new Base64();
      try {
        final byte[] input = dec.decode(amf);
        final InputStream bIn = new ByteArrayInputStream(input);
        final Amf3Input amf3Input = new Amf3Input(getSerializationContext());
        amf3Input.setInputStream(bIn);
        final T obj = baseClass.cast(amf3Input.readObject());
        return obj;
      } catch (final IOException e) {
        throw new SystemException(e.getMessage(), e);
      } catch (final ClassNotFoundException e) {
        throw new SystemException(e.getMessage(), e);
      }
    }
    Je me sens un peu boulet sur ce coup...
    « Se demander si un ordinateur peut penser est aussi intéressant que de se demander si un sous-marin peut nager. »
    -- Edsger Dijkstra

  9. #9
    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
    Le problème avec les types paramétrés, c'est qu'il n'est pas possible de garantir le typeage exact à l'exécution.

    Dans ton cas tu peux seulement contrôler le "raw type" de l'objet, c'est à dire List, mais en aucun cas le typeage précis (List<MonObjet>).

    En gros il faudrait passer par une version non-typé (List<?> avec List.class):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    final List<?> maListe = AmfUtil.fromAmf(source, List.class);
    Après bien sûr tu peux typer un peu plus fortement en remplaçant le List<?> par un List<MonObjet>, mais tu n'a aucune garantie que le type soit bien correct. D'ailleurs le compilateur te le signalera par un warning que tu peux ignorer seulement si tu es sûr de ton coup :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    @SuppressWarnings("unchecked")
    final List<MonObjet> maListe = AmfUtil.fromAmf(source, List.class);

    Le problème c'est que tu n'auras aucune erreur ni exception si tu récupère un mauvais type (par exemple si ce que tu récupère correspond en réalité à une List<String>).
    Tout se passera normalement à la lecture... mais cela pourrait provoquer des erreurs incompréhensible plus tard lorsque tu utiliseras ta liste.


    a++

  10. #10
    Modérateur

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

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 551
    Points : 21 607
    Points
    21 607
    Par défaut
    Dans ces cas-là, ce que je fais, c'est une autre méthode, dédiée aux listes. C'est moins générique mais ça vérifie bien le typage, et des cas comme celui-ci on n'en a pas non plus soixante.

    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 <T> List<T> listFromAmf(String amf, Class<T> eltClass) {
      //...
      // choper la liste
      List<?> list = (List<?>)amf3Input.readObject();
      // verifier le type des éléments
      for(Object element : list) {
        eltClass.cast(element);
      }
      // éléments ok : typer la liste, et dire au compilateur de pas s'inquiéter
      @SuppressWarnings("unchecked")
      List<T> tList = (List<T>)list;
      return tList;
    }
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  11. #11
    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
    @thelvin : bonne idée mais pourquoi ne pas externaliser cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    	public static <E, C extends Collection<E>> C cast(Collection<?> collection, Class<E> elementType) {
    		for (Object o : collection) {
    			elementType.cast(o);
    		}
    		@SuppressWarnings("unchecked")
    		C checkedCollection = (C) collection;
    		return checkedCollection;
    	}
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    List<MonObjet> maListe = cast(fromAmf(source, List.class), MonObjet.class);
    Non ?!?

    a++

  12. #12
    Membre éclairé
    Homme Profil pro
    Développeur
    Inscrit en
    Juin 2006
    Messages
    645
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Juin 2006
    Messages : 645
    Points : 709
    Points
    709
    Par défaut
    Argh, j'arrive tard !
    Entre temps, j'avais créé une méthode spécifique pour les listes en passant par une liste tampon :

    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
    public static <T> List<T> listFromAmf(final String amf, final Class<T> baseClass) throws SystemException {
      List<?> tempList;
      final List<T> retList = new ArrayList<T>();
      try {
        final Amf3Input amf3Input = getAmf3InputFromString(amf);
        tempList = (List<?>) amf3Input.readObject();
        for (final Object o : tempList) {
          if (o instanceof DtObject) {
            retList.add(baseClass.cast(DtUtil.cloneObjectWoNull((DtObject) o)));
          } else {
            retList.add(baseClass.cast(o));
          }
        }
        return retList;
      } catch (final IOException e) {
        throw new SystemException(e.getMessage(), e);
      } catch (final ClassNotFoundException e) {
        throw new SystemException(e.getMessage(), e);
      }
    }
    Ca recoupe pas mal ta solution, thelvin, à ceci près que tu n'utilises qu'une seule liste (à voir ce qui est le mieux).
    Merci à tous les deux, je pense qu'avec tout ça, je devrais m'en sortir !
    « Se demander si un ordinateur peut penser est aussi intéressant que de se demander si un sous-marin peut nager. »
    -- Edsger Dijkstra

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

Discussions similaires

  1. migration Business object vers cognos
    Par lahdeb dans le forum Administration-Migration
    Réponses: 0
    Dernier message: 19/12/2013, 16h55
  2. Réponses: 1
    Dernier message: 21/06/2013, 09h48
  3. Object vers List<Object>
    Par lahmar.abdel1 dans le forum Langage
    Réponses: 10
    Dernier message: 07/09/2010, 13h12
  4. Cast de Object vers Image
    Par krolis dans le forum C#
    Réponses: 1
    Dernier message: 17/05/2010, 19h19
  5. [C#] Conversion implicite de type object vers int
    Par alexking2005 dans le forum C#
    Réponses: 5
    Dernier message: 02/01/2007, 10h02

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