Galileo Computing < openbook >Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Eigene Klassen schreiben
6 Exceptions
7 Äußere.innere Klassen
8 Besondere Klassen der Java SE
9 Generics<T>
10 Architektur, Design und angewandte Objektorientierung
11 Die Klassenbibliothek
12 Einführung in die nebenläufige Programmierung
13 Einführung in Datenstrukturen und Algorithmen
14 Einführung in grafische Oberflächen
15 Einführung in Dateien und Datenströme
16 Einführung in die <XML>-Verarbeitung mit Java
17 Einführung ins Datenbankmanagement mit JDBC
18 Bits und Bytes und Mathematisches
19 Die Werkzeuge des JDK
A Die Klassenbibliothek
Stichwort

Download:
- openbook, ca. 24,5 MB
- Aufgaben, ca. 1,1 MB
- Programme, ca. 12,8 MB
Buch bestellen
Ihre Meinung?

Spacer
Java ist auch eine Insel von Christian Ullenboom
Das umfassende Handbuch
Buch: Java ist auch eine Insel

Java ist auch eine Insel
Galileo Computing
1308 S., 10., aktualisierte Auflage, geb., mit DVD
ca. 49,90 Euro, ISBN 978-3-8362-1802-3
Pfeil9 Generics<T>
Pfeil9.1 Einführung in Java Generics
Pfeil9.1.1 Mensch versus Maschine: Typprüfung des Compilers und der Laufzeitumgebung
Pfeil9.1.2 Taschen
Pfeil9.1.3 Generische Typen deklarieren
Pfeil9.1.4 Generics nutzen
Pfeil9.1.5 Diamonds are forever
Pfeil9.1.6 Generische Schnittstellen
Pfeil9.1.7 Generische Methoden/Konstruktoren und Typ-Inferenz
Pfeil9.2 Umsetzen der Generics, Typlöschung und Raw-Types
Pfeil9.2.1 Realisierungsmöglichkeiten
Pfeil9.2.2 Typlöschung (Type Erasure)
Pfeil9.2.3 Probleme aus der Typlöschung
Pfeil9.2.4 Raw-Type
Pfeil9.3 Einschränken der Typen über Bounds
Pfeil9.3.1 Einfache Einschränkungen mit extends
Pfeil9.3.2 Weitere Obertypen mit &
Pfeil9.4 Typparameter in der throws-Klausel *
Pfeil9.4.1 Deklaration einer Klasse mit Typvariable <E extends Exception>
Pfeil9.4.2 Parametrisierter Typ bei Typvariable <E extends Exception>
Pfeil9.5 Generics und Vererbung, Invarianz
Pfeil9.5.1 Arrays sind invariant
Pfeil9.5.2 Generics sind kovariant
Pfeil9.5.3 Wildcards mit ?
Pfeil9.5.4 Bounded Wildcards
Pfeil9.5.5 Bounded-Wildcard-Typen und Bounded-Typvariablen
Pfeil9.5.6 Das LESS-Prinzip
Pfeil9.5.7 Enum<E extends Enum<E>> *
Pfeil9.6 Konsequenzen der Typlöschung: Typ-Token, Arrays und Brücken *
Pfeil9.6.1 Typ-Token
Pfeil9.6.2 Super-Type-Token
Pfeil9.6.3 Generics und Arrays
Pfeil9.6.4 Brückenmethoden

Galileo Computing - Zum Seitenanfang

9.2 Umsetzen der Generics, Typlöschung und Raw-TypesZur nächsten Überschrift

Zum Verständnis der Generics und um zu erfahren, was zur Laufzeit an Informationen vorhanden ist, lohnt es sich, sich anzuschauen, wie der Compiler Generics in Bytecode übersetzt.


Galileo Computing - Zum Seitenanfang

9.2.1 RealisierungsmöglichkeitenZur nächsten ÜberschriftZur vorigen Überschrift

Im Allgemeinen gibt es zwei Möglichkeiten, um generische Typen zu realisieren:

  • Heterogene Variante: Für jeden Typ (etwa String, Integer, Point) wird individueller Code erzeugt, also drei Klassendateien. Die Variante nennt sich auch Code-Spezialisierung.
  • Homogene Übersetzung: Aus der parametrisierten Klasse wird eine Klasse erzeugt, die anstelle des Typparameters nur Object einsetzt. Für den konkreten Typparameter werden Typanpassungen in die Anweisungen eingebaut.
  • Java nutzt die homogene Übersetzung, und der Compiler erzeugt nur eine Klassendatei. Es gibt keine multiplen Kopien der Klasse – weder im Bytecode noch im Speicher.

