Angelika Langer - Training & Consulting
HOME | COURSES | TALKS | ARTICLES | GENERICS | LAMBDAS | IOSTREAMS | ABOUT | CONTACT | Twitter | Lanyrd | Linkedin
 
HOME 

  OVERVIEW

  BY TOPIC
    JAVA
    C++

  BY COLUMN
    EFFECTIVE JAVA
    EFFECTIVE STDLIB

  BY MAGAZINE
    JAVA MAGAZIN
    JAVA SPEKTRUM
    JAVA WORLD
    JAVA SOLUTIONS
    JAVA PRO
    C++ REPORT
    CUJ
    OTHER
 

GENERICS 
LAMBDAS 
IOSTREAMS 
ABOUT 
CONTACT 
Java 7 - JSR 334 - Project Coin - Sprachneuerungen im Zusammenhang mit Generics

Java 7 - JSR 334 - Project Coin - Sprachneuerungen im Zusammenhang mit Generics
Java 7 
JSR 334 - "Project Coin"
(Sprachneuerungen im Zusammenhang mit Generics)
 

Java Magazin, Oktober 2011
Klaus Kreft & Angelika Langer

Dies ist die Überarbeitung eines Manuskripts für einen Artikel, der im Rahmen einer Kolumne mit dem Titel "Effective Java" im Java Magazin erschienen ist.  Die übrigen Artikel dieser Serie sind ebenfalls verfügbar ( click here ).

Die Version 7 von Java hat eine Reihe von kleineren Sprachergänzungen gebracht, die unter dem Namen "Project Coin" entwickelt wurden.  Wir haben im vorangegangenen Beitrag unserer Reihe über Java7-Neuerungen einen Teil dieser Neuerungen angesehen.  Dieses Mal wollen wir die neuen Sprachmittel vorstellen, die Verbesserungen im Zusammenhang mit Generics betreffen.
 

Sprachverbesserungen im Zusammenhang mit Generics

Nachdem die Generics mit Java 5 freigegeben wurden und sie nun seit ca. 6 Jahren benutzt werden, sind einige Defizite aufgefallen, die der javac-Compiler mit vertretbarem Aufwand beheben kann.  Dabei geht es um
(a) eine verbesserte Type Inference für Konstruktoren generischer Typen und um
(b) Probleme im Zusammenspiel von variablen Argumentenlisten und nicht-reifizierbaren Typen.
Beginnen wir mit den Details zu Punkt (a): dem sogenannten Diamond -Operator, der im Zusammenhang mit der Type Inference für Konstruktoren generischer Typen erfunden wurde.

Der Diamond -Operator

Bei der Konstruktion von Objekten eines parametrisierten Typs musste bislang in der new -Expression der Typ des Objekts vollständig mit allen Typparametern angegeben werden:

Beispiel (kompletter Typename):

ThreadLocal<Map<String, List<Integer>>> ref = new ThreadLocal<Map<String, List<Integer>>>();
Die meisten Entwickler finden diese Schreibweise redundant und lästig.  Wozu muss man den gesamten Typ auf der rechten Seite noch einmal hinschreiben, wenn er doch schon auf der linken Seite steht?

Sollte man aus Bequemlichkeit oder Unachtsamkeit die Schreibweise vereinfacht haben, bekommt man vom Compiler allerlei Warnungen.

Beispiel (Raw Type):

ThreadLocal<Map<String, List<Integer>>> ref = new ThreadLocal();
Hier wird auf der rechten Seite - versehentlich oder absichtlich - der Raw Type verwendet.  Der Compiler beschwert sich darüber und gibt all die unchecked -Warnungen aus, die im Zusammenhang mit Raw Types angemessen sind.

In Java 7 gibt es nun den sogenannten Diamond -Operator, der die Schreibweise vereinfacht und den Compiler veranlasst, sich die fehlende Information selber aus dem Kontext des Konstruktoraufrufs zu überlegen.

