Rheinwerk Computing < openbook > Rheinwerk 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 Objektorientierte Beziehungsfragen
7 Ausnahmen müssen sein
8 Äußere.innere Klassen
9 Besondere Typen der Java SE
10 Generics<T>
11 Lambda-Ausdrücke und funktionale Programmierung
12 Architektur, Design und angewandte Objektorientierung
13 Die Klassenbibliothek
14 Einführung in die nebenläufige Programmierung
15 Einführung in Datenstrukturen und Algorithmen
16 Einführung in grafische Oberflächen
17 Einführung in Dateien und Datenströme
18 Einführung ins Datenbankmanagement mit JDBC
19 Einführung in <XML>
20 Testen mit JUnit
21 Bits und Bytes und Mathematisches
22 Die Werkzeuge des JDK
A Java SE Paketübersicht
Stichwortverzeichnis

Download:
- Beispielprogramme, ca. 20,0 MB
- Übungsaufgaben, ca. 1,8 MB
- Musterlösungen, ca. 0,8 MB

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenbloom
Das umfassende Handbuch
Buch: Java ist auch eine Insel

Java ist auch eine Insel
Rheinwerk Computing
1306 Seiten, gebunden, 11. Auflage
49,90 Euro, ISBN 978-3-8362-2873-2
Pfeil 11 Lambda-Ausdrücke und funktionale Programmierung
Pfeil 11.1 Code = Daten
Pfeil 11.2 Funktionale Schnittstellen und Lambda-Ausdrücke im Detail
Pfeil 11.2.1 Funktionale Schnittstellen
Pfeil 11.2.2 Typ eines Lambda-Ausdrucks ergibt sich durch Zieltyp
Pfeil 11.2.3 Annotation @FunctionalInterface
Pfeil 11.2.4 Syntax für Lambda-Ausdrücke
Pfeil 11.2.5 Die Umgebung der Lambda-Ausdrücke und Variablenzugriffe
Pfeil 11.2.6 Ausnahmen in Lambda-Ausdrücken
Pfeil 11.2.7 Klassen mit einer abstrakten Methode als funktionale Schnittstelle? *
Pfeil 11.3 Methoden-Referenz
Pfeil 11.3.1 Varianten von Methoden-Referenzen
Pfeil 11.4 Konstruktor-Referenz
Pfeil 11.4.1 Standard- und parametrisierte Konstruktoren
Pfeil 11.4.2 Nützliche vordefinierte Schnittstellen für Konstruktor-Referenzen
Pfeil 11.5 Implementierung von Lambda-Ausdrücken *
Pfeil 11.6 Funktionale Programmierung mit Java
Pfeil 11.6.1 Programmierparadigmen: imperativ oder deklarativ
Pfeil 11.6.2 Funktionale Programmierung und funktionale Programmiersprachen
Pfeil 11.6.3 Funktionale Programmierung in Java am Beispiel vom Comparator
Pfeil 11.6.4 Lambda-Ausdrücke als Funktionen sehen
Pfeil 11.7 Funktionale Schnittstelle aus dem java.util.function-Paket
Pfeil 11.7.1 Blöcke mit Code und die funktionale Schnittstelle java.util.function.Consumer
Pfeil 11.7.2 Supplier
Pfeil 11.7.3 Prädikate und java.util.function.Predicate
Pfeil 11.7.4 Funktionen und die allgemeine funktionale Schnittstelle java.util.function.Function
Pfeil 11.7.5 Ein bisschen Bi …
Pfeil 11.7.6 Funktionale Schnittstellen mit Primitiven
Pfeil 11.8 Optional ist keine Nullnummer
Pfeil 11.8.1 Optional-Typ
Pfeil 11.8.2 Primitive optionale Typen
Pfeil 11.8.3 Erstmal funktional mit Optional
Pfeil 11.9 Was ist jetzt so funktional?
Pfeil 11.10 Zum Weiterlesen
 
Zum Seitenanfang

11.8Optional ist keine Nullnummer Zur vorigen ÜberschriftZur nächsten Überschrift

Java hat eine besondere Referenz, die Entwicklern die Haare zu Berge stehen lässt und die Grund für lange Debug-Stunden ist: die null-Referenz. Eigentlich sagt null nur aus: »nicht initialisiert«. Doch was null so problematisch macht, ist die NullPointerExcpetion, die durch referenzierte null-Ausdrücke ausgelöst wird.