Galileo Computing - Zum Seitenanfang

9.2.2 Typlöschung (Type Erasure)Zur nächsten ÜberschriftZur vorigen Überschrift

Übersetzt der Java-Compiler die generischen Anwendungen, so löscht er dabei alle Typinformationen, da die Java-Laufzeitumgebung keine Generics im Typsystem hat. Das nennt sich Typlöschung (engl. type erasure). Wir können uns das so vorstellen, dass alles wegfällt, was in spitzen Klammen steht, und dass jede Typvariable zu Object wird.[170](Sind Bounds im Spiel – eine Typeinschränkung, die später noch vorgestellt wird –, wird ein präziserer Typ statt Object genutzt.)

Tabelle 9.5: Generische Klasse im Quellcode und wie sie nach der Typlöschung aussieht

Mit Generics Nach der Typlöschung

public class Pocket<T>
{
private T value;
public void set( T value ) {
this.value = value; }

public T get() { return value; }
}

public class Pocket
{
private Object value;
public void set( Object value ) {
this.value = value; }

public Object get() { return value; }
}

So entspricht der Programmcode nach der Typlöschung genau dem, was wir selbst auch ohne Generics am Anfang programmiert haben. Auch bei der Nutzung wird gelöscht:

Tabelle 9.6: Nutzung generischer Klassen und wie es nach der Typlöschung aussieht

Mit Generics Nach der Typlöschung

Pocket<Integer> p  = new Pocket<Integer>( 1 );
p.set( 1 );
Integer i = p.get();

Pocket p = new Pocket( 1 );
p.set( 1 );
Integer i = (Integer) p.get();

Beim Herausholen über get() fügt der Compiler genau die explizite Typanpassung ein, die wir in unserem ersten Beispiel noch von Hand eingesetzt haben.

Aber...

Wenn der Compiler Bytecode erzeugt, der auch für ältere JVMs keine Probleme bereitet, so stellt sich die Frage, wo denn die Informationen abgespeichert sind, dass ein Typ generisch deklariert wurde oder nicht. Irgendwo muss das stehen, denn der Compiler weiß das ja. Die Antwort ist, dass der Compiler diese Typinformationen, die nicht Teil des Typsystems der JVM sind, als Signatur-Attribute in den Konstantenpool des Bytecodes legt. Das Attribut ist ein UTF-8 Text, der von älteren Compilern als Kommentar überlesen wird. Mit dem Diassembler javap und dem Schalter –verbose lassen sich diese Informationen anzeigen. Interessierte bekommen weitere Informationen unter: http://java.sun.com/docs/books/jvms/second_edition/jvms-clarify.html.

Das große Ziel: Interoperabilität

Interoperabilität stand bei der Einführung der Generics ganz oben auf der Wunschliste. Zwei wichtige Anforderungen sind:

  • Die neuen mit Generics deklarierten Klassen – wie List<E> – müssen auf jeden Fall noch von altem Programmcode, der zum Beispiel mit einem Java 1.4-Compiler erzeugt wurde, nutzbar sein. Das funktioniert so, dass generisch deklarierte Klassen im Bytecode für einen »alten« Compiler bzw. Laufzeitumgebung so aussehen, als gäbe es keine Generics. Wir sprechen von Typlöschung. Hätte Sun sich nicht dieses Kompatibilitätsziel auf die Fahnen geschrieben, hätte die Umsetzung auch anders ausfallen können. Denn die Konsequenz der Typlöschung ist, dass es keine Informationen über den Typparameter zur Laufzeit gibt. Das führt zu Überraschungen und Einschränkungen (insbesondere bei Arrays), die wir uns gleich anschauen werden. Was wir hier vor uns haben, ist der Wunsch nach Bytecode-Kompatibilität.
  • Auf der anderen Seite gibt es neben der Bytecode-Kompatibiliät auch noch die Quellcode-Kompatibilität. Alter Programmcode, der zum Beispiel Listen als List list; statt zum Beispiel als List<String> list; nutzt, soll immer noch übersetzbar sein, auch wenn er die überarbeiteten Datenstrukturen nicht generisch nutzt. Warnungen sind akzeptabel, aber keine Compilerfehler. Es gibt Millionen Zeilen alten Quellcodes, die Listen ohne Generics nutzen, ohne dass sofort ein Team alle Programmstellen anfasst und Typparameter einführt.