Beispiel ( Diamond -Operator):

ThreadLocal<Map<String, List<Integer>>> ref = new ThreadLocal<>();
Anstelle des kompletten Typparameters muss nur noch ein leeres Paar von spitzen Klammern geschrieben werden.  Den Rest deduziert der Compiler aus dem Typ der linken Seite der Zuweisung.

Das leere Klammer-Paar wird als Diamond Operator bezeichnet, obwohl er kein Operator im eigentlichen Sinne der Sprache ist, sondern nur ein syntaktisches Konstrukt, das den Raw Type vom parametrisierten Typ ohne Typparameter unterscheidet.

Die Deduktion der fehlenden Typparameter aus dem Kontext nennt man Type Inference .  Type Inference gibt es schon, seit es die Java Generics gibt; sie wird auch im Zusammenhang mit dem Aufruf generischer Methoden verwendet.   Die Type Inference ist nun für Java 7 auf Konstruktoraufrufe ausgedehnt worden (siehe /JSR334/ und /DIAMND/).

Wie so häufig im Zusammenhang mit Generics ist nicht alles so einfach, wie man es sich wünschen würde.  Die Type Inference führt nämlich gelegentlich zu überraschenden Ergebnissen.

Beispiel ("interessante" Type Inference):

Set<Number> s3 = new HashSet<>(Arrays.asList(0L,0L)); // error
Der Compiler hätte für die Type Inference prinzipiell die Wahl, ob er die fehlenden Typparameter für den HashSet<> aus dem Typ des Ausdrucks auf der linken Seite der Zuweisung ableiten will ( Set<Number> => E:=Number ) oder ob er für die Deduktion den Typ des Konstruktorarguments heranzieht.  Der Typ des Konstruktorarguments ist in diesem Beispiel der Returntyp der Arrays.asList() -Methode; diesen Returntyp muss der Compiler auch erst einmal per Type Inference bestimmen, weil die Arrays.asList() -Methode eine generische Methode ist.  Bei der Deduktion für die asList() -Methode kommt E:=Long heraus, so dass das Argument für den Konstruktor vom Typ List<Long> ist.  Der Compiler würde also bei Berücksichtigung des Konstruktorarguments für die Type Inference beim Diamond -Operator E:=Long deduzieren ( List<Long> => E:=Long ).

Welche Strategie wählt nun der Compiler?

Das ist klar geregelt: Für den Compiler haben die Konstruktorargumente Vorrang; er deduziert also E:=Long und dann gibt es eine Fehlermeldung, weil der Set<Number> auf der linken Seite inkompatibel zum HashSet<Long> auf rechten Seite ist.

Das ist vielleicht nicht ganz die Deduktion, die man sich gewünscht hätte, aber es ist eine definierte und auch sinnvolle Deduktionsstrategie.  Die Strategie bei der Type Inference ist nämlich für generische Methoden und den Diamond -Operator identisch:  Der Compiler schaut sich immer erst die Typen der Konstruktor- bzw. Methodenargumente an. Nur wenn er daraus nichts deduzieren kann, schaut er die linke Seite der Zuweisung an.  Letzteres tut er natürlich nur, wenn der Konstruktor- bzw. Methodenaufruf im Kontext einer Zuweisung steht.

Und was ist, wenn der Diamond -Operator nicht im Kontext einer Zuweisung auftaucht? Das wollen wir jetzt an dieser Stelle nicht vertiefen.  Aber auch dafür gibt es Regeln.  Wer sich für die Details der Type Inference interessiert, kann sie in den Generics FAQs (siehe /GENFAQ1/) nachlesen.

Insgesamt führt der Diamond -Operator zu spürbar weniger Code. Gelegentlichen gibt es Überraschungseffekten, wie oben erläutert.