[zB]Beispiel

Entwickler haben vergessen, das Attribut location mit einem Objekt zu initialisieren, sodass setLocation(…) fehlschlagen wird:

class Place {
private Point2D location;
public void setLocation( double longitude, double latitude ) {
location.setLocation( longitude, latitude ); // NullPointerException
}
}

Einsatz von null

Fehler dieser Art sind durch Tests relativ leicht aufzuspüren. Aber hier liegt nicht das Problem. Das eigentliche Problem ist, dass Entwickler allzu gerne die typenlose null[ 203 ] als magischen Sonderwert sehen, sodass sie neben »nicht initialisiert« noch etwas anderes bedeutet:

  • Erlaubt die API in Argumenten für Methoden/Konstruktoren null, heißt das meistens »nutze einen Default-Wert« oder »nichts gegeben, ignorieren«.

  • In Rückgaben von Methoden steht null oftmals für »nichts gemacht« oder »keine Rückgabe«. Im Gegensatz dazu kodieren andere Methoden wiederum mit der Rückgabe null, dass eine Operation erfolgreich durchlaufen wurde, und würden sonst zum Beispiel Fehlerobjekte zurückgegeben.[ 204 ]

[zB]Beispiel 1

Die Javadoc Methode getTask(out, fileManager, diagnosticListener, options, classes, compilationUnits) in der Schnittstelle JavaCompiler ist so ein Beispiel:

  • out: »a writer for additional output from the compiler; use system.err if null«

  • fileManager: »a file manager; if null use the compiler’s standard filemanager«

  • diagnosticListener: »a diagnostic listener; if null use the compiler’s default method for reporting diagnostics«

  • options: »compiler options, null means no options«

  • classes: »names of classes to be processed by annotation processing, null means no class names«

  • compilationUnits: »the compilation units to compile, null means no compilation units«

Alle Argumente können null sein, getTask(null, null, null, null, null, null) ist also ein korrekter Aufruf. Schön ist die API nicht, und besser wäre sie wohl mit einem Builder-Pattern gelöst.

[zB]Beispiel 2

Der BufferedReader erlaubt das zeilenweise Einlesen aus Datenquellen, und readLine() liefert null, wenn es keine Zeile mehr zu lesen gibt.

[zB]Beispiel 3

Viel Irritation gibt es mit der API vom Assoziativspeicher. Eine gewöhnliche HashMap kann als assoziierten Wert null bekommen, doch get(key) liefert auch dann null, wenn es keinen assoziierten Wert gibt. Das führt zu einer Mehrdeutigkeit, da die Rückgabe von get(…) nicht verrät, ob es eine Abbildung auf null gibt oder ob der Schlüssel nicht vorhanden ist.

Map<Integer, String> map = new HashMap<>();
map.put( 0, null );
System.out.println( map.containsKey( 0 ) ); // true
System.out.println( map.containsValue( null ) ); // true
System.out.println( map.get( 0 ) ); // null
System.out.println( map.get( 1 ) ); // null

Kann die Map null-Werte enthalten, muss es immer ein Paar der Art if(map.containsKey(key)), gefolgt von map.get(key) geben. Am besten verzichten Entwickler auf null in Datenstrukturen.

Da null so viele Einsatzfälle hat und das Lesen der API-Dokumentation gerne übersprungen wird, sollte es zu einigen null-Einsätzen Alternativen geben. Manches Mal ist das einfach, etwa wenn die Rückgabe Sammlungen sind. Dann gibt es mit einer leeren Sammlung eine gute Alternative zu null. Das ist ein Spezialfall des so genannten Null-Object-Patterns und wird in Kapitel 15, »Einführung in Datenstrukturen und Algorithmen«, näher beschrieben.

Fehler, die aufgrund einer NullPointerException entstehen, ließen sich natürlich komplett vermeiden, wenn immer ordentlich auf null-Referenzen getestet würde. Aber gerade die null-Prüfungen werden von Entwicklern gerne vergessen, da ihnen nicht bewusst ist oder sie nicht erwarten, dass eine Rückgabe null sein kann. Gewünscht ist ein Programmkonstrukt, bei dem explizit wird, dass ein Wert nicht vorhanden sein kann, sodass nicht null diese Rolle übernehmen muss. Wenn im Code lesbar ist, dass ein Wert optional ist, also vorhanden sein kann oder nicht, reduziert das Fehler.