Java Generics und C++-Templates

Java Generics gehen bei den Typbeschreibungen weit über das hinaus, was C++-Templates bieten. In C++ kann ein beliebiger Typparameter eingesetzt werden – was zu unglaublichen Fehlermeldungen führt. Der C++-Compiler führt somit eher eine einfache Ersetzung durch. Doch durch die heterogene Umsetzung generiert der C++-Compiler für jeden genutzten Template-Typ unterschiedlichen (und wunderbar optimierten) Maschinencode. Im Fall von Java würde die heterogene Variante zu sehr vielen sehr ähnlichen Klassen führen, die sich nur in ein paar Typanpassungen unterschieden. Und da in Java sowieso nur Referenzen als Typvariablen möglich sind und keine primitiven Typen, ist auch eine besondere Optimierung an dieser Stelle nicht möglich. Durch die Code-Spezialisierung sind aber andere Dinge in C++ machbar, die in Java unmöglich sind, zum Beispiel Template-Metaprogramming. Der Compiler wird in diesem Fall als eine Art Interpreter für rekursive Template-Aufrufe genutzt, um später optimalen Programmcode zu generieren. Das ist funktionale Programmierung mit einem Compiler ...


Galileo Computing - Zum Seitenanfang

9.2.3 Probleme aus der TyplöschungZur nächsten ÜberschriftZur vorigen Überschrift

Typlöschung ist für die Laufzeitumgebung praktisch, weil sie überhaupt nicht an die Generics angepasst werden muss. So sehen zum Beispiel die seit Java 5 generisch deklarierten Datenstrukturen nach dem Übersetzungsvorgang genauso aus wie unter Java 1.4 und sind damit voll kompatibel. Sonst aber stellt die Typlöschung ein riesiges Problem dar, weil die Typinformationen zur Laufzeit nicht vorhanden sind.[171](Dass diese Typinformationen nicht vorliegen, wird auch damit begründet, dass die Laufzeit leiden könnte. Microsoft war das hingegen egal, dort besteht Generizität in der Common Language Runtime (CLR), also auch in der Laufzeitumgebung. Microsoft ist damit einen klaren Schritt voraus. Doch gab es Generics (Parametric Polymorphism ist der offizielle Name) auch wie in Java nicht von Anfang an; es zog erst in Version 2 in die Sprache und CLR ein. Die alten Datenstrukturen wurden einfach als veraltet markiert, und die Entwickler waren gezwungen, auf die neuen generischen Varianten umzusteigen.)

Reified Generics

Für Java 7 stand auf der Aufgabenliste, die generischen Parameter auch zur Laufzeit zugänglich zu machen. Das wurde jedoch verschoben und kommt vielleicht irgendwann, in Java 8, Java 9, Java 2020, ... Das Stichwort dazu ist Reified Generics, also generische Informationen, die auch zur Laufzeit komplett zugänglich sind.

Kein new T

Da durch die Typlöschung bei Deklarationen wie Pocket<T> die Parametervariable durch Object ersetzt wird, lässt sich zum Beispiel in der Tasche nicht Folgendes schreiben, um ein neues Exemplar eines Tascheninhalts zu erzeugen:

Tabelle 9.7: Warum »new T()« nicht funktionieren kann: nur ein »new Object()« würde gebildet

Gedacht: Mit Generics (Compilerfehler!) Konsequenz aus Typlöschung

class Pocket<T>
{
T newPocketContent() {
return new T(); }
}

class Pocket<T>
{
Object newPocketContent() {
return new Object(); }
}

Als Aufrufer von newPocketContent() erwarten wir aber nicht immer ein lächerliches Object, sondern ein Objekt vom Typ T.

Kein instanceof

Der instanceof-Operator ist bei parametrisierten Typen ungültig, auch wenn das praktisch wäre, um zum Beispiel aufgrund der tatsächlichen Typen eine Fallunterscheidung vornehmen zu können:

void printType( Pocket<?> p )
{
if ( p instanceof Pocket<Number> ) // Fehler illegal generic type for instanceof
System.out.println( "Pocket mit Number" );
else if ( p instanceof Pocket<String> ) // Fehler illegal generic type for instanceof
System.out.println( "Pocket mit String" );
}