Neben dem Code-Overhead bei Konstruktoraufrufen von generischen Typen hat man in Java 7 versucht, auch die Probleme zu beheben, die sich aus der Kombination von Varargs und Generics ergeben. Beide Sprachmittel, die variable Argumentenliste und die generischen Typen, sind mit Java 5 zur Sprache hinzugekommen. Augenscheinlich hat man die Kombination der beiden neuen Sprachmittel nicht bis in Detail durchdacht, denn bei gewissen Funktionsaufrufen gibt es Probleme.

Varargs und Generics

Unter Varargs versteht man das in Java 5 eingeführte Sprachmittel der variablen Argumentenliste für Methoden und Konstruktoren.  Man findet im JDK zahlreiche Beispiele für deren Verwendung, zum Beispiel die Methode asList() in der Klasse java.util.Arrays :
public static <T> List<T> asList(T... a);
Dort, wo T... spezifiziert ist, kann eine beliebig lange Liste von Argumenten des Typs T übergeben werden, wobei T ein Typparameter, also ein "unbekannter Typ" ist.  Der Compiler erzeugt aus der variablen Argumentenliste ein Array und übergibt dieses Array an die Methode.  In Wirklichkeit, d.h. im Byte Code, ist asList() also eine Methode mit dem Argumenttyp T[] .

Wenn die Aufrufargumente einer Methode mit einer variablen Argumentenliste von einem nicht-reifizierbaren Typ (siehe /GENFAQ2/) sind, dann produziert der Compiler eine unchecked-Warnung.

Nicht-reifizierbare Typen sind Typen, die aufgrund der Type Erasure im Byte-Code keine exakte Darstellung mehr haben.  Zu den nicht-reifizierbaren Typen gehören praktisch alle parametrisierten Typen (wie List<String> , Map<? extends Number,String> , Future<Long> , usw.) sowie alle Typvariablen (wie etwa T im obigen Beispiel).

Schauen wir uns ein Beispiel für die problematische Kombination von Varargs und Generics an:

List<ThreadLocal<String>> list2 = Arrays.asList(new ThreadLocal<String>());
____________________________________________________________________________________
warning: [unchecked] unchecked generic array creation for varargs parameter of type ThreadLocal<String>[]
List< ThreadLocal<String>> list2 = Arrays.asList(new ThreadLocal<String>());
                                                ^
Die Warnung hängt damit zusammen, dass der Compiler aus den übergebenen variablen Argumenten ein Array konstruieren muss.  Dieses Array ist vom Typ ThreadLocal<String>[] .   Der Type ThreadLocal<String> ist nicht-reifizierbar, denn im Byte Code bleibt nach der Type Erasure nur noch  ThreadLocal übrig.   In Java sind aber Arrays mit Elementen eines nicht-reifizierbaren Typs unzulässig.  Warum dies so ist, ist in der Randbemerkung "Heap Pollution im Zusammenhang mit Arrays" beschrieben. Der Compiler erzeugt also ein Array von einem unzulässigen Typ, das er mit einer Fehlermeldung zurückweisen würde, wenn wir selber versuchen würden, ein solches Array zu erzeugen.  Er macht also etwas, das eigentlich nicht sein sollte und das er „normalerweise“ mit einer Fehlermeldung quittieren würde.  In diesem Dilemma gibt er dann eine unchecked-Warnung anstelle einer Fehlermeldung aus.
 

Randbemerkung: Heap Pollution im Zusammenhang mit Arrays 

Warum sind Arrays mit Elementen von einem nicht-reifizierbaren Typ unzulässig?

In Java gibt es eine sogenannte Kovarianzbeziehung zwischen den Array-Typen:  ein Array mit Elementen eines Supertyps gilt als Supertyp eines Arrays mit Elementen eines Subtyps.  Die Super-Subtyp-Beziehung der Elementtypen zieht sich also hoch auf die korrespondierenden Array-Typen.  Das ist anders als bei den generischen Typen, wo es eine solche Kovarianz nicht gibt.