Geschichte

Tony Hoare gilt als »Erfinder« der null-Referenz. Heute bereut er es und nennt die Entscheidung »my billion-dollar mistake«.[ 205 ]

 
Zum Seitenanfang

11.8.1Optional-Typ Zur vorigen ÜberschriftZur nächsten Überschrift

Seit Java 8 bietet die Java-API eine Art Container, der ein Element enthalten kann oder nicht. Wenn der Container ein Element enthält, ist es nie null. Dieser Container kann befragt werden, ob er ein Element enthält oder nicht. Eine null als Kennung ist somit überflüssig.

[zB]Beispiel

Optional<String> opt1 = Optional.of( "Aitazaz Hassan Bangash" );
System.out.println( opt1.isPresent() ); // true
System.out.println( opt1.get() ); // Aitazaz Hassan Bangash
Optional<String> opt2 = Optional.empty();
System.out.println( opt2.isPresent() ); // false
// opt2.get() -> java.util.NoSuchElementException: No value present
Optional<String> opt3 = Optional.ofNullable( "Malala" );
System.out.println( opt3.isPresent() ); // true
System.out.println( opt3.get() ); // Malala
Optional<String> opt4 = Optional.ofNullable( null );
System.out.println( opt4.isPresent() ); // false
// opt4.get() -> java.util.NoSuchElementException: No value present

final class java.lang.Optional<T>

  • static <T> Optional<T> empty()Liefert ein leeres Optional-Objekt.

  • boolean isPresent()Liefert wahr, wenn dieses Optional einen Wert hat, sonst ist wie im Fall von empty()die Rückgabe false.

  • static <T> Optional<T> of(T value)Baut ein neues Optional mit einem Wert auf, der nicht null sein darf; andernfalls gibt es eine NullPointerException. null in das Optional hineinzubekommen, geht also nicht.

  • static <T> Optional<T> ofNullable(T value)Erzeugt ein Optional mit dem Wert, wenn dieser ungleich null ist, bei null ist die Rückgabe ein empty()-Optional.

  • T get()
    Liefert den Wert. Handelt es sich um ein Optional empty(), folgt eine NoSuchElementException.

  • T orElse(T other)Liefert den Wert vom Optional und, wenn dieser leer ist, other.

Weiterhin überschreibt Optional die Methoden equals(…), toString() und hashCode() – 0, wenn kein Wert gegeben ist, sonst Hashcode vom Element – und ein paar weitere Methoden, die wir uns später anschauen.

[+]Hinweis

Intern null zu verwenden hat zum Beispiel den Vorteil, dass die Objekte serialisiert werden können. Optional implementiert Serializablenicht, daher sind Optional-Attribute nicht serialisierbar, können also etwa nicht im Fall von Remote-Aufrufen mit RMI übertragen werden. Auch die Abbildung auf XML oder auf Datenbanken ist umständlicher, wenn nicht JavaBean-Properties herangezogen werden, sondern die internen Attribute.

Ehepartner oder nicht?

Optional wird also dazu verwendet, im Code explizit auszudrücken, ob ein Wert vorhanden ist oder nicht. Das gilt auf beiden Seiten: Der Erzeuger muss explizit ofXXX(…) aufrufen und der Nutzer explizit isPresent() oder get(). Beide Seiten sind sich also bewusst, dass sie es mit einem Wert zu tun haben, der optional ist, also existieren kann oder nicht. Wir wollen das in einem Beispiel nutzen und zwar für eine Person, die einen Ehepartner haben kann:

Listing 11.8com/tutego/insel/java/lang/Person.java, Person

public class Person {
private Person spouse;
public void setSpouse( Person spouse ) {
this.spouse = Objects.requireNonNull( spouse );
}
public void removeSpouse() {
spouse = null;
}
public Optional<Person> getSpouse() {
return Optional.ofNullable( spouse );
}
}