Der Compiler meldet zu Recht einen Fehler – nicht nur eine Warnung –, weil es die Typen Pocket<String> und Pocket<Number> zur Laufzeit gar nicht gibt: Es sind nur typgelöschte Pocket-Objekte. Nach der Typlöschung würde unsinniger Code entstehen:

void printType( Pocket p )
{
if ( p instanceof Pocket )
...
else if ( p instanceof Pocket )
...
}

Keine Typanpassungen auf parametrisierten Typ

Typanpassungen wie

Pocket<String> p = (Pocket<String>) new Pocket<Integer>(); // Fehler Compilerfehler

sind illegal. Wir haben ja extra Generics, damit der Compiler die Typen testet. Und durch die Typlöschung verschwindet der Typparameter, sodass der Compiler Folgendes erzeugen würde:

Pocket p = (Pocket) new Pocket();

Kein .class für generische Typen und keine Class-Objekte mit Typparameter zur Laufzeit

Ein hinter einen Typ gesetztes .class liefert das Class-Objekt zum jeweiligen Typ.

Class<Object> objectClass = Object.class;
Class<String> stringClass = String.class;

Class selbst ist als generischer Typ deklariert.

Bei generischen Typen ist das .class nicht erlaubt. Zwar ist noch (mit Warnung) Folgendes gültig:

Class<Pocket> pocketClass = Pocket.class;

aber dies nicht mehr:

Class<Pocket<String>> pocketClass = Pocket<String>.class;   // Fehler Compilerfehler

Der Grund ist die Typlöschung: Alle Class-Objekte für einen Typ sind gleich und haben zur Laufzeit keine Information über den Typparameter:

Pocket<String>  p1 = new Pocket<String>();
Pocket<Integer> p2 = new Pocket<Integer>();
System.out.println( p1.getClass() == p2.getClass() ); // true

Alle Exemplare von generischen Typen werden zur Laufzeit vom gleichen Class-Objekt repräsentiert. Hinter Pocket<String> und Pocket<Integer> steckt also immer nur Pocket. Kurz gesagt: Alles in spitzen Klammern verschwindet zur Laufzeit.

Keine generischen Ausnahmen

Grundsätzlich ist eine Konstruktion wie class MyClass<T> extends SuperClass erlaubt. Aber der Compiler enthält eine spezielle Regel, die verhindert, dass eine generische Klasse Throwable (Exception und Error sind Unterklassen von Throwable) erweitern kann. Wäre zum Beispiel

class MyException<T> extends Exception { }   // Fehler Compilerfehler

erlaubt, könnte im Quellcode vielleicht ein

try { }
catch ( MyException<Typ1> e ) { }
catch ( MyException<Typ2> e ) { }

stehen, doch durch die Typlöschung würde das auf zwei identische catch-Blöcke hinauslaufen, was nicht erlaubt ist.

Keine statischen Eigenschaften

Statische Eigenschaften hängen nicht an einzelnen Objekten, sondern an Klassen. Pocket kann zum Beispiel einmal als parametrisierter Typ Pocket<String> und einmal als Pocket<Integer> auftauchen, also als zwei Instanzen. Aber kann Pocket auch eine statische Methode deklarieren, die auf den formalen Typparameter der Klasse zurückgreift? Nein, das geht nicht. Würden wir in Pocket etwa die folgende statische Methode einsetzen

public static boolean isEmpty( T value ) { return value == null; }   // N

so gäbe es bei T die Fehlermeldung »Cannot make a static reference to the non-static type T«.

Statische Variablen und die Parameter/Rückgaben von statischen Methoden sind nicht an ein Exemplar gebunden. Eine Typvariable jedoch, so wie wir sie bisher verwendet haben, ist immer mit dem Exemplar verbunden. Das T für den value ist ja erst immer dann festgelegt, wenn wir zum Beispiel Pocket<String> oder Pocket<Integer> mit einem Exemplar verbinden. Bei Pocket.isEmpty(""); zum Beispiel kann der Compiler nicht wissen, was für ein Typ gemeint ist, da für statische Methodenaufrufe ja keine Exemplare nötig sind, also nie ein parametrisierter Typ festgelegt werde. Das Nutzen von Code wie Pocket<String>.isEmpty("") führt zu einem Compilerfehler, denn die Syntax ist nicht erlaubt.

Statische generische Methoden sind natürlich möglich, wie wir schon gesehen haben; sie haben dann eine eigene Typvariable.

Kein Überladen mit Typvariablen

