Java'da Class <T> nasıl kullanılır?


247

Generics ve bu sorudaki sahnelerin arkasında gerçekten ne yaptıkları hakkında iyi bir tartışma var , bu yüzden hepimiz Vector<int[]>bunun bir tamsayı dizileri vektörü ve HashTable<String, Person>anahtarları dizeler ve değerler Persons olan bir tablo olduğunu biliyoruz . Ancak, beni zorlayan şey kullanımıdır Class<>.

Java sınıfının Classda bir şablon adı alması gerekiyordu (ya da bana tutulmanın sarı alt çizgisi tarafından söylendi). Oraya ne koymam gerektiğini anlamıyorum. ClassNesnenin bütün amacı, bir nesne, yansıma ve benzeri için tam bilgiye sahip olmadığınız zamandır. Neden Classnesnenin hangi sınıfı tutacağını belirtmemi sağlıyor ? Açıkça bilmiyorum ya da Classnesneyi kullanmazdım, özel olanı kullanırdım.

Yanıtlar:


136

Class sınıfının oluşturulmuş sürümünü kullanmak, diğer şeylerin yanı sıra,

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

ve sonra aldığınız Class nesnesinin genişlediğinden emin olabilirsiniz Collectionve bu sınıfın bir örneği (en azından) bir Koleksiyon olacaktır.


183

Bildiğimiz tek şey, " Herhangi bir sınıfın tüm örnekleri, bu tür bir sınıfın aynı java.lang.Class nesnesini paylaşır "

Örneğin)

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

O zaman a.getClass() == b.getClass()doğrudur.

Şimdi varsayalım

Teacher t = new Teacher();

jenerikler olmadan aşağıdakiler mümkündür.

Class studentClassRef = t.getClass();

Ama bu şimdi yanlış ..?

örneğin) public void printStudentClassInfo(Class studentClassRef) {}ile çağrılabilirTeacher.class

Bu, jenerikler kullanılarak önlenebilir.

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

Şimdi T nedir ?? T, tip parametreleridir (tip değişkenleri olarak da adlandırılır); köşeli parantez (<>) ile sınırlandırılmış, sınıf adını izler.
T, sınıf dosyasının yazılması sırasında bildirilen bir değişken adı (herhangi bir ad olabilir) gibi sadece bir semboldür. Daha sonra T,
başlatma sırasında geçerli Sınıf adıyla değiştirilir ( HashMap<String> map = new HashMap<String>();)

Örneğin) class name<T1, T2, ..., Tn>

Yani Class<T>belirli sınıf türündeki bir sınıf nesnesini temsil eder ' T'

Sınıf yöntemlerinizin aşağıdaki gibi bilinmeyen tür parametreleriyle çalışması gerektiğini varsayın