In diesem Beispiel ist null für die interne Referenz auf den Partner möglich; diese Kodierung soll aber nicht nach außen gelangen. Daher liefert getSpouse() nicht direkt die Referenz, sondern es kommt Optional zum Einsatz und drückt aus, ob eine Person einen Ehepartner hat oder nicht. Auch bei setSpouse(…) akzeptieren wir kein null, denn null-Argumente sollten soweit wie möglich vermieden werden. Ein Optional ist hier nicht angemessen, weil es ein Fehler ist, null zu übergeben. Zusätzlich sollte natürlich die Javadoc an setSpouse (…) dokumentieren, dass ein null-Argument zu einer NullPointerException führt. Daher passt Optional als Parametertyp nicht.

Listing 11.9com/tutego/insel/java/lang/OptionalDemo.java, main()

Person heinz = new Person();
System.out.println( heinz.getSpouse().isPresent() ); // false
Person eva = new Person();
heinz.setSpouse( eva );
System.out.println( heinz.getSpouse().isPresent() ); // true
System.out.println( heinz.getSpouse().get() ); // com/…/Person
heinz.removeSpouse();
System.out.println( heinz.getSpouse().isPresent() ); // false
 
Zum Seitenanfang

11.8.2Primitive optionale Typen Zur vorigen ÜberschriftZur nächsten Überschrift

Während Referenzen null sein können, und auf diese Weise das Nichtvorhandensein anzeigen, ist das bei primitiven Datentypen nicht so einfach. Wenn eine Methode ein boolean zurückgibt, bleibt neben true und false nicht viel übrig, und ein »nicht zugewiesen« wird dann doch gerne wieder über einen Boolean verpackt und auf null getestet. Gerade bei Ganzzahlen gibt es immer wieder Rückgaben wie –1.[ 206 ] Das ist bei den folgenden Beispielen der Fall:

  • Wenn bei InputStreams read(…) keine Eingaben mehr kommen, wird -1 zurückgegeben.

  • indexOf(Object) von List liefert -1, wenn das gesuchte Objekt nicht in der Liste ist und folglich auch keine Position vorhanden ist.

  • Bei einer unbekannten Bytelänge einer MIDI-Datei (Typ MidiFileFormat) hat getByteLength() als Rückgabe -1.

Diese magischen Werte sollten vermieden werden, und daher kann auch der optionale Typ wieder erscheinen.

Als generischer Typ kann Optional beliebige Typen kapseln, und primitive Werte könnten in Wrapper verpackt werden. Allerdings bietet Java für drei primitive Typen spezielle Optional-Typen an: OptionalInt, OptionalLong, OptionalDouble:

Optional<T>

OptionalInt

OptionalLong

OptionalDouble

static <T>Optional<T> empty()

staticOptionalInt empty()

staticOptionalLong empty()

staticOptionalDouble empty()

T get()

int getAsInt()

long getAsLong()

double getAsDouble()

boolean isPresent()

static <T>Optional<T> of(T value)

static OptionalIntof(int value)

static OptionalLongof(long value)

static OptionalDoubleof(double value)

static <T>Optional<T>ofNullable(T value)

nicht übertragbar

T orElse(T other)

int orElse(int other)

long orElse(long other)

double orElse(double other)

boolean equals(Object obj)

int hashCode()

String toString()

Tabelle 11.12Methodenvergleich zwischen den vier OptionalXXX-Klassen

Die Optional-Methode ofNullable(…) fällt in den primitiven Optional-Klassen natürlich raus. Die optionalen Typen für die drei primitiven Typen haben insgesamt weniger Methoden, und die obere Tabelle ist nicht ganz vollständig. Wir kommen im Rahmen der funktionalen Programmierung in Java noch auf die verbleibenden Methoden wie isPresent(…) zurück.

Best Practice

OptionalXXX-Typen eignen sich hervorragend als Rückgabetyp, sind als Parametertyp denkbar, doch wenig attraktiv für interne Attribute. Intern ist null eine akzeptable Wahl, der »Typ« ist schnell und speicherschonend.

 
Zum Seitenanfang

11.8.3Erstmal funktional mit Optional Zur vorigen ÜberschriftZur nächsten Überschrift

Neben den vorgestellten Methoden wie ofXXX(…), isPresent(), … gibt es weitere, die auf funktionale Schnittstellen zurückgreifen, die wir uns jetzt anschauen wollen:

final class java.lang.Optional<T>

  • void ifPresent(Consumer<? super T> consumer)
    Repräsentiert das Optional einen Wert, rufe den Consumer mit diesem Wert auf, andernfalls mache nichts.

  • Optional<T> filter(Predicate<? super T> predicate)Repräsentiert das Optional einen Wert und das Prädikat predicate auf dem Wert ist wahr, ist die Rückgabe das eigene Optional, sonst ist die Rückgabe Optional.empty().

  • <U> Optional<U> map(Function<? super T,? extends U> mapper)Repräsentiert das Optional einen Wert, dann wende die Funktion an, und verpacke das Ergebnis (wenn es ungleich null ist) wieder in ein Optional. War das Optional ohne Wert, dann ist die Rückgabe Optional.empty(), genauso, wenn die Funktion null liefert.

  • <U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper)Wie map(…), nur dass die Funktion ein Optional statt eines direkten Werts gibt. Liefert die Funktion mapper ein leeres Optional, so ist das Ergebnis von flatMap(…) auch Optional. empty().

  • T orElseGet(Supplier<? extends T> other)
    Repräsentiert das Optional einen Wert, so liefere ihn, ist das Optional leer, so beziehe den Alternativwert aus dem Supplier.

  • <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)Repräsentiert das Optional einen Wert, so liefere ihn, andernfalls hole mit Supplier das Ausnahme-Objekt, und löse es aus.

Beispiel für NullPointerException-sichere Kaskadierung von Aufrufen mit Optional

Die beiden XXXmap(…)-Methoden sind besonders interessant und ermöglichen einen ganz neuen Programmierstil. Warum, soll ein Beispiel zeigen.

Der folgende Zweizeiler gibt auf meinem System »MICROSOFT KERNELDEBUGGER-NETZWERKADAPTER« aus:

String s = NetworkInterface.getByIndex( 2 ).getDisplayName().toUpperCase();
System.out.println( s );

Allerdings ist der Programmcode alles andere als gut, denn NetworkInterface.getByIndex(int) kann null zurückgeben und getDisplayName() auch. Um ohne eine NullPointerException um die Klippen zu schiffen, müssen wir schreiben:

NetworkInterface networkInterface = NetworkInterface.getByIndex( 2 );
if ( networkInterface != null ) {
String displayName = networkInterface.getDisplayName();
if ( displayName != null )
System.out.println( displayName.toUpperCase() );
}

Von der Eleganz des Zweizeilers ist nicht mehr viel geblieben. Integrieren wir Optional (was ja eigentlich ein toller Rückgabetyp für getByIndex() und getDisplayName() wäre):

Optional<NetworkInterface> networkInterface = Optional.ofNullable( NetworkInterface.getByIndex( 2 ) );
if ( networkInterface.isPresent() ) {
Optional<String> name = Optional.ofNullable( networkInterface.get().getDisplayName() );
if ( name.isPresent() )
System.out.println( name.get().toUpperCase() );
}

Mit Optional wird es nicht sofort besser, doch statt if können wir einen Lambda-Ausdruck nehmen und bei ifPresent(…) einsetzen:

Optional<NetworkInterface> networkInterface = Optional.ofNullable( NetworkInterface.getByIndex( 2 ) );
networkInterface.ifPresent( ni -> {
Optional<String> displayName = Optional.ofNullable( ni.getDisplayName() );
displayName.ifPresent( name -> {
System.out.println( name.toUpperCase() );
} );
} );

Wenn wir nun die lokalen Variablen networkInterface und displayName entfernen, kommen wir aus bei:

Optional.ofNullable( NetworkInterface.getByIndex( 2 ) ).ifPresent( ni -> {
Optional.ofNullable( ni.getDisplayName() ).ifPresent( name -> {
System.out.println( name.toUpperCase() );
} );
} );

Von der Struktur her ist das mit der if-Abfrage identisch und über die Einrückungen auch zu erkennen. Fallunterscheidungen mit Optional und ifPresent(…) umzuschreiben bringt also keinen Vorteil.

In Fallunterscheidungen zu denken hilft hier nicht weiter. Was wir uns bei NetworkInterface.getByIndex( 2 ).getDisplayName().toUpperCase() vor Augen halten müssen, ist eine Kette von Abbildungen. NetworkInterface.getByIndex(int) bildet auf NetworkInterface ab, getDisplayName() von NetworkInterface bildet auf String ab, und toUpperCase() bildet von einem String auf einen anderen String ab. Wir verketten also drei Abbildungen und müssten ausdrücken können: Wenn eine Abbildung fehlschlägt, dann höre mit der Abbildung auf. Und genau hier kommen Optional und map(…) ins Spiel. In Code:

Optional<String> s = Optional.ofNullable( NetworkInterface.getByIndex( 2 ) )
.map( ni -> ni.getDisplayName() )
.map( name -> name.toUpperCase() );
s.ifPresent( System.out::println );

Die Klasse Optional hilft uns bei zwei Dingen: Erstens wird map(…) beim Empfangen einer null-Referenz auf ein Optional.empty() abbilden, und zweitens ist das Verketten von leeren Optionals kein Problem, es passiert einfach nichts – Optional.empty().map(…) führt nichts aus, und die Rückgabe ist einfach nur ein leeres Optional. Am Ende der Kette steht also nicht mehr String (wie am Anfang des Beispiels), sondern Optional<String>.

Umgeschrieben mit Methoden-Referenzen und weiter verkürzt ist der Code sehr gut lesbar und Null-Pointer-Exception-sicher:

Optional.ofNullable( NetworkInterface.getByIndex( 2 ) )
.map( NetworkInterface::getDisplayName )
.map( String::toUpperCase )
.ifPresent( System.out::println );

Die Logik kommt ohne externe Fallunterscheidungen aus und arbeitet nur mit optionalen Abbildungen. Das ist ein schönes Beispiel für funktionale Programmierung.

Primitiv-Optionales

Die eigentliche Optional-Klasse ist generisch und kapselt jeden Referenztyp. Auch für die primitiven Typen int, long und double gibt es in drei spezielle Klassen OptionalInt, OptionalLong, OptionalDouble Methoden zur funktionalen Programmierung. Stellen wir die Methoden der vier OptionalXXX-Klassen gegenüber:

Optional<T>

OptionalInt

OptionalLong

OptionalDouble

static <T>Optional<T> empty()

staticOptionalIntempty()

staticOptionalLongempty()

staticOptionalDoubleempty()

T get()

int getAsInt()

long getAsLong()

double getAsDouble()

boolean isPresent()

static <T>Optional<T> of(T value)

static OptionalIntof(int value)

static OptionalLongof(long value)

static OptionalDoubleof(double value)

static <T>Optional<T>ofNullable(T value)

nicht übertragbar

T orElse(T other)

int orElse(int other)

long orElse(long other)

double orElse(double other)

boolean equals(Object obj)

int hashCode()

String toString()

void ifPresent(Consumer<? super T> consumer)

void ifPresent(IntConsumer consumer)

void ifPresent(LongConsumer consumer)

void ifPresent(DoubleConsumer consumer)

T orElseGet(Supplier<? extends T> other)

int orElseGet(IntSupplier other)

long orElseGet(LongSupplier other)

double orElseGet(DoubleSupplier other)

<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)

<X extends Throwable> int orElseThrow(Supplier<X> exceptionSupplier)

<X extends Throwable> long orElseThrow(Supplier<X> exceptionSupplier)

<X extends Throwable> doubleorElseThrow(Supplier<X> exceptionSupplier)

Optional<T> filter(Predicate<? super T> predicate)

nicht vorhanden

<U> Optional<U> flatMap(Function <? super T,Optional<U>> mapper)

<U> Optional<U> map(Function<? super T,? extends U> mapper)

Tabelle 11.13Vergleich von Optional mit den primitiven OptionalXXX-Klassen

 


Ihr Kommentar

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

>> Zum Feedback-Formular
<< 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 SE 8 Standard-Bibliothek
Java SE 8 Standard-Bibliothek


Zum Katalog: Professionell entwickeln mit Java EE 7
Professionell entwickeln mit Java EE 7


Zum Katalog: Schrödinger programmiert Java
Schrödinger programmiert Java


Zum Katalog: Einführung in Java
Einführung in Java


Zum Katalog: Programmieren lernen mit Java
Programmieren lernen mit Java


Zum Katalog: Apps entwickeln für Android 5
Apps entwickeln für Android 5


Zum Katalog: Apps entwickeln mit Android Studio
Apps entwickeln mit Android Studio


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2016
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.

 
Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de