Kommt nach der Typlöschung einfach nur Object heraus, kann natürlich keine Methode einmal mit einer Typvariablen und einmal mit Object parametrisiert sein. Folgendes ist nicht erlaubt:

public class Pocket<T>
{
public T value;
public void set( T value ) { this.value = value; }
public void set( Object value ) { this.value = value; } // Fehler Compilerfehler!
}

Der Compiler liefert: »Method set(T) has the same erasure set(Object) as another method in type Pocket<T>«.

Ist der Typ spezieller, also etwa String, sieht das wieder anders aus. Dann taucht die Frage auf, welche Methode bei Pocket<String> aufgerufen wird. Die Leser dürfen das gerne prüfen.

Es lassen sich keine Arrays generischer Klassen bilden

Die Nutzung von Generics bei Arrays schränkt der Compiler ebenfalls ein. Während

Pocket[] pockets = new Pocket[1];

gültig ist und mit einer Warnung versehen wird, führt bei

Pocket<String>[] pockets;                         // (1)
pockets = new Pocket<String>[1]; // (2) Fehler Compilerfehler

nicht die erste, aber die zweite Zeile zum Compilerfehler »Cannot create a generic array of Pocket<String>«.

Typsicher kann das nicht genutzt werden, aber drei schnelle Lösungen sind denkbar:

  • auf Generics ganz zu verzichten und ein @SuppressWarnings("unchecked") an die Feldvariable zu setzen
  • den Typ durch ein Wildcard ersetzen, sodass es etwa zu einem Pocket<?>[] pockets = new Pocket<?>[1]; kommt. Wildcards sind Platzhalter, die später noch detaillierter vorgestellt werden.
  • gleich auf Datenstrukturen der Collection-API umsteigen, bei denen ein Collection<String> pockets = new ArrayList<String>(); keine Probleme bereitet

Als Zusammenfassung lässt sich festhalten, dass Array-Variablen von generischen Typen zwar deklariert (1), dass aber keine Array-Objekte gebaut werden können (2). Mit einem Trick funktioniert es:

class PocketFullOfMoney extends Pocket<BigInteger> {}
Pocket<BigInteger>[] pockets = new PocketFullOfMoney[1];

Hübsch ist das nicht, denn es muss extra eine temporäre Klasse angelegt werden.


Galileo Computing - Zum Seitenanfang

9.2.4 Raw-TypeZur nächsten ÜberschriftZur vorigen Überschrift

Generische Klassen müssen nicht unbedingt parametrisiert werden, doch es ist einleuchtend, dass wir dem Compiler so viel Typinformation wie möglich geben sollten. Auf die Typparameter zu verzichten ist nur für die Rückwärtskompatibilität wichtig, da sonst viele parametrisierte neue Klassen nicht mehr mit altem Programmcode verwendet werden könnten. Wenn zum Beispiel Pocket unter Java 1.4 deklariert und mit den Sprachmitteln von Java 5 zu einem generischen Typ verfeinert wurde, kann es immer noch alten Programmcode geben, der wie folgt aussieht:

Pocket p = new Pocket();       // Gefährlich, wie wir gleich sehen werden
p.set( "Drei Pleitegeier, die Taschen voller Sand" );
String content = (String) p.get();

Ein generischer Typ, der nicht als parametrisierter Typ, also ohne Typargument, genutzt wird, heißt Raw-Type. In unserem Beispiel ist Pocket der Raw-Type von Pocket<T>. Bei einem Raw-Type kann der Compiler die Typkonformität nicht mehr prüfen, denn es ist der Typ nach der Typlöschung; get() liefert Object, und set(Object) kann alles annehmen.

Ein unter Java 1.4 geschriebenes Programm nutzt also nur Raw-Types. Trifft ein Java 5-Compiler auf Programmcode, der einen generischen Typ nicht als parametrisierten Typ nutzt, fängt er an zu meckern, denn er wünscht, dass der Typ generisch verwendet wird.

Abbildung
Abbildung

Abbildung 9.1: Eclipse warnt Raw-Types standardmäßig an

Auch bei set() gibt der Compiler eine Warnung aus, denn er sieht eine Gefahr für die Typsicherheit. Die Methode set() ist so entworfen, dass sie ein Argument von dem Typ akzeptiert, mit dem sie parametrisiert wurde. Fehlt durch die Verwendung des Raw-Types der konkrete Typ, bleibt Object, und der Compiler gibt bei den sonst mit einem Typ präzisierten Methoden eine Warnung aus:

