Converter objeto em tipo genérico para retornar


134

Existe uma maneira de converter um objeto para retornar o valor de um método? Eu tentei dessa maneira, mas deu uma exceção de tempo de compilação na parte "instanceof":

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

Eu também tentei este, mas deu uma exceção de tempo de execução, ClassCastException:

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

Existe uma maneira possível de fazer isso facilmente:

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: eu escrevi uma cópia de trabalho da resposta correta:

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);
}

por que o último deve imprimir null? e, por que você não retorna um Object? O apagamento de Java de qualquer maneira traduz seu genérico em um Object, então por que você não escreve diretamente public static Object convertInstanceOfObject?
ThanksForAllTheFish

Posso decidir o posterior mais tarde, mas eu queria pegar ClassCastException :) O que eu queria saber é que um objeto é uma instância de outro objeto antes da transmissão, onde não conheço seu tipo real.
Sedran

Respostas:


207

Você precisa usar uma Classinstância devido ao apagamento de tipo genérico durante a compilação.

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

A declaração desse método é:

public T cast(Object o)

Isso também pode ser usado para tipos de matriz. Seria assim:

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

Observe que quando someObjecté passado para convertToInstanceOfObjectele, tem o tipo de tempo de compilação Object.


58
Eu sei que é a partir do OP, mas realmente { catch(ClassCastException e) { return null; }é imperdoável
artbristol

1
@SpaceTrucker, obrigado. Gostaria de saber, seria alguma maneira de usar .isArray () dentro convertInstanceOfObject () no someObject e extrair a classe (como o intArrayType) dele por reflexão e, em seguida, chamar um método privado interno passando esses? Mas, mesmo fazendo isso, devo criar uma matriz vazia e passá-la no método público, certo?
Cristiano

4
Deve-se mencionar que o apagamento de tipo realmente acontece na compilação, não no tempo de execução.
Antonio Malcolm

19

Eu me deparo com essa pergunta e ela me interessou. A resposta aceita está completamente correta, mas pensei em fornecer minhas descobertas no nível de código de bytes da JVM para explicar por que o OP encontra o ClassCastException.

Eu tenho o código que é praticamente o mesmo que o código do 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);
}

e o código de bytes correspondente é:

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

Observe que a checkcastinstrução do código de bytes ocorre no método principal, e não o método convertInstanceOfObjecte convertInstanceOfObjectnão possui nenhuma instrução que possa ser lançada ClassCastException. Como o método principal não captura, ClassCastExceptionportanto, quando você executa o método principal, você obtém uma ClassCastExceptione não a expectativa de impressão null.

Agora eu modifico o código para a resposta aceita:

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);
    }

O código de bytes correspondente é:

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

Observe que existe uma invokevirtualinstrução no convertInstanceOfObjectmétodo que chama o Class.cast()método que lança ClassCastExceptionque será capturado pela catch(ClassCastException e)bock e retornará null; portanto, "null" é impresso no console sem nenhuma exceção.


14

Se você não quiser depender da exceção de lançamento (o que provavelmente não deveria), tente o seguinte:

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

Muito melhor. Eu odeio fluxos de programas baseados em exceção. No meu caso, eu queria verificar se o objeto em questão implementa uma interface. Para isso, eu usei isAssignableFrom.
haslo
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.