Bagaimana cara menggunakan Kelas <T> di Jawa?

247

Ada diskusi yang baik tentang Generics dan apa yang sebenarnya mereka lakukan di balik layar pada pertanyaan ini , jadi kita semua tahu itu Vector<int[]>adalah vektor array integer, dan HashTable<String, Person>merupakan tabel yang kuncinya adalah string dan nilai Persons. Namun, apa yang membuat saya bingung adalah penggunaan Class<>.

Kelas java Classseharusnya juga mengambil nama templat, (atau jadi saya diberitahu oleh garis bawah kuning di gerhana). Saya tidak mengerti apa yang harus saya masukkan ke sana. Inti dari Classobjek adalah ketika Anda tidak sepenuhnya memiliki informasi tentang suatu objek, untuk refleksi dan semacamnya. Mengapa itu membuat saya menentukan kelas mana yang Classakan dipegang objek? Saya jelas tidak tahu, atau saya tidak akan menggunakan Classobjek, saya akan menggunakan yang spesifik.

Karl
sumber

Jawaban:

136

Menggunakan versi umum dari kelas Kelas memungkinkan Anda, antara lain, untuk menulis hal-hal seperti

Class<? extends Collection> someCollectionClass = someMethod();

dan kemudian Anda dapat yakin bahwa objek Kelas yang Anda terima meluas Collection, dan turunan dari kelas ini akan menjadi (setidaknya) Koleksi.

Yuval
sumber
183

Yang kita tahu adalah " Semua instance dari kelas apa pun berbagi objek java.lang.Class yang sama dari jenis kelas "

misalnya)

Student a = new Student();
Student b = new Student();

Maka a.getClass() == b.getClass()itu benar.

Sekarang anggaplah

Teacher t = new Teacher();

tanpa obat generik, di bawah ini dimungkinkan.

Class studentClassRef = t.getClass();

Tapi ini salah sekarang ..?

misalnya) public void printStudentClassInfo(Class studentClassRef) {}dapat dipanggil denganTeacher.class

Ini bisa dihindari menggunakan obat generik.

Class<Student> studentClassRef = t.getClass(); //Compilation error.

Sekarang apa itu T ?? T adalah tipe parameter (juga disebut variabel tipe); dibatasi oleh kurung sudut (<>), mengikuti nama kelas.
T hanyalah simbol, seperti nama variabel (dapat berupa nama apa saja) yang dideklarasikan saat menulis file kelas. Nanti T itu akan diganti dengan
nama Kelas yang valid selama inisialisasi ( HashMap<String> map = new HashMap<String>();)

misalnya) class name<T1, T2, ..., Tn>

Jadi Class<T>mewakili objek kelas dari tipe kelas spesifik ' T'.

Asumsikan bahwa metode kelas Anda harus bekerja dengan parameter tipe yang tidak dikenal seperti di bawah ini

/**
 * Generic version of the Car class.
 * @param <T> the type of the value
 */
public class Car<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

Di sini T dapat digunakan sebagai Stringtipe sebagai CarName

ATAU T dapat digunakan sebagai Integertipe sebagai modelNumber ,

ATAU T dapat digunakan sebagai Objectjenis sebagai instance mobil yang valid .

Sekarang di sini yang di atas adalah POJO sederhana yang dapat digunakan secara berbeda saat runtime.
Collections eg) List, Set, Hashmap adalah contoh terbaik yang akan bekerja dengan objek yang berbeda sesuai dengan deklarasi T, tetapi begitu kita mendeklarasikan T sebagai String
misalnya) HashMap<String> map = new HashMap<String>();Maka ia hanya akan menerima objek instance String Class.

Metode Generik

Metode generik adalah metode yang memperkenalkan parameter tipe mereka sendiri. Ini mirip dengan mendeklarasikan tipe generik, tetapi lingkup parameter tipe terbatas pada metode yang dideklarasikan. Metode statis dan non-statis generik diperbolehkan, serta konstruktor kelas generik.

