Keluarkan Objek ke Tipe Generik untuk kembali

134

Apakah ada cara untuk melemparkan objek untuk mengembalikan nilai metode? Saya mencoba cara ini tetapi memberikan pengecualian waktu kompilasi di bagian "instanceof":

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

Saya juga mencoba yang ini tetapi itu memberikan pengecualian runtime, ClassCastException:

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

Apakah ada cara yang mungkin untuk melakukan ini dengan mudah:

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: Saya menulis salinan jawaban yang benar:

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
sumber
mengapa yang terakhir harus dicetak null? dan, mengapa Anda tidak mengembalikannya Object? Penghapusan Java akan menerjemahkan generik Anda menjadi Object, jadi mengapa Anda tidak menulis secara langsung public static Object convertInstanceOfObject?
ThanksForAllTheFish
Saya dapat memutuskan yang kemudian, tapi saya ingin menangkap ClassCastException :) Hal yang ingin saya ketahui adalah bahwa suatu objek adalah turunan dari objek lain sebelum casting, di mana saya tidak tahu tipe aslinya.
sedran

Jawaban:

207

Anda harus menggunakan Classinstance karena penghapusan tipe generik selama kompilasi.

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

Deklarasi metode itu adalah:

public T cast(Object o)

Ini juga dapat digunakan untuk tipe array. Akan terlihat seperti ini:

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

Perhatikan bahwa ketika someObjectdilewatkan ke convertToInstanceOfObjectitu memiliki jenis waktu kompilasi Object.

SpaceTrucker
sumber
58
Saya tahu itu dari OP, tapi benar { catch(ClassCastException e) { return null; }- benar tidak bisa dimaafkan
artbristol
1
@ SpaceTrucker, terima kasih. Saya bertanya-tanya, apakah ada cara untuk menggunakan .isArray () di dalam convertInstanceOfObject () di someObject dan ekstrak kelas (sebagai intArrayType) darinya dengan refleksi dan kemudian memanggil metode privat internal yang melewatinya? Tetapi bahkan melakukan ini, saya harus membuat array kosong dan menyebarkannya di metode publik, kan?
Cristiano
4
Harus disebutkan bahwa tipe erasure sebenarnya terjadi saat kompilasi, bukan runtime.
Antonio Malcolm
19

Saya menemukan pertanyaan ini dan itu menarik minat saya. Jawaban yang diterima sepenuhnya benar, tapi saya pikir saya memberikan temuan saya pada level kode byte JVM untuk menjelaskan mengapa OP menemukan ClassCastException.

Saya memiliki kode yang hampir sama dengan kode 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);
}

dan kode byte yang sesuai adalah:

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

Perhatikan bahwa checkcastinstruksi kode byte terjadi pada metode utama bukan metode convertInstanceOfObjectdan convertInstanceOfObjectmetode tidak memiliki instruksi yang dapat melempar ClassCastException. Karena metode utama tidak menangkap ClassCastExceptionmaka ketika Anda menjalankan metode utama Anda akan mendapatkan ClassCastExceptiondan bukan harapan pencetakan null.

Sekarang saya mengubah kode menjadi jawaban yang diterima:

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

Kode byte yang sesuai adalah:

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

Perhatikan bahwa ada invokevirtualinstruksi dalam convertInstanceOfObjectmetode yang memanggil Class.cast()metode yang melempar ClassCastExceptionyang akan ditangkap oleh catch(ClassCastException e)bock dan kembali null; karenanya, "null" dicetak ke konsol tanpa kecuali.

Alvin
sumber
14

Jika Anda tidak ingin bergantung pada melempar pengecualian (yang mungkin sebaiknya tidak Anda lakukan), Anda dapat mencoba ini:

public static <T> T cast(Object o, Class<T> clazz) {
    return clazz.isInstance(o) ? clazz.cast(o) : null;
}
intra
sumber
Jauh lebih baik. Aku benci aliran program berbasis pengecualian. Dalam kasus saya, saya ingin memeriksa apakah objek tersebut mengimplementasikan antarmuka. Untuk itu, saya menggunakan isAssignableFrom.
haslo