web-dev-qa-db-fra.com

Objet casté en type générique pour le retour

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 il 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 aussi 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;
    }
}

Est-il 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);
}
119
sedran

Vous devez utiliser une instance Class 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)

Ceci 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 lorsque someObject est passé à convertToInstanceOfObject, il a le type de temps de compilation Object.

188
SpaceTrucker

Je suis tombé sur cette question et cela 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 en octets de la machine virtuelle Java pour expliquer pourquoi le PO rencontrait le ClassCastException.

J'ai le code qui est à peu près le même que le code de l'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'instruction de code d'octet checkcast se produit dans la méthode principale et que la méthode convertInstanceOfObject et convertInstanceOfObject ne comporte aucune instruction pouvant renvoyer ClassCastException. Parce que la méthode principale ne récupère pas la ClassCastException, par conséquent, lorsque vous l'exécuterez, vous obtiendrez une ClassCastException et non l'attente de l'impression null.

Maintenant, je modifie le code en fonction de 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 existe une instruction invokevirtual dans la méthode convertInstanceOfObject qui appelle la méthode Class.cast() qui renvoie ClassCastException qui sera interceptée par la fonction catch(ClassCastException e) bock et renvoie null; par conséquent, "null" est imprimé sur la console sans aucune exception.

14
Alvin

Si vous ne voulez pas dépendre d'une exception de projection (ce que vous ne devriez probablement pas faire), vous pouvez essayer ceci:

public static <T> T cast(Object o, Class<T> clazz) {
    return clazz.isInstance(o) ? clazz.cast(o) : null;
}
6
intra