p.set( "Type safety: The method set(Object) belongs to the " +
"raw type Pocket. References to generic type " +
"Pocket<T> should be parameterized" );

Der Hinweis besagt, dass die Tasche hätte typisiert werden müssen. Wenn wir nicht darauf achten, kann das schnell zu Problemen führen:

Pocket<String> p1 = new Pocket<String>();
Pocket p2 = p1; // Compiler-Warnung
p2.set( new java.util.Date() ); // Compiler-Warnung
String string = p1.get(); // Fehler ClassCastException
System.out.println( string );

Der Compiler gibt keinen Fehler, aber Warnungen aus. Die dritte Zeile ist hochgradig problematisch, denn über die nicht parametrisierte Tasche können wir beliebige Objekte eintüten. Da aber das Objekt hinter p2 und dem typgelöschten p1 identisch ist, haben wir ein Typproblem, das zur Laufzeit zu einer ClassCastException führt:

Exception in thread "main" java.lang.ClassCastException: java.util.Date cannot be cast to java.lang.String

Es kann also nur die Empfehlung ausgesprochen werden, Raw-Types in neuen Programmen zu vermeiden, da ihre Verwendung zu Ausnahmen führen kann, die erst zur Laufzeit auffallen.

Typanpassungen

Ein Raw-Type lässt sich automatisch in eine speziellere Form bringen, wobei es natürlich Warnungen vom Compiler gibt.

Pocket p = new Pocket();                     // (1) Warnung
p.set( "Roh macht nicht froh" ); // (2) Warnung
Pocket<String> stringPocket = p; // (3) Warnung
String result = stringPocket.get(); // (4)

Bei der Variablen p, die wir über den Raw-Type nutzen (2), prüft der Compiler gar keine Typen in set(), denn er hat sie ja nie kennengelernt. Zeile (3) verkauft dem Compiler den Raw-Type als parametrisierten Typ. Eine explizite Typanpassung ist nicht nötig, denn Casts sind nur zwischen »echten« Typen gültig, wie Object auf Pocket, nicht aber von Pocket auf Pocket<String>, da Pocket<String> ja der gleiche Class-Typ ist (siehe »Kein .class für generische Typen und keine Class-Objekte mit Typparameter zur Laufzeit«). Eine Anweisung wie (4), die einen Nicht-String-Typ in die Tasche setzt, bringt keinen Fehler zur Übersetzungszeit, und so kann auch über diese Hintertür ein falscher Typ in die Tasche kommen.

Abbildung
Abbildung

Abbildung 9.2: Warnung von Eclipse bei Raw-Types

Annotation SuppressWarnings

In seltenen Fällen muss in den Typ konvertiert werden. Als Beispiel soll cast() dienen:

public <T> T cast( Object obj )
{
return (T) obj; // Compilerwarnung: Type safety: Unchecked cast from Object to T
}

Lässt sich der Cast nicht vermeiden, um dem Compiler den Typ zu geben und ihn somit glücklich zu machen, setzen wir eine @SuppressWarnings-Annotation:

@SuppressWarnings("unchecked")
public <T> T cast( Object obj )
{
return (T) obj;
}

Die Generics bieten uns Möglichkeiten, den Quellcode sicherer zu machen. Wir sollten diese Sicherheit nicht durch Raw-Types kaputtmachen.

Abbildung
Unter den Preferences von Eclipse können drei Typen von Hinweisen für die Nutzung von Raw-Types angegeben werden: Der Compiler gibt einen harten Compilerfehler aus, eine Warnung, oder er ignoriert sie. Dass er Warnungen ausgibt, ist voreingestellt, und diese Vorgabe ist ganz gut.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.







<< zurück
  Zum Katalog
Zum Katalog: Java ist auch eine Insel





Java ist auch eine Insel
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Java 7 – Mehr als eine Insel





 Java 7 –
 Mehr als eine Insel


Zum Katalog: Android 3






 Android 3


Zum Katalog: Android-Apps entwickeln






 Android-Apps
 entwickeln


Zum Katalog: NetBeans Platform 7






 NetBeans
 Platform 7


Zum Katalog: Einstieg in Eclipse 3.7






 Einstieg in
 Eclipse 3.7


Zum Katalog: Einstieg in Java






 Einstieg
 in Java


Zum Katalog: Einstieg in Java 7






 Einstieg in
 Java 7


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2011
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de