Convertir l'objet en type générique pour le renvoi

134

Existe-t-il un moyen de convertir un objet pour renvoyer la valeur d'une méthode? J'ai essayé de cette façon mais cela a donné une exception de temps de compilation dans la partie "instanceof":

public static <T> T convertInstanceOfObject(Object o) {
    if (o instanceof T) {
        return (T) o;
    } else {
        return null;
    }
}

J'ai également essayé celui-ci mais il a donné une exception d'exécution, ClassCastException:

public static <T> T convertInstanceOfObject(Object o) {
    try {
        T rv = (T)o;
        return rv;
    } catch(java.lang.ClassCastException e) {
        return null;
    }
}

Existe-t-il un moyen possible de le faire facilement:

String s = convertInstanceOfObject("string");
System.out.println(s); // should print "string"
Integer i = convertInstanceOfObject(4);
System.out.println(i); // should print "4"
String k = convertInstanceOfObject(345435.34);
System.out.println(k); // should print "null"

EDIT: j'ai écrit une copie de travail de la bonne réponse:

public static <T> T convertInstanceOfObject(Object o, Class<T> clazz) {
    try {
        return clazz.cast(o);
    } catch(ClassCastException e) {
        return null;
    }
}

public static void main(String args[]) {
    String s = convertInstanceOfObject("string", String.class);
    System.out.println(s);
    Integer i = convertInstanceOfObject(4, Integer.class);
    System.out.println(i);
    String k = convertInstanceOfObject(345435.34, String.class);
    System.out.println(k);
}
Sedran
la source
pourquoi le dernier devrait-il imprimer null? et pourquoi ne pas retourner un Object? L'effacement Java traduira de toute façon votre générique en un Object, alors pourquoi n'écrivez-vous pas directement public static Object convertInstanceOfObject?
ThanksForAllTheFish
Je peux décider du dernier plus tard, mais je voulais attraper ClassCastException :) Ce que je voulais savoir, c'est qu'un objet est une instance d'un autre objet avant le casting, où je ne connais pas son type réel.
sedran

Réponses:

207

Vous devez utiliser une Classinstance en raison de l'effacement du type générique lors de la compilation.

public static <T> T convertInstanceOfObject(Object o, Class<T> clazz) {
    try {
        return clazz.cast(o);
    } catch(ClassCastException e) {
        return null;
    }
}

La déclaration de cette méthode est:

public T cast(Object o)

Cela peut également être utilisé pour les types de tableaux. Cela ressemblerait à ceci:

final Class<int[]> intArrayType = int[].class;
final Object someObject = new int[]{1,2,3};
final int[] instance = convertInstanceOfObject(someObject, intArrayType);

Notez que quand lui someObjectest passé convertToInstanceOfObjecta le type de compilation Object.

SpaceTrucker
la source
58
Je sais que ça vient de l'OP, mais c'est vraiment { catch(ClassCastException e) { return null; }impardonnable
artbristol
1
@SpaceTrucker, merci. Je me demande, serait un moyen d'utiliser .isArray () à l'intérieur de convertInstanceOfObject () dans someObject et d'extraire la classe (comme intArrayType) par réflexion, puis d'appeler une méthode privée interne en passant ceux-ci? Mais même en faisant cela, je dois créer un tableau vide et le passer dans la méthode publique, non?
Cristiano
4
Il convient de mentionner que l'effacement de type se produit en fait lors de la compilation, pas à l'exécution.
Antonio Malcolm
19

Je suis tombé sur cette question et elle a attiré mon intérêt. La réponse acceptée est tout à fait correcte, mais je pensais fournir mes conclusions au niveau du code d'octet JVM pour expliquer pourquoi l'OP rencontre le ClassCastException.

J'ai le code qui est à peu près le même que le code d'OP:

public static <T> T convertInstanceOfObject(Object o) {
    try {
       return (T) o;
    } catch (ClassCastException e) {
        return null;
    }
}

public static void main(String[] args) {
    String k = convertInstanceOfObject(345435.34);
    System.out.println(k);
}

et le code d'octet correspondant est:

public static <T> T convertInstanceOfObject(java.lang.Object);
    Code:
       0: aload_0
       1: areturn
       2: astore_1
       3: aconst_null
       4: areturn
    Exception table:
       from    to  target type
           0     1     2   Class java/lang/ClassCastException

  public static void main(java.lang.String[]);
    Code:
       0: ldc2_w        #3                  // double 345435.34d
       3: invokestatic  #5                  // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
       6: invokestatic  #6                  // Method convertInstanceOfObject:(Ljava/lang/Object;)Ljava/lang/Object;
       9: checkcast     #7                  // class java/lang/String
      12: astore_1
      13: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
      16: aload_1
      17: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      20: return

Notez que l' checkcastinstruction de code d'octet se produit dans la méthode main et non la méthode convertInstanceOfObjectet convertInstanceOfObjectn'a aucune instruction pouvant être lancée ClassCastException. Parce que la méthode main n'attrape pas le ClassCastExceptiondonc, lorsque vous exécutez la méthode principale, vous obtiendrez un ClassCastExceptionet non l'attente d'impression null.

Maintenant, je modifie le code à la réponse acceptée:

public static <T> T convertInstanceOfObject(Object o, Class<T> clazz) {
        try {
            return clazz.cast(o);
        } catch (ClassCastException e) {
            return null;
        }
    }
    public static void main(String[] args) {
        String k = convertInstanceOfObject(345435.34, String.class);
        System.out.println(k);
    }

Le code d'octet correspondant est:

public static <T> T convertInstanceOfObject(java.lang.Object, java.lang.Class<T>);
    Code:
       0: aload_1
       1: aload_0
       2: invokevirtual #2                  // Method java/lang/Class.cast:(Ljava/lang/Object;)Ljava/lang/Object;
       5: areturn
       6: astore_2
       7: aconst_null
       8: areturn
    Exception table:
       from    to  target type
           0     5     6   Class java/lang/ClassCastException

  public static void main(java.lang.String[]);
    Code:
       0: ldc2_w        #4                  // double 345435.34d
       3: invokestatic  #6                  // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
       6: ldc           #7                  // class java/lang/String
       8: invokestatic  #8                  // Method convertInstanceOfObject:(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
      11: checkcast     #7                  // class java/lang/String
      14: astore_1
      15: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      18: aload_1
      19: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      22: return

Notez qu'il y a une invokevirtualinstruction dans la convertInstanceOfObjectméthode qui appelle la Class.cast()méthode qui lance ClassCastExceptionqui sera capturée par le catch(ClassCastException e)bock et retournée null; par conséquent, "null" est imprimé sur la console sans aucune exception.

Alvin
la source
14

Si vous ne voulez pas dépendre de la levée d'exception (ce que vous ne devriez probablement pas), vous pouvez essayer ceci:

public static <T> T cast(Object o, Class<T> clazz) {
    return clazz.isInstance(o) ? clazz.cast(o) : null;
}
intra
la source
Bien mieux. Je déteste les flux de programmes basés sur les exceptions. Dans mon cas, j'ai voulu vérifier si l'objet en question implémentait une interface. Pour cela, j'ai utilisé isAssignableFrom.
haslo