/**
 * 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; }
}

Burada T, CarName olarak Stringtip olarak kullanılabilir

VEYA T modelNumber olarak Integertip olarak kullanılabilir ,

VEYA T geçerli araba örneği olarak Objecttip olarak kullanılabilir .

Şimdi burada yukarıda çalışma zamanında farklı olarak kullanılabilecek basit POJO var.
Koleksiyonlar örn.) List, Set, Hashmap, T bildirimi uyarınca farklı nesnelerle çalışacak en iyi örneklerdir, ancak T'yi String olarak ilan ettikten sonra,
örneğin) HashMap<String> map = new HashMap<String>();Sadece String Class örnek nesnelerini kabul eder.

Genel Yöntemler

Genel yöntemler, kendi tür parametrelerini tanıtan yöntemlerdir. Bu, genel bir tür bildirmeye benzer, ancak tür parametresinin kapsamı bildirildiği yöntemle sınırlıdır. Statik ve statik olmayan jenerik yöntemlerin yanı sıra genel sınıf yapıcılarına izin verilir.

Genel bir yöntemin sözdizimi bir tür parametresi, iç köşeli parantezler içerir ve yöntemin dönüş türünden önce görünür. Genel yöntemler için, type parametresi bölümü, yöntemin dönüş türünden önce görünmelidir.

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

Burada <K, V, Z, Y>yöntem argümanlar kullanılan türde beyanı olan should olan dönüş türü önce booleanburada.

Aşağıda; <T>sınıf düzeyinde zaten bildirildiğinden, yöntem düzeyinde tür bildirimi gerekli değildir.

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

Ancak, sınıf düzeyinde tip parametreleri K, V, Z ve Y statik bir bağlamda kullanılamadığından (burada statik yöntem) aşağıda yanlıştır.

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

DİĞER GEÇERLİ SENARYOLAR

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

Ve son olarak, Statik yöntem her zaman açık bir <T>beyan gerektirir; Sınıf seviyesinden türetilmeyecektir Class<T>. Bunun nedeni, Sınıf seviyesi T'nin örneğe bağlı olmasıdır.

Ayrıca Jeneriklerle İlgili Kısıtlamalar'ı okuyun

Joker Karakterler ve Alt Türlendirme

genel bir yöntem için argüman yazın


2
Sınırlı wildCards hakkındaki cevabım stackoverflow.com/questions/1368166/…
Kanagavelu Sugumar

"Sınıf <T> anlamlı olan 'T' sınıf türündeki sınıf nesnesini temsil eder." Teşekkür ederim ..
bynu022

Bu cevap, Sınıflar (Java'da) ile ilgili bir soruda Sınıfları (okuldan) kullanma biçiminde çok kafa karıştırıcıdır. Yazarın bir cümleden diğerine ne hakkında konuştuğunu bilmek zor.
Echox

@Echox Üzgünüm, herhangi bir sorunuz varsa geliştirebilirim.
Kanagavelu Sugumar


32

Java Belgelerinden:

[...] Daha şaşırtıcı bir şekilde, Class Class türetildi. Sınıf değişmezleri artık hem çalışma zamanı hem de derleme zamanı türü bilgileri sağlayan tür belirteçleri olarak işlev görür. Bu, yeni AnnotatedElement arabiriminde getAnnotation yöntemiyle örneklenen bir statik fabrika stili sağlar:

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

Bu genel bir yöntemdir. T türü parametresinin değerini bağımsız değişkeninden alır ve aşağıdaki snippet'te gösterildiği gibi uygun bir T örneği döndürür:

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

Jeneriklerden önce, sonucu Yazar'a vermek zorunda kalacaksınız. Ayrıca, derleyicinin gerçek parametrenin Ek Açıklama alt sınıfını temsil ettiğini kontrol etmesini sağlamazsınız. [...]

Asla bu tür şeyleri kullanmak zorunda kalmadım. Kimse?


2
İ yaptığını düşündüm. Birlikte çalıştığım bir çerçeve, modülünüzün bağlı olduğu hizmetlerin sınıf adını geçmenizi gerektiriyordu. Seçimlerin miktarını kısıtlamak için Class nesnelerini alan bir katman oluşturdum. Class<? extends X>Anladım gösterimi kullanarak sadece 'hizmet' türleriyle sınırlayabilirim. Hiçbir ortak 'hizmet' türü dışında, bu yüzden sadece ile yapabilirdi Class<?>. Ne yazık ki.
fwielstra

9

class<T>Hizmet kayıt defteri aramaları oluştururken faydalı buldum . Örneğin

<T> T getService(Class<T> serviceClass)
{
    ...
}

5

Diğer cevapların belirttiği gibi, bunun classjenerik hale getirilmesinin birçok ve iyi nedeni vardır . Bununla birlikte, kullanmak için genel türü bilmenin bir yolu yoktur Class<T>. Bu durumlarda, sarı tutulma uyarılarını görmezden gelebilirsiniz veya kullanabilirsiniz Class<?>... Bunu ben böyle yaparım;)


@SuppressWarnings("unchecked")kurtarmaya geliyor! (Sadece her zaman mümkün olduğunca kullanıcı küçük bir kapsam olarak uygulamak için dikkatli olmak yok Kodunuzdaki belirsiz olası sorunları.)
Donal Fellows

4

@Kire Haglin'in cevabını takiben, jenerik ilaç yöntemlerinin başka bir örneği, JAXB'nin belgelenmesinin belgelenmesinde görülebilir :

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

Bu, unmarshalisteğe bağlı bir JAXB içerik ağacı türünde bir belgenin döndürülmesini sağlar .


2

Genellikle joker karakterler kullanmak istersiniz Class. Örneğin Class<? extends JComponent>, sınıfın bazı alt sınıfları olduğunu belirtmenize izin verir JComponent. Örneği buradan aldıysanız, Classörneğin bir örnek oluşturmaya çalışmadan önce kadroyu yapmak için Class.forNamekullanabilirsiniz Class.asSubclass.


2

Java'da <T>Genel sınıf anlamına gelir. Genel Sınıf, her tür veri türü üzerinde çalışabilen bir sınıftır veya başka bir deyişle veri türünden bağımsız olduğunu söyleyebiliriz.

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

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

Burada T , tip anlamına gelir. Şimdi bu Shape sınıfının bir örneğini oluşturduğunuzda, derleyiciye bunun üzerinde çalışacağı veri türünü söylemeniz gerekir.

Misal:

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

Tamsayı bir tür ve String de bir tür.

<T>özellikle genel tip anlamına gelir. Java Belgelerine Göre - Genel bir tür, türler üzerinden parametrelenen genel bir sınıf veya arabirimdir .


0

Başka bir örnek vermek gerekirse, Class ( Class<T>) ' in genel sürümü , kişinin aşağıdaki gibi genel işlevler yazmasına izin verir.

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

-2

Başlangıçta kafa karıştırıcı. Ancak aşağıdaki durumlarda yardımcı olur:

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.

4
Eylem a = yeni Eylem () söylemenin inanılmaz derecede karmaşık bir yolu değil mi?
Karl

1
Action a = new Action() ? Actioni bir arayüz, SomeActionbiz bir örnek almak için çalışıyoruz. Sadece SomeActionçalışma zamanında kullanılabilir ismimiz var.
fastcodejava

Bu, yazım denetimini geçmez: java derleyicisinin <code> Class.forName ("SomeAction") </code> türünün <code>Class<Action> </code> türünde olduğunu söyleyemez, çünkü bu yalnızca zamanında bilinir.
tonio

@tonio, doğru, bu yüzden muhtemelen ilk satırı bir tür try / catch ile sarmanız gerekiyor. Ancak orada istisna olmadığı varsayılırsa, ikinci hattın çalışması garanti edilir.
MatrixFrog

2
Gerçekten açıkladığınız şey, SomeAction.class sınıf <<deseniyle eşleşiyor? Action> öğesini genişletir, yani useAction (Class <? Action> klass öğesini genişletir) yönteminiz varsa useAction (SomeAction.class) yöntemini çağırabilirsiniz.
Thomas Andrews

-5

Sadece sığır eti sınıfını kullanın:

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

4
Bu gerçekten soruya tam bir cevap vermez. Ekleyeceğiniz bir şey olduğunu düşünüyorsanız bu yanıtı düzenleyin.
MasterAM
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.