Sintaks untuk metode generik termasuk parameter tipe, kurung sudut dalam, dan muncul sebelum tipe pengembalian metode. Untuk metode generik, bagian parameter tipe harus muncul sebelum tipe pengembalian metode.

 class Util {
    // Generic static method
    public static <K, V, Z, Y> boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

 class Pair<K, V> {

    private K key;
    private V value;
}

Berikut <K, V, Z, Y>ini adalah deklarasi jenis yang digunakan dalam argumen metode yang harus sebelum jenis kembali yang ada di booleansini.

Di bawah ini; deklarasi tipe <T>tidak diperlukan pada level metode, karena sudah dideklarasikan di level kelas.

class MyClass<T> {
   private  T myMethod(T a){
       return  a;
   }
}

Tetapi di bawah ini salah karena parameter tipe tingkat kelas K, V, Z, dan Y tidak dapat digunakan dalam konteks statis (metode statis di sini).

class Util <K, V, Z, Y>{
    // Generic static method
    public static  boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

SKENARIO VALID LAINNYA ADALAH

class MyClass<T> {

        //Type declaration <T> already done at class level
        private  T myMethod(T a){
            return  a;
        }

        //<T> is overriding the T declared at Class level;
        //So There is no ClassCastException though a is not the type of T declared at MyClass<T>. 
        private <T> T myMethod1(Object a){
                return (T) a;
        }

        //Runtime ClassCastException will be thrown if a is not the type T (MyClass<T>).  
        private T myMethod1(Object a){
                return (T) a;
        }       

        // No ClassCastException        
        // MyClass<String> obj= new MyClass<String>();
        // obj.myMethod2(Integer.valueOf("1"));
        // Since type T is redefined at this method level.
        private <T> T myMethod2(T a){
            return  a;
        }

        // No ClassCastException for the below
        // MyClass<String> o= new MyClass<String>();
        // o.myMethod3(Integer.valueOf("1").getClass())
        // Since <T> is undefined within this method; 
        // And MyClass<T> don't have impact here
        private <T> T myMethod3(Class a){
            return (T) a;
        }

        // ClassCastException for o.myMethod3(Integer.valueOf("1").getClass())
        // Should be o.myMethod3(String.valueOf("1").getClass())
    private  T myMethod3(Class a){
        return (T) a;
    }


        // Class<T> a :: a is Class object of type T
        //<T> is overriding of class level type declaration; 
        private <T> Class<T> myMethod4(Class<T> a){
            return  a;
        }
    }

Dan akhirnya metode statis selalu membutuhkan <T>deklarasi eksplisit ; Itu tidak akan berasal dari tingkat kelas Class<T>. Ini karena level Kelas T terikat dengan instance.

Baca juga Pembatasan Generik

Wildcard dan Subtyping

ketik argumen untuk metode generik

Kanagavelu Sugumar
sumber
2
Jawaban saya di wildcard terbatas stackoverflow.com/questions/1368166/…
Kanagavelu Sugumar
"Kelas <T> mewakili objek kelas dari tipe kelas spesifik 'T'." Itu memang masuk akal. Terima kasih ..
bynu022
Jawaban ini sangat membingungkan dalam cara menggunakan Kelas (dari sekolah) dalam pertanyaan tentang Kelas (di Jawa). Sulit untuk mengetahui apa yang penulis bicarakan dari satu kalimat ke kalimat lainnya.
Echox
@Echox Maaf, saya dapat memperbaikinya, jika Anda memiliki pertanyaan khusus.
Kanagavelu Sugumar
32

Dari Dokumentasi Java:

[...] Lebih mengejutkan, kelas Kelas telah dibuat. Literal kelas sekarang berfungsi sebagai token tipe, menyediakan informasi tipe run-time dan compile-time. Ini memungkinkan gaya pabrik statis yang dicontohkan oleh metode getAnnotation di antarmuka AnnotatedElement yang baru:

<T extends Annotation> T getAnnotation(Class<T> annotationType); 

Ini adalah metode generik. Itu menyimpulkan nilai parameter tipe T dari argumennya, dan mengembalikan instance T yang tepat, seperti yang diilustrasikan oleh cuplikan berikut:

Author a = Othello.class.getAnnotation(Author.class);

Sebelum menggunakan obat generik, Anda harus menyerahkan hasilnya kepada Penulis. Anda juga tidak akan memiliki cara untuk membuat kompiler memeriksa bahwa parameter aktual mewakili subkelas Anotasi. [...]

Yah, saya tidak pernah harus menggunakan barang seperti ini. Siapa saja?

raupach
sumber
2
Aku pikir aku melakukannya. Kerangka kerja (macam) yang saya gunakan mengharuskan Anda untuk lulus dari classname layanan yang bergantung pada modul Anda. Saya membangun lapisan di atasnya yang mengambil objek Class, untuk membatasi jumlah pilihan. Menggunakan Class<? extends X>notasi, saya pikir saya bisa membatasi hanya untuk tipe 'layanan'. Kecuali tidak ada tipe 'layanan' yang umum, jadi saya hanya bisa melakukannya dengan Class<?>. Sayang.
fwielstra
9

Saya menemukan class<T>berguna ketika saya membuat layanan pencarian registri. Misalnya

<T> T getService(Class<T> serviceClass)
{
    ...
}
Kire Haglin
sumber
5

Seperti jawaban lain tunjukkan, ada banyak dan alasan bagus mengapa ini classdibuat generik. Namun ada banyak kali Anda tidak memiliki cara untuk mengetahui jenis generik yang akan digunakan Class<T>. Dalam kasus ini, Anda bisa mengabaikan peringatan gerhana kuning atau Anda dapat menggunakan Class<?>... Begitulah cara saya melakukannya;)

bruno conde
sumber
@SuppressWarnings("unchecked")datang untuk menyelamatkan! (Berhati-hatilah untuk selalu menerapkannya pada ruang lingkup sekecil mungkin karena hal itu mengaburkan masalah potensial dalam kode Anda.)
Donal Fellows
4

Mengikuti jawaban @Kire Haglin, contoh lebih lanjut dari metode generik dapat dilihat dalam dokumentasi untuk JAXB unmarshalling :

public <T> T unmarshal( Class<T> docClass, InputStream inputStream )
         throws JAXBException {
  String packageName = docClass.getPackage().getName();
  JAXBContext jc = JAXBContext.newInstance( packageName );
  Unmarshaller u = jc.createUnmarshaller();
  JAXBElement<T> doc = (JAXBElement<T>)u.unmarshal( inputStream );
  return doc.getValue();
}

Ini memungkinkan unmarshaluntuk mengembalikan dokumen tipe pohon konten JAXB yang sewenang-wenang.

rebus
sumber
2

Anda sering ingin menggunakan wildcard Class. Sebagai contoh,, Class<? extends JComponent>akan memungkinkan Anda untuk menentukan bahwa kelas tersebut adalah beberapa subkelas dari JComponent. Jika Anda telah mengambil Classinstance dari Class.forName, maka Anda dapat menggunakan Class.asSubclassuntuk melakukan casting sebelum mencoba, katakanlah, membangun sebuah instance.

Tom Hawtin - tackline
sumber
2

Dalam java <T>berarti kelas Generik. Kelas Generik adalah kelas yang dapat bekerja pada semua jenis tipe data atau dengan kata lain kita dapat mengatakan itu adalah tipe data yang independen.

public class Shape<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

Di mana T berarti tipe. Sekarang ketika Anda membuat instance dari kelas Shape ini, Anda perlu memberi tahu kompiler untuk tipe data apa yang akan dikerjakan.

Contoh:

Shape<Integer> s1 = new Shape();
Shape<String> s2 = new Shape();

Integer adalah tipe dan String juga tipe.

<T>secara khusus singkatan dari tipe generik. Menurut Java Docs - Tipe generik adalah kelas generik atau antarmuka yang parameternya lebih dari tipe.

Frostbyte1010
sumber
0

Sebagai contoh lain, versi generik dari Class ( Class<T>) memungkinkan seseorang untuk menulis fungsi-fungsi umum seperti yang di bawah ini.

public static <T extends Enum<T>>Optional<T> optionalFromString(
        @NotNull Class<T> clazz,
        String name
) {
    return Optional<T> opt = Optional.ofNullable(name)
            .map(String::trim)
            .filter(StringUtils::isNotBlank)
            .map(String::toUpperCase)
            .flatMap(n -> {
                try {
                    return Optional.of(Enum.valueOf(clazz, n));
                } catch (Exception e) {
                    return Optional.empty();
                }
            });
}
zeronone
sumber
-2

Ini membingungkan pada awalnya. Tetapi ini membantu dalam situasi di bawah ini:

class SomeAction implements Action {
}

// Later in the code.
Class<Action> actionClass = Class.forName("SomeAction"); 
Action action = actionClass.newInstance();
// Notice you get an Action instance, there was no need to cast.
fastcodejava
sumber
4
Bukankah itu hanya cara yang sangat rumit untuk mengatakan Aksi a = Aksi baru ()?
Karl
1
Action a = new Action() ? Actioni an interface, ini adalah SomeActionkami mencoba untuk mendapatkan contoh. Kami hanya memiliki nama yang SomeActiontersedia saat runtime.
fastcodejava
Ini tidak lulus pemeriksaan ketik: java compiler tidak memiliki cara untuk mengatakan bahwa <code> Class.forName ("SomeAction") </code> akan menjadi tipe <code>Class<Action> </code>, karena ini hanya akan dikenal saat runtime.
tonio
@tonio, benar, jadi Anda mungkin harus membungkus baris pertama dalam semacam coba / tangkap. Tetapi dengan asumsi tidak ada pengecualian yang dilemparkan ke sana, baris kedua dijamin akan berfungsi.
MatrixFrog
2
Apa yang benar-benar Anda gambarkan adalah SomeAction.class cocok dengan pola Class <? extends Action> - yaitu, jika Anda memiliki metode useAction (Kelas <? extends Action> klass), Anda dapat memanggil useAction (SomeAction.class).
Thomas Andrews
-5

Cukup gunakan kelas daging sapi:

public <T> T beefmarshal( Class<beef> beefClass, InputBeef inputBeef )
     throws JAXBException {
     String packageName = docClass.getPackage().getBeef();
     JAXBContext beef = JAXBContext.newInstance( packageName );
     Unmarshaller u = beef.createBeef();
     JAXBElement<T> doc = (JAXBElement<T>)u.beefmarshal( inputBeef );
     return doc.getBeef();
}
yaa
sumber
4
Ini tidak benar-benar memberikan jawaban penuh untuk pertanyaan itu. Jika Anda merasa ada sesuatu untuk ditambahkan, edit jawaban ini.
MasterAM