Object[] objArr = new String[10];                // okay : covariance
List<Object> objList = new ArrayList<String>();  // error: no covariance
Aufgrund der Kovarianz zwischen den Arraytypen kann der Compiler bei der Übersetzung nicht prüfen, ob in ein String-Array auch tatsächlich nur Strings eingefüllt werden. Deshalb gibt es zur Laufzeit den sogenannten Array-Store-Check.  Dabei prüft die JVM bei der Zuweisung eines Objekts an eine Array-Position, ob das Objekt vom richtigen Typ ist.  Damit soll garantiert werden, dass ein Array stets nur Elemente des deklarierten (oder eines dazu kompatiblen) Typs enthält.  Wenn das zuzuweisende Element von einem inkompatiblen Typ ist, löst die JVM eine ArrayStoreException aus.
Object[] objArr = new String[10];
objArr[0] = "ABC";  // fine
objArr[1] = 0L;     // ArrayStoreException
Dieser Array-Store-Check ist bei Arrays mit einem nicht-reifizierbaren Elementtyp nicht so präzise möglich, wie er nötig wäre. Hier ist ein Beispiel:
Pair<Integer,Integer>[] intPairArr  = new Pair<Integer,Integer>[10];   // 1: error
Object[] objArr = intPairArr; 
objArr[0] = new Pair<String,String>("","");                            // 2: heap pollution
In Zeile //1 wird ein Array vom Typ Pair<Integer,Integer>[] erzeugt.  Diese Erzeugung wird vom Compiler mit einer Fehlermeldung abgewiesen, weil der Elementtyp ein nicht-reifizierbarer Typ ist.  Um zu verstehen, warum Arrays mit nicht-reifizierbarem Elementtyp unzulässig sind, nehmen wir einmal an, der Compiler ließe uns das eigentlich verbotene Pair<Integer,Integer> -Array erzeugen.

Dann könnte eine Situation wie in Zeile //2 entstehen.  Der Compiler kann in Zeile //2 aufgrund des statischen Typs Object[] nicht wissen, auf welchen Typ von Array die Referenzvariable objArr zur Laufzeit verweisen wird.  Der Compiler akzeptiert daher die Zuweisung und verlässt sich auf den Array-Store-Check der JVM.  Zur Laufzeit ist aber das Pair<Integer,Integer> -Array aufgrund der Type Erasure nur noch ein Pair -Array.  Der Array-Store-Check wird also bei der Prüfung in Zeile 3 ein Pair -Array und ein Pair vorfinden und dies natürlich akzeptieren.  Das Ergebnis ist ein Array, das vom Typ her ein Pair<Integer,Integer> -Array ist, aber de facto ein Element vom fremden Typ Pair<String,String> enthält. 

Eine solche Situation bezeichnet man als Heap Pollution; die Details der Heap Pollution wollen wir an dieser Stelle nicht weiter ausbreiten; sie sind in /GENFAQ3/ beschrieben.  Wir wollen uns lediglich anschauen, was im Falle der Heap Pollution typischerweise passiert. Verfolgen wir zu diesem Zwecke unser obiges Beispiel weiter:

Integer i = intPairArr[0].getFirst();  // ClassCastException
Da die Referenzvariable intPairArr als Pair<Integer,Integer>[] deklariert ist, wird sie sicher irgendwann dazu benutzt werden, um Integer -Paare aus dem Array zu verwenden.  An der Stelle, an der das String -Paar als vermeintliches Integer -Paar benutzt wird, gibt es eine ClassCastException .  Solche unerwarteten ClassCastException s sind die am häufigsten auftretende Konsequenz einer Heap Pollution.

Da die Ursache einer Heap Pollution meistens nur schwer zu finden ist, haben sich die Sprachdesigner dazu entschlossen, Arrays mit nicht-reifizierbarem Elementtyp komplett zu verbieten.
 

Oft ist die Warnung des Compilers unberechtigt, weil die aufgerufene Methode so implementiert ist, dass sie mit dem compiler-generierten Array nichts Problematisches tut.  Ausgegeben wird die Warnung aber immer.  Dummerweise kann nun ausgerechnet der Aufrufer einer Varargs-Methode am allerwenigsten beurteilen, ob die Warnung berechtigt ist oder nicht;  das kann eigentlich nur der Implementierer der Methode beurteilen.

Die Änderung in Java 7 besteht nun darin, dass der Compiler die Warnung nicht mehr nur beim Aufruf der Varargs-Methode gibt, sondern auch schon bei der Definition der Methode (siehe /JSR334/ und /VARARG/).

Hier ist ein Beispiel:

public  static <E> void addAll(List<E> list, E... array) {
   for (E element : array) list.add(element);
}
public static void main(String[] args) {
   addAll(new ArrayList<Pair<String,String>>(),
      new Pair<String,String>("Leonardo","da Vinci"),
      new Pair<String,String>("Vasco","de Gama")
   );
}
______________________________________________________________________________

warning: [unchecked] Possible heap pollution from parameterized vararg type E
        public  static <E> void addAll(List<E> list, E... array) {
                                               ^
  where E is a type-variable:
    E extends Object declared in method <E>addAll(List<E>,E...)

warning: [unchecked] unchecked generic array creation for varargs parameter of type Pair<String,String>[]
            addAll(new ArrayList<Pair<String,String>>(),   // unchecked warning
                  ^

Die neue Warnung bei der Methodendefinition soll den Implementierer der Methode dazu anregen, darüber nachzudenken, ob seine Methode für den Benutzer unproblematisch ist oder ob sie womöglich zur Heap Pollution führen könnte.  Die obige Varargs-Methode ist in der Tat unproblematisch, weil das vom Compiler aus E... erzeugte Array vom Typ E[] nur gelesen, aber nicht modifiziert wird und es auch außerhalb der Methode überhaupt nicht sichtbar und nicht zugänglich ist.
Wenn sich der Implementierer seiner Sache sicher ist, dann kann er an seine Methode die Annotation @SafeVarargs schreiben – mit dem Effekt, dass auch bei der Benutzung der Methode keine Warnung mehr kommt.  Der Implementierer einer Varargs-Methode kann also mit Hilfe dieser Annotation dafür sorgen, dass der Aufrufer seiner Methode nicht mehr mit Warnungen belästigt wird, die er ohnehin nicht beurteilen kann.

Nun sollte der Implementierer die Annotation @SafeVarargs natürlich nur dann benutzen, wenn er sicher weiß, dass auch wirklich keine Typprobleme auftreten können.  Wann ist eine vom Compiler monierte Varargs-Methode problematisch bzw. unproblematisch?  Das ist nicht immer ganz einfach zu beurteilen.

Hier ist ein offensichtliches Beispiel:

Pair<String,String>[] method ( Pair<String,String>... lists) {  //1: Warnung: possible heap pollution
   Object[] objs = lists;
   objs[0] = new Pair<String,String>("x","y");
   objs[1] = new Pair<Long,Long>(0L,0L);                      //2: heap pollution
   return lists;
}
Zu Zeile //1 kommt die Warnung wegen der möglichen Heap-Pollution; in Zeile //2 wird das vom Compiler erzeugte Array mit einem Element von einem unpassenden Typ befüllt. Das führt unweigerlich zur Heap Pollution und zur unerwarteten ClassCastException , wie man an einem einfachen Benutzungsbeispiel zeigen kann:
public static void main(String[] args) {
   Pair<String,String>[] result
     = method ( new Pair<String,String>("Vasco","da Gama"),      //3: Warnung: generic array creation
           new Pair<String,String>("Leonard","da Vinci" ));
   for (Pair<String,String> p : result) {
     String s = p.getFirst();                                 //4: ClassCastException
   }
}
Zu Zeile //3 kommt die Warnung wegen der Erzeugung eines unzulässigen generischen Arrays; in Zeile //4 kommt dann eine ClassCastException .  Eine @SafeVarargs -Annotation wäre hier gänzlich unangebracht, denn die Varargs-Methode führt die Heap Pollution mutwillig herbei, indem sie das vom Compiler generierte Array ändert und ein unpassendes Element in das Array einfügt.  Aber nicht immer ist die Situation so eindeutig.

Wie ist es hier?  Ein weniger offensichtliches Beispiel:

<T> T[] method_2 ( T... args) {                       //1: Warnung: possible heap pollution
    return args;
}
Ist die Varargs-Method in Ordnung? Immerhin ändert sie das compiler-generierte Array nicht und steckt auch keine unpassenden Elemente hinein.  Trotzdem ist Vorsicht angebracht.  Obwohl die Methode harmlos aussieht, ist sie keineswegs unproblematisch.  Im nachfolgenden, ebenfalls völlig  harmlos aussehenden Beispiel zeigen sich die Probleme bereits:
<T> T[] method_1(T t1, T t2) {
    return method _2( t1, t2 );                        //2: Warnung: generic array creation
}

String[] strings = method_1("bad", "karma");        //3: ClassCastException

Um zu verstehen, was hier passiert, muss man sich die Type Erasure ansehen.  Nach der Type Erasure sieht das obige Beispiel so aus:
Object[] method_2( Object[] args) {                          //1: Warnung: possible heap pollution
   return args;
}
Object[] method_1(Object  t1, Object t2) {
   return method_2(new Object[] {t1, t2});                  //2: Warnung: generic array creation
}

String[] strings = ( String[] ) method_1("bad", "karma");     //3: ClassCastException

Aus T... wird ein T[] , das in Wirklichkeit, d.h. im Byte-Code, ein Object[] ist.  Die Methode method_1() erwartet aber, dass sie ein String[] von der Methode method_2() bekommt, wenn sie beim Methodenaufruf Strings als Argumente übergibt.  Das stimmt aber leider nicht, was sich später in einer ClassCastException äußert.

Fazit: Auch hier wäre die Verwendung einer @SafeVarargs -Annotation an der Varargs-Methode method_2() gänzlich unangebracht.  Leider ist es in diesem Falle aber überhaupt nicht offensichtlich.

Was also nützt die Neuerung in Java 7 im Zusammenhang mit Varargs und Generics?  Wenn der Implementierer einer Methode mit einer variablen Argumentenliste absolut sicher ist, dass seine Methode keine Heap Pollution verursachen kann, dann kann er mit der @SafeVarargs -Annotation dafür sorgen, dass der Aufrufer seiner Methode nicht mehr wie bisher mit unchecked-Warnungen belästigt wird, die er ohnehin nicht beurteilen kann.  Ob der Implementierer einer Varargs-Methode jedoch stets in der Lage ist, zuverlässig zu beurteilen, wann er die Annotation zu Recht verwendet und wann er einfach nur irrtümlich eine durchaus berechtigte Warnung unterdrückt, ist eine ganz andere Frage.  An dem zugrundeliegenden Problem, dass nämlich variable Argumentenlisten zur Erzeugung problematischer Array mit nicht-reifizierbarem Elementtyp führen, hat sich auch in Java 7 nichts geändert.
 

Zusammenfassung und Ausblick

In diesem Beitrag haben wir die Spracherweiterungen in Java 7 vorgestellt, die das Arbeiten mit Generics vereinfachen.  Der Diamond-Operator liefert die automatische Deduktion von Typparametern bei der Konstruktion von Objekten eines generischen Typs.  Die Möglichkeit, Warnung im Zusammenhang mit Varargs zu unterdrücken, vereinfacht in gewissen Fällen den Aufruf von Methoden mit variabler Argumentenliste.
 

Literaturverweise

/COIN/  Project Coin/JSR 334 Final Release Specification, v1.0
URL: http://download.oracle.com/otn-pub/jcp/enhancements-1_0-final-eval-spec/enhancements-1_0-final-eval-spec.zip
/DIAMND/ The "Diamond" Operator - Improved Type Inference for Generic Instance Creation
URL: http://mail.openjdk.java.net/pipermail/coin-dev/2009-February/000009.html
/GENFAQ1/ Generics FAQs: What is type argument inference? 
URL: http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ400
/GENFAQ2/  Generics FAQs: Why does the compiler sometimes issue an unchecked warning when I invoke a "varargs" method?
URL: http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ300
/GENFAQ3/  Can I create an array whose component type is a parameterized type?
URL: http://www.angelikalanger.com/GenericsFAQ/FAQSections/ParameterizedTypes.html#FAQ104
/JSR334/  JSR 334: Small Enhancements to the JavaTM Programming Language
URL: http://jcp.org/en/jsr/detail?id=334
/VARARG/  Simplified Varargs Method Invocation
URL: http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000217.html

Die gesamte Serie über Java 7:

/JAVA7-1/ Java 7 - Überblick
Klaus Kreft & Angelika Langer, Java Magazin, Juni 2011
URL: http://www.angelikalanger.com/Articles/EffectiveJava/57.Java7.Overview/57.Java7.Overview.html
/JAVA7-2/ JSR 334 - "Project Coin" (Strings in switch, Exception Handling, ARM, numerische Literale)
Klaus Kreft & Angelika Langer, Java Magazin, August 2011
URL: http://www.angelikalanger.com/Articles/EffectiveJava/58.Java7.Coin1/58.Java7.Coin1.html
/JAVA7-3/ JSR 334 - "Project Coin" (Sprachneuerungen im Zusammenhang mit Generics)
Klaus Kreft & Angelika Langer, Java Magazin, Oktober 2011
URL: http://www.angelikalanger.com/Articles/EffectiveJava/59.Java7.Coin2/59.Java7.Coin2.html
/JAVA7-4/ JSR 203 - "NIO2" (Erweiterung der I/O Funktionalität)
Klaus Kreft & Angelika Langer, Java Magazin, Dezember 2011
URL: http://www.angelikalanger.com/Articles/EffectiveJava/60.Java7.NIO2/60.Java7.NIO2.html
/JAVA7-5/ JSR 166y - Fork-Join-Framework (Teil 1: Internals)
Klaus Kreft & Angelika Langer, Java Magazin, Februar 2012
URL: http://www.angelikalanger.com/Articles/EffectiveJava/61.Java7.ForkJoin.1/61.Java7.ForkJoin.1.htm
/JAVA7-6/ JSR 166y - Fork-Join-Framework (Teil 2: Benutzung)
Klaus Kreft & Angelika Langer, Java Magazin, April 2012
URL: http://www.angelikalanger.com/Articles/EffectiveJava/62.Java7.ForkJoin.2/62.Java7.ForkJoin.2.htm
/JAVA7-7/ Thread-Synchronisation mit Hilfe des Phasers
Klaus Kreft & Angelika Langer, Java Magazin, Juni 2012
URL: http://www.angelikalanger.com/Articles/EffectiveJava/63.Java7.Phaser/63.Java7.Phaser.htm

 
 

If you are interested to hear more about this and related topics you might want to check out the following seminar:
Seminar
 
Effective Java - Advanced Java Programming Idioms 
4 day seminar ( open enrollment and on-site)
 
  © Copyright 1995-2013 by Angelika Langer.  All Rights Reserved.    URL: < http://www.AngelikaLanger.com/Articles/EffectiveJava/59.Java7.Coin2/59.Java7.Coin2.html  last update: 24 Jan 2013