2016-04-17 10 views
0

Ich habe eine grundlegende Java-Frage, als ich vor kurzem entschied, in die Sprache zu graben (ich arbeite seit langem ausschließlich mit C++ und beschloss, meinen Horizont ein wenig zu erweitern).Java-Grundlagen über Generika und Reflexion

Unter der Annahme, dass ich eine Klasse namens Bankkonto haben und ich versuche zu erstellen und Objekt in meinem Haupt, was ist der Unterschied zwischen:

Class baCls = BankAccount.class; 

und

Class<BankAccount> baCls = BankAccount.class; 

Was bedeutet die baCls halten in jeder der Fälle und was ist der Compiler ausgegeben?

Antwort

4

In beiden Fällen baCls ist ein Verweis auf die Klasse BankAccount

Der generische fügt eine Kompilierung Prüfzeit hat aber keine Auswirkung zur Laufzeit.

Sie können den erzeugten Bytecode sehen, wenn Sie in Ihrer IDE javap -c -p oder einen Bytecode-Viewer verwenden.

+0

Aha, ich verstehe, danke. Also abgesehen davon gibt es keinen Unterschied und baCls kann in beiden Fällen im Programm in gleicher Weise verwendet werden, ist das richtig? – Cooli

+1

@Cooli der wichtigste Trick, der aus C++ besteht, ist, dass Java nur Primitive und Referenzen hat. Es gibt keine anderen Typen (außer 'void'), weshalb es keine speziellen Symbole gibt, um Ihnen eine Referenz zu zeigen. –

+0

Danke Peter. Ich denke, es wird einige Zeit dauern, bis ich ein wenig von den C++ - Konzepten getrennt bin. – Cooli

0

Peter Lawreys Antwort adressiert Ihre spezifische Frage, aber ich denke, dass es eine wichtige Tatsache fehlt, dass der zweite Fall zusätzliche, unschätzbare Informationen für den Compiler liefert.

Der Unterschied besteht darin, dass der erste Fall ist ein roh Typ:

Class baCls = BankAccount.class; 
    ^Missing type parameters. 

Es gibt (mindestens) zwei Folgen davon (das sind die allgemeinen Folgen der rohen Typen verwenden):

  1. Sie können keine Producer-Methoden wie Class.nextInstance() Methode aufrufen und das Ergebnis einer Referenz vom Typ BankAccount ohne Besetzung zuweisen:

    Class baCls = BankAccount.class; 
    BankAccount instance = (BankAccount) baCls.newInstance(/* required args */); 
    

    jedoch Sie nicht über die Besetzung im zweiten Fall brauchen, weil es bekannt ist, dass die nextInstance Methode wird eine Instanz von BankAccount zurück:

    Class<BankAccount> baClsSafe = BankAccount.class; 
    BankAccount instance = baCls.newInstance(/* req args */); 
    
  2. Sie verlieren Typsicherheit, wenn Sie diese in Sammlungen einfügen. Zum Beispiel:

    Class baCls = BankAccount.class; 
    List<Class<String>> list = new ArrayList<>(); 
    list.add(baCls); // Compiles fine. 
    

    Dies scheint in Ordnung, aber Sie erhalten eine Laufzeitausnahme später, wenn Sie davon ausgehen, dass die Elemente in der Liste sind vom richtigen Typ:

    for (Class<String> clazz : list) { 
        String instance = clazz.newInstance(); // ClassCastException. 
    } 
    

    weil instance ist eigentlich eine Instanz BankAccount (unter der Annahme, dass ein Konstruktor mit null Argumenten vorhanden ist).

    Im zweiten Fall würden Sie nie dieses Element in die Liste an erster Stelle hinzuzufügen, in der Lage:

    Class<BankAccount> baCls = BankAccount.class; 
    List<Class<String>> list = new ArrayList<>(); 
    list.add(baCls); // Compiler error. 
    

Ich würde empfehlen, dass Sie rohe Typen nachlesen, z.B.in der generics tutorial, sowie in Effektive Java 2nd Ed Artikel 23 "Verwenden Sie keine rohen Typen in neuen Code".