Erste Spezifikation der neuen Java 7 Closures
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Samstag, Januar 23, 2010.Die gibt es unter http://mail.openjdk.java.net/pipermail/lambda-dev/2010-January/000349.html. Das ganze heißt “Projekt Lambda”. Vom ersten Überfliegen her wirklich eine starke Vereinfachung der ersten BGGA-Closures Variante und ohne große Überraschungen.
Labels: Java 7
Frustrierend: Java 7 doch später
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Donnerstag, November 19, 2009.Heute auf der Devoxx Konferenz gab es die Info, dass wegen der neuen Spracheigenschaften sich Java 7 von Februar auf etwa September verschieben wird. Immerhin wird es dann wohl Closures geben und das Projekt Coin wird vermutlich noch mehr aufnehmen, etwa die Aufzählung von mehreren Exceptions im catch-Block.
Labels: Java 7
Überraschung: Wohl doch Closures in Java 7
1 Kommentar(e). Veröffentlicht von Christian Ullenboom am Donnerstag, November 19, 2009.Fork und Join aus Java 7
3 Kommentar(e). Veröffentlicht von Christian Ullenboom am Dienstag, November 17, 2009.- Threads: Werden vom Betriebssystem verwaltet und laufen entweder pseudo-parallel auf einem Prozessor/Core oder echt parallel. Threads können sich mit anderen Threads koordinieren. Zu viele Threads, die sich im Weg stehen und aufeinander Warten führen zu keiner verbesserten Ausführungszeit gegenüber einer sequentiellen Lösung.
- Tasks: Werden von Threads bzw. einem Thread-Pool ausgeführt. Sie sind Arbeitseinheiten, die nicht auf andere Tasks warten
Die Tasks sind kleine Arbeitspakete und werden in eine Task-Queue gelegt und dann von Threads abgearbeitet. Hat das System zwei Prozessoren und hat der Thread-Pool die Größe 2, so ist es wahrscheinlich, dass 2 Tasks parallel abgearbeitet werden. Gibt es 4 Prozessoren, können 4 Tasks vielleicht parallel laufen. Tasks lassen sich also grundsätzlich auf einer beliebigen Anzahl Threads und somit Prozessoren/Cores bringen, wobei im Gegensatz die Effektivität von Threads immer mit der physikalischen Anzahl von Prozessoren/Cores assoziiert ist.
Das Fork/Join-Framework löst die Probleme effektiv. Wie der Name schon andeutet, geht es bei Fork um das Erstellen eines neuen Tasks und bei Join um das Zusammenführen der Ergebnisse. Die Fork/Join-Bibliothek bietet dazu die Klasse ForkJoinPool und zwei zentrale Methoden: fork() und join(). Zur Abarbeitung der Tasks stellt das Framework die Threads zu Verfügung, deren Anzahl wir zwar selbst bestimmen können, aber die Anzahl Prozessoren/Cores eine gute Standardgröße ist.[1] Die Methode fork() erzeugt einen neuen Task, der an den Anfang (!) einer Queue gestellt wird. Dabei haben alle Threads eine Queue für ihre Arbeitsaufträge und sollte einmal eine Queue leer gelaufen sein, so nimmt sich der Thread einfach einen Task vom Ende (!) einer anderen nicht-leeren Queue. (Das nennt sich work-stealing und ist in der Realwelt ziemlich selten anzutreffen.) Dass neue Tasks an den Anfang gestellt werden ist einfach zu erklären: Die Tasks werden ja immer kleiner und somit stehen die kleinen, schnell lösbaren Aufgaben vorne. Erst später folgen die größeren Aufgaben, die auf die Ergebnisse der kleinen Aufgaben zurückgreifen, die dann logischerweise schon berechnet wurden.
Zur Theorie ein Beispiel: Es geht darum, mit Fork/Join ein Programm zu haben, welches parallel das Maximum eines Arrays sucht. Der Start ist:
int[] array = { 0, 9, 10, 111, 1, 12, 13, 14, 17 };
System.out.println( MaxElementInArrayFinder.findMax( array ) );
Die eigene Klasse MaxElementInArrayFinder bietet die Methode findMax(), die auf den ForkJoinPool zurückgreift, um mit invoke() den Haupt-Task abzusetzen.
class MaxElementInArrayFinder
{
private static final ForkJoinPool fjPool = new ForkJoinPool();
...
public static int findMax( int[] array )
{
return fjPool.invoke( new MaxElemTask( array, 0, array.length -1 ) );
}
}
Die Klasse MaxElemTask repräsentiert unser Arbeitspakt. Die Tasks referenzieren jeweils das Array, und die Anfangs-/Endeposition, ab der sie nach dem Maximum suchen sollen.
private static class MaxElemTask extends RecursiveTask<Integer>
{
private final int[] array;
private final int start, end;
MaxElemTask( int[] array, int start, int end )
{
assert array != null && start >= 0 && start <= end;
this.array = array;
this.start = start;
this.end = end;
}
@Override protected Integer compute()
{
…
}
}
Unsere Klasse erweitert die Basisklasse RecursiveTask<Integer> und deutet durch den generischen Typ schon an, das das Ergebnis des Tasks eine Ganzzahl sein wird, nämlich das Feldmaximum aus dem gewünschten Bereich. Der Konstruktor sichert die Werte und compute() führt die eigentliche Arbeit aus: Es löst entweder das Problem direkt, wenn es klein genug ist, oder spannt Unter-Tasks auf und wartet anschließend auf deren Ergebnisse.
@Override protected Integer compute()
{
assert array != null && array.length > 0;
System.out.printf( "max( start=%d, end=%d )%n", start, end );
if ( end - start < 4 )
{
int max = array[start];
for ( int i = start + 1; i <= end; i++ )
if ( array[i] > max )
max = array[i];
return max;
}
int middle = (start + end) / 2;
MaxElemTask leftTask = new MaxElemTask( array, start, middle );
leftTask.fork();
MaxElemTask rightTask = new MaxElemTask( array, middle + 1, end );
int rightMax = rightTask.compute();
int leftMax = leftTask.join();
return Math.max( rightMax, leftMax );
}
[1] Das die Maximalanzahl von Threads beim ForkJoinPool zurzeit 32767 ist, dürfte für normale Nutzer keine Einschränkung sein.
switch auf Strings seit Java 7
2 Kommentar(e). Veröffentlicht von Christian Ullenboom am Samstag, November 14, 2009.Seit Java 7 sind switch-Anweisungen auf String-Objekten möglich.
String input = javax.swing.JOptionPane.showInputDialog( "Eingabe" );
switch ( input.toLowerCase() )
{
case "kekse":
System.out.println( "Ich mag Keeeekse" );
break;
case "kuchen":
System.out.println( "Ich mag Kuchen" );
break;
case "scholokade":
case "lakritze":
System.out.println( "Hm. lecker" );
break;
default:
System.out.printf( "Kann man %s essen?", input );
}
Obwohl Zeichenkettenvergleiche nun möglich sind, fallen Überprüfungen auf reguläre Ausdrücke leider heraus, die insbesondere Skriptsprachen anbieten.
Java 7 Milestone 5: Build b76 vorgestellt
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Samstag, November 14, 2009.Download gibt es unter http://download.java.net/jdk7/m5/, die Neuerungen bei http://download.java.net/jdk7/changes/jdk7-b76.html. Am Interessantesten dürfen sein:
- bb3. 6865582. jsr166y - jsr166 maintenance update
- 6865571. Add a lightweight task framework known as ForkJoin
- 6445158. Phaser - an improved CyclicBarrier
- 6865579. Add TransferQueue/LinkedTransferQueue
und
- 8fb9b4be3cb1. 6827009. Project Coin: Strings in Switch
Labels: Java 7
Swing-Komponenten neu erstellen oder verändern und JLayer
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Mittwoch, November 04, 2009.Unproblematisch ist auch, wenn sich neue Komponenten aus anderen Swing Teilkomponenten zusammenzusetzen lassen und. Dann erweitert die neue Swing-Klasse erweitert einen Container wie JPanel, der einfach die anderen Elemente wie gewünscht platziert. Möglich ist dies zum Beispiel bei einer Statuszeile, da diese nichts großartiges macht, als einfach horizontal andere Komponenten anzuordnen und einen besonderen Rahmen zu setzen. Einen Dialog zur Auswahl eines Zeichensatzes bietet Swing bisher auch nicht an, der lässt sich aber als JDialog mit passenden Swing-Komponenten leicht nachbauen.
Mehr Arbeit ist nötig, wenn sich auf keine allgemeinen Swing-Komponenten zurückgreifen lässt. Die Swing-Bibliothek bietet etwa keine Ribbon-Komponente, keinen wirklich guten HTML-Renderer, oder ein Docking-Framework. Bei Anforderungen dieser Art lässt sich nicht so einfach auf Standardkomponenten zurückgreifen, sondern spezieller Programmcode zum Zeichnen nötig. Der wesentliche Unterschied ist also der, dass sich die Darstellung nicht vollständig an Standardkomponenten delegieren lässt sondern immer etwas einer Java-Code zum Zeichnen nötig ist.
Um es richtig gut zu machen, sind für eine eigene Swing-Komponente drei Dinge nötig: Die Komponentenklasse, eine Modellklasse und ein UI-Delegate. Die Komponentenklasse ist die Hauptklasse und eine JComponent, die der Entwickler auf die Gui setzt. Sie bietet die API zum Setzten der Zustände. Die Modell-Daten werden nicht selbst in der Komponentenklasse gespeichert, sondern idealweise über eine eigene Klasse modelliert. Die Tabelle nimmt zum Beispiel die Zellen aus einem Tabellemodell, eine Textkomponenten den Text aus einem Dokumentenmodell. Als letztes bleibt der UI-Delegate, der das wirkliche Zeichen und die Ereignisbehandlung übernimmt. Es kann sehr anspruchvoll sein ein gutes Aussehen und effektive Navigation zu erreichen und insbesondere wenn die Komponente in verschiedenen Look-and-Feels arbeiten soll, eine Menge Arbeit werden. Und das die eigene Swing-Komponente die UI-Eigenschaften wie Farben, Abstände und Antialiasing-Modus toleriert ist selbstverständlich.
Überlagerungen mit dem Swing-Komponenten-Dekorator JLayer
Können Swing-Komponten überlagert werden, können dadurch interessante Effekte erzieht werden. Ein paar Beispiele:
· Während ein Text in die Textbox geladen wird, erscheint ein JProgressBar.
· Bei aufwändigen Operationen wird das Haupt-Panel gesperrt und eine drehende Sanduhr erscheint.
· Ist die Eingabe in einem Textfeld falsch, erscheint ein kleines Symbol, welches über die invalide Eingabe informiert.
· Über einer leeren Tabelle liegt eine Beschriftung, die erklärt, dass Doppelklick eine neue Zeile einfügt.
Alle die Darstellungen lassen sich mit Hilfe der in Java 7 eingefügten Klassen JLayer einfach lösen (Nutzer vor Java 7 greifen auf SwingX zurück, denn von dort kommt die Klasse auch; sie heißt nur dort JXLayer. Als Alternative haben Autoren auch oft auf Glass-Pane zurückgegriffen.)
Haupteigenschaft von JLayer ist, sich um existierende Swing-Komponenten zu legen. Soll ein JLayer um ein Textfeld gelegt werden, heißt es:
JLayer
Die zu ummantelnde Komponente wird über den Konstruktor angegeben und nicht über add(), da JLayer kein Container ist. Der nächste Schritt ist die Angabe eines Objekts, dass das Zeichnen übernimmt.
layer.setUI( layerUI );
Die Angabe erfordert ein LayerUI-Objekt, welches eine paint()-Methode realisiert. Die Implementierung kann super.paint() aufrufen, um die ummantelte Komponente zu zeichnen, und dann eigenen Programmcode hinzufügen, um etwa einen Sanduhr darzustellen.
Das folgende Beispiel fasst die Schritte zusammen und realisiert ein Programm, welches bei Eingabe von „pu“ einen kleinen roten Kreis anzeigt.
JFrame f = new JFrame();
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.setLayout( new BorderLayout(2, 2) );
f.add( new JSeparator(), BorderLayout.PAGE_START );
f.add( new JLabel( "Name:" ), BorderLayout.LINE_START );
final JTextField textField = new JTextField();
LayerUI
{
@Override
public void paint( Graphics g, JComponent component )
{
super.paint( g, component );
if ( textField.getText().equalsIgnoreCase( "pu" ) )
{
g.setColor( new Color( 255, 0, 0, 100 ) );
g.fillOval( 0, component.getHeight() - 10, 10, 10 );
}
}
};
JLayer
layer.setUI( layerUI );
f.add( layer );
f.add( new JSeparator(), BorderLayout.PAGE_END );
f.pack();
f.setVisible( true );
Die JLayer kann auch das Hauptpanel dekorieren und die Events auffangen. Das ist eine zweites Anwendungsfeld neben dem Änderung der Darstellung. Die JLayer-Komponente kann einfach Events auffangen und verarbeiten und so zum Beispiel global F1 für die Hilfe abfangen.
Process-Ströme in Dateien umlenken
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Dienstag, November 03, 2009.Neben diesem Pipe-Modus gibt es seit Java 7 eine Alternative, die Ströme direkt auf Dateien umzulenken. Dazu definiert die ProcessBuilder-Klasse diverse redirectXXX()-Methoden. (Sollte dann ein getXXXStream()-Aufruf gemacht werden, so kommen nicht-aktive Ströme zurück, denn das externe Programm kommuniziert ja dann direkt mit einer Datei und die Java-Pipe hängt nicht dazwischen.)
class java.lang.ProcessBuilder
§ ProcessBuilder redirectInput( File file )
ProcessBuilder redirectInput( ProcessBuilder.Redirect source )
Der Unterprozess wird die Eingaben aus der angegeben Quelle beziehen.
§ ProcessBuilder redirectOutput( File file )
§ ProcessBuilder redirectOutput( ProcessBuilder.Redirect destination )
Der Unterprozess wird Standardausgaben an das angegebene Ziel senden.
§ ProcessBuilder redirectError( File file )
§ ProcessBuilder redirectError( ProcessBuilder.Redirect destination )
Der Unterprozess wird Fehlerausgaben an das angegebene Ziel senden.
Die redirectXXX(File file)-Methoden bekommen als Ziel ein einfaches File-Objekt. Die redirectXXX()-Methoden sind aber überladen mit einem anderen Typ Redirect, der als innere statische Klasse in ProcessBuilder angelegt ist. Mit Redirect.PIPE und Redirect.INHERIT gibt es zwei Konstanten, und drei statischen Methoden Redirect.from(File), Redirect.to(File), Redirect.appendTo(File) die Redirect-Objekte für die Umleitung zur Datei liefern. Die mit File parametrisierten Methoden greifen auf die Redirect-Klasse zurück, so dass es bei redirectOutput(File file) intern auf ein redirectOutput(Redirect.to(file)) herausläuft.
NumberFormat, Währungen angeben und die Klasse Currency
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Dienstag, November 03, 2009.NumberFormat frmt1 = DecimalFormat.getCurrencyInstance( Locale.FRANCE );
System.out.println( frmt1.format( 12345.6789 ) ); // 12 345,68 €
so ist die Währung automatisch Euro (denn Frankreich nutzt den Euro); schreiben wir DecimalFormat.getCurrencyInstance(Locale.JAPAN) ist sie Yen und wir bekommen ¥12,346. (Es gibt standardmäßig keine Nachkommastellen beim Yen.) Locale-Objekten repräsentieren immer eine sprachliche Region.
DecimalFormat bzw. schon die Oberklasse NumberFormat ermöglicht die explizite Angabe der Währung. In der Java-Bibliothek wird sich durch die Klasse java.util.Currency repräsentiert. NumberFormat liefert mit getCurrency() die eingestellte Currency, die zur Formatierung verwendet wird und setCurrency() setzt sie neu. Das löst Szenarios, in denen etwa ein Euro-Zeichen die Währung darstellt, aber die Zahlenformatierung englisch ist, wie die folgenden Zeilen zeigen:
NumberFormat frmt = DecimalFormat.getCurrencyInstance( Locale.ENGLISH );
frmt.setCurrency( Currency.getInstance( "EUR" ) );
System.out.println( frmt.format( 12345.6789 ) ); // EUR12,345.68
Die Currency-Klasse bietet drei statische Methoden, die Currency-Objekte liefern. Da ist einmal getAvailableCurrencies(), was ein Set
Folgendes Programm geht über alle Währungen und gibt die zentralen Informationen aus:
for ( Currency currency : Currency.getAvailableCurrencies() )
{
System.out.printf( "%s, %s, %s (%s)%n",
currency.getCurrencyCode(),
currency.getSymbol(),
currency.getDisplayName(),
currency.getDisplayName(Locale.ENGLISH) );
}
Wir bekommen dann mehr als 200 Ausgaben, und die Ausgabe beginnt mit:
EGP, EGP, Ägyptisches Pfund (Egyptian Pound)
IQD, IQD, Irak Dinar (Iraqi Dinar)
GHS, GHS, Ghana Cedi (Ghana Cedi)
AFN, AFN, Afghani (Afghani)
MUR, MUR, Mauritius Rupie (Mauritius Rupee)
SGD, SGD, Singapur Dollar (Singapore Dollar)
…
Wrapperklassen-Vergleiche durchführen mit compare() und compareTo()
1 Kommentar(e). Veröffentlicht von Christian Ullenboom am Montag, November 02, 2009.· Sie implementieren eine Objektmethode compareTo(). Die Methode ist nicht zufällig da, denn Wrapper-Klassen implementieren die Schnittstelle Comparable. (Wir haben die Schnittstelle schon im Kapitel 6 kurz vorgestellt.)
· Wrapper-Klassen besitzen statische compare()-Methoden.
Die Rückgabe der Methoden ist ein int und es kodiert, ob ein Wert größer, kleiner oder gleich ist.
Beispiel Teste verschiedene Werte.
System.out.println( Integer.compare(1, 2) ); // -1
System.out.println( Integer.compare(1, 1) ); // 0
System.out.println( Integer.compare(2, 1) ); // 1
System.out.println( Double.compare(2.0, 2.1) ); // -1
System.out.println( Double.compare(Double.NaN, 0) );// 1
System.out.println( Boolean.compare(true, false) ); // 1
System.out.println( Boolean.compare(false, true) ); // -1
Ein true ist „größer“ als als false.
Die Tabelle fasst von den Wrapper-Klassen die Methoden zusammen.
Klasse Methode aus Comparable Statische Methode compare()
Byte int compareTo(Byte anotherByte) int compare(int x, int y)
Short int compareTo(Short anotherShort) int compare(short x, short y)
Float int compareTo(Float anotherFloat) int compare(float f1, float f2)
Double int compareTo(Double anotherDouble) int compare(double d1, double d2)
Integer int compareTo(Integer anotherInteger) int compare(int x, int y)
Long int compareTo(Long anotherLong) int compare(long x, long y)
Character int compareTo(Character anotherCharacter) int compare(char x, char y)
Boolean int compareTo(Boolean b) int compare(boolean x, boolean y)
Die Implementierung einer statischen Methode WrapperKlasse.compare() ist äquivalent zu WrapperKlasse.valueOf(x).compareTo(WrapperKlasse.valueOf(y)).
Die Klassen BigInteger, BigDecimal implementieren zwar Number und somit Comparable, aber eine statische compare()-Methode bieten sie nicht. Auch String implementiert Comparable, aber eine statische Methode fehlt. Der Grund ist, dass es eine statische Methode Objects.compare() gibt, zwei Objekte mit einem Comperator vergleicht.
Die Utility-Klasse java.lang.Objects
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Montag, November 02, 2009.null-Tests um equals()/hashCode()/toString()
class java.lang.Objects |
Liefert true wenn beide Argument entweder null sind, oder a.equls(b) ebenfalls true ergibt. Sonst false. Das Objects.equals(null, null) die Rückgabe true ergibt ist sinnvoll und so erspart die Methode einige händische Tests.
Liefert 0 wenn o gleich null ist, sonst o.hashCode().
Liefert den String "null" wen das Argument null ist sonst o.toString().
Null-Prüfungen mit eingebauter Ausnahmebehandlung
Beispiel Die Methde setName() soll keine name-Argument gleich null erlauben. public void setName( String name ) { this.name = Objects.nonNull( name ); } Alternativ ist eine Fehlermeldung möglich: public void setName( String name ) { this.name = Objects.nonNull( name, "name is not supposed to be null" ); } |
class java.lang.Objects |
Java 7 bringt binary literals und Underscores in literals
1 Kommentar(e). Veröffentlicht von Christian Ullenboom am Donnerstag, Oktober 15, 2009.Im Build http://download.java.net/jdk7/changes/jdk7-b73.html gibt es noch mehr, aber diese beiden Änderungen betreffen die Sprache:
- 6860965. Project Coin: binary literals. http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000929.html
- 6860973. Project Coin: Underscores in literals. http://mail.openjdk.java.net/pipermail/coin-dev/2009-April/001628.html
Labels: Java 7
Erste Sprachänderung in Java 7
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Sonntag, September 20, 2009.- http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6840638
- http://download.java.net/jdk7/changes/jdk7-b72.html
- http://download.java.net/jdk7/
Labels: Java 7
Die Sprachänderungen für Java 7 nun fest
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Samstag, August 29, 2009.Improved Type Inference for Generic Instance Creation (diamond)
An omnibus proposal for better integral literals (also binary literals and underscores in numbers)
Language support for Collections
Raus sind erst einmal
und auch alle anderen Dinge.
Das alles erscheint mir schon mehr merkwürdig in der Auswahl. Improved Exception Handling for Java war so ein heißer Kandidat und wird es nun doch nicht.
Deadline ist Ende Oktober.
Labels: Java 7
Video mit Danny Coward über Java 7
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Dienstag, August 25, 2009.Das Video gibt es bei Sun unter http://java.sun.com/developer/media/deepdivejdk7.jsp. Es zeigt die 5 Top-Features, die in Java 7 erwartet werden. Das Interview erwähnt noch das Swing Application Framework, was aber gestorben ist.
Labels: Java 7
Swing Application Framework fliegt aus Java 7 raus
5 Kommentar(e). Veröffentlicht von Christian Ullenboom am Donnerstag, August 20, 2009.After much discussion it's become clear that the Swing Application Framework API as it is today hasn't reached consensus and we feel still needs further design work done.
Since the SAF API was committed to milestone 5 of JDK7 and that time is already here, this date is now impossible, and we need to decommit SAF from any specific JDK 7 milestone
NIO.2: Wahlfreier Zugriff mit SeekableByteChannel und ByteBuffer
2 Kommentar(e). Veröffentlicht von Christian Ullenboom am Mittwoch, August 05, 2009.Für den wahlfreien Zugriff auf Bytes in Dateien bietet Java seit Version 1.0 die Klasse RandomAccessFile. Unter Java 7 ist die Klasse nun nicht mehr nötig, und genaugenommen auch schon seit Java 1.4 nicht mehr, denn in Java 1.4 wurde das erste NIO-Paket eingeführt und mit ihm sogenannte Channel-Klassen, die eine offene Verbindung mit einem Datenkanal repräsentieren. Neu in Java 7 ist die Verbindung von Path und einem besonderen Channel für wahlfreie Ein-/Ausgabe, dem SeekableByteChannel, der ebenfalls in Java 7 eingeführt wurde.
SeekableByteChannel
Der SeekableByteChannel deklariert Operationen zum Lesen und Schreiben von Daten und zur Positionierung des Dateizeigers.
| public interface java.nio.channels.SeekableByteChannel extends ByteChannel |
§ int read( ByteBuffer dst )
§ int write( ByteBuffer src )
§ long size()
§ long position()
§ SeekableByteChannel position( long newPosition )
§ SeekableByteChannel truncate( long size )
Die Methoden close() und isOpen() kommen aus Channel hinzu.
Es fällt auf, und das ist einer der großen Unterschiede zu RandomAccessFile, dass SeekableByteChannel kein byte-Feld oder einzelne Bytes liest oder schreibt, sondern einen ganz eigenen Typ, einen ByteBuffer, erwartet.
ByteBuffer
Ein ByteBuffer ist einem Byte-Feld sehr ähnlich; seine maximale Größe wird vorher festgelegt und kann später nicht dynamisch wachsen. Ist ein ByteBuffer angelegt, so können über einen Index die einzelnen Bytes gelesen und geschrieben werden, zum Beispiel mit byte get() oder put(byte b) relativ zur letzten Position, oder mit byte get(int index) und put(int index, byte b) absolut. Der wirkliche Unterschied ist aber, dass Java zwei verschiedene Arten von ByteBuffer-Implementierungen bietet (ByteBuffer ist eine abstrakte Klasse):
· Nicht-direkte ByteBuffer sind wie byte[]-Felder, also Java-Objekte, die auf dem Heap Platz einnehmen.
· Bei einem direkten ByteBuffer versucht Java einen Speicherbereich vom Betriebssystem zu bekommen. Während die nicht-direkten ByteBuffer und byte-Arrays auf dem Heap leben, und er normalen GC unterworfen sind, sollten die direkten ByteBuffer vom Betriebssystem verwaltet werden. Im Idealfall sind dadurch hohe Ein-/Ausgabegeschwindigkeiten möglich, denn mit direkten ByteBuffern kann sich das Betriebssystem Kopieroperationen zwischen nativen und Java-Puffern sparen.
Die Methoden auf direkten oder nicht-direkten ByteBuffern sind identisch. Insbesondere speichern die alle Puffer Zustände: Die Position, einen Limit und eine Kapazität. Diesen Eigenschaften wollen wir aber in der Insel nicht nachgehen.
Beispiel mit Path + SeekableByteChannel + ByteBuffer
Das folgende Beispiel fasst alles zusammen: Von einem Path wird über newByteChannel ein SeekableByteChannel erfragt. Anschließen leiten wir aus einer Zeichenkette über das byte[] einen nicht-direkten ByteBuffer ab und schreiben diesen in den SeekableByteChannel, sodass später die Datei Kurt Cobain.txt einen ASCII-Text enthält.
com/tutego/insel/nio2/SeekableByteChannelDemo.java, main()
Path p = Paths.get( "Kurt Cobain.txt" );
SeekableByteChannel raf = p.newByteChannel( StandardOpenOption.CREATE,
StandardOpenOption.WRITE );
String s = "Drugs are bad for you. ";
ByteBuffer byteBuffer = ByteBuffer.wrap( s.getBytes() );
raf.write( byteBuffer );
raf.write( ByteBuffer.wrap( "They will f*ck you up.".getBytes() ) );
raf.position( 34 );
raf.write( ByteBuffer.wrap( new byte[]{'u'} ) );
raf.close();
Das Beispiel zeigt, dass mit ByteBuffer.wrap() aus dem byte[] der Strings ein nicht-direkter Buffer angelegt wird, den write() dann in den Kanal schreibt.
Nur zum Testen schreiben wie ASCII-Zeichen, was aber im „echten Leben“ eher nicht der Fall sein wird, denn wir müssen hier die korrekten Zeichenkodierungen beachten. Auch für sequenzielle Schreiboperationen ist der SeekableByteChannel eher weniger komfortabel – dennoch ist der Einsatz von Kanälen nicht per se falsch. Im nächsten Kapitel werden die Ströme vorgestellt, mit denen das Schreiben, insbesondere von Textdokumenten, viel einfacher wird.
FileChannel
Die Schnittstelle SeekableByteChannel gibt Operationen an, um die aktuelle Position auszulesen und den Positionszeiger neu zu setzen und über ByteBuffer Bytes und Bytefolgen zu lesen und zu schreiben. SeekableByteChannel ist dabei nicht an Dateien gebunden und enthält keine Informationen zu Dateipfaden oder sonstigen tiefer liegenden Schichten. Und da Path grundsätzlich ein Pfad auf alles Mögliche sein kann, etwa auf ein BLOB in der Datenbank, liefert newByteChannel() eine Rückgabe mindestens von Typ SeekableByteChannel, und damit erst ein mal keine Möglichkeiten dateispezifische Operationen vorzunehmen.
Wird allerdings newByteChannel() auf einem Pfad aufgerufen, der eine Datei vom Dateisystem repräsentiert, so ist die Rückgabe nicht einfach nur ein SeekableByteChannel, sondern der Untertyp FileChannel.[1] Ein Typcast ist daher möglich:
Path p = Paths.get( "Kurt Cobain.txt" );
FileChannel channel = (FileChannel) p.newByteChannel( options );
Da FileChannel die Schnittstelle SeekableByteChannel implementiert, bietet natürlich FileChannel alle Methoden zum Lesen, Schreiben und Positionieren. Zusätzlich bietet FileChannel aber Methoden, die explizit an Dateien gebunden sind. Drei Methoden fallen sofort auf:
· lock(): Sperrt die Datei (oder Dateiteile) für andere, soweit es das Betriebssystem unterstützt.
· force(): Updates werden sofort materialisiert, das heißt auf das Dateisystem übertragen.
· map(): Blendet die Datei, oder einen Teil der Datei, in den Speicher ein.
Die Methode map() ist besonders interessant. Damit kann ein FileChannel auf ein ByteBuffer abgebildet werden, sodass unsere Lese-/Schreiboperationen auf dem ByteBuffer direkt aus der Datei kommen oder direkt in die Datei gehen. Das Betriebssystem versucht sein bestes, die Operationen zu optimieren und geeignete Blöcke der Datei in den Speicher zu laden. Java und das Betriebssystem tuen damit ihr Bestes, die Operationen so schnell wie möglich und mit wenigen Kopieroperationen zwischen den internen Puffern vom Dateisystem und den Java-Puffern durchführen.
Das folgende Beispiel bezieht im ersten Schritt über newByteChannel() den FileChannel. Anschließend bildet die Methode map() die gesamte Datei auf einen MappedByteBuffer ab, der ein ByteBuffer ist, wie wir ihn im letzten Beispiel schon kennengelernt haben. Wir könnten nun Methoden auf dem ByteBuffer aufrufen, und die Bytes auslesen, doch hier gehen wir etwa anders vor: Die Bytes des ByteBuffer konvertiert ein CharsetDecoder von ASCII in Java-Unicode; das Ergebnis ist ein CharBuffer. Den CharBuffer laufen wir ab und geben die Zeichen auf der Konsole aus.
com/tutego/insel/nio2/FileChannelDemo.java, main()
Path p = Paths.get( "Kurt Cobain.txt" );
FileChannel fileChannel = (FileChannel) p.newByteChannel( StandardOpenOption.READ );
ByteBuffer byteBuffer = fileChannel.map( FileChannel.MapMode.READ_ONLY,
0, fileChannel.size() );
CharsetDecoder decoder = Charset.forName( "ASCII" ).newDecoder();
CharBuffer charBuffer = decoder.decode( byteBuffer );
while ( charBuffer.hasRemaining() )
System.out.print( charBuffer.get() );
Beim FileChannel gilt das gleiche wie beim SeekableByteChannel. Sequenzieller Lese- oder Schreibzugriff wird am Einfachsten über die Strom-Klassen realisiert. Sie werden im folgenden Kapitel vorgestellt.
[1] Den Typ FileChannel gibt es in Java schon länger, seit Java 1.4. Und vor Java 7 lieferten die Methoden getChannel() von FileInputStream, FileOutputStream und RandomAccessFile den FileChannel.
OpenJDK7 / JDK7 M4 Release
1 Kommentar(e). Veröffentlicht von Christian Ullenboom am Samstag, Juli 25, 2009.
Labels: Java 7
Ausführlicher Artikel über invokedynamic
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Samstag, Juli 18, 2009.Labels: Java 7
Vorschläge für Projekt Coin (Java 7 Sprachänderungen) geht in die nächste Runde
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Freitag, Mai 29, 2009.Strings in switch,
Joe DarcyImproved Exception Handling for Java,
Neal GafterAutomatic Resource Management,
Josh BlochImproved Type Inference for Generic Instance Creation,
Jeremy MansonElvis and Other Null-Safe Operators, Neal Gafter, Stephen Colebourne
Simplified Varargs Method Invocation,
Bob Lee
Integer-Literale:
Byte and Short Integer Literal Suffixes,
Bruce ChapmanBinary Literals,
Derek FosterUnderscores in numbers,
Derek Foster
Language support for JSR 292
(wiki),
John RoseIndexing access syntax for Lists and Maps,
Shams Mahmood ImamCollection Literals,
Joshua BlochLarge arrays (revised),
James Lowden
Labels: Java 7
Nimbus Swing Look and Feel wird auch Teil von Java 7 sein
1 Kommentar(e). Veröffentlicht von Christian Ullenboom am Samstag, Mai 09, 2009.Von Java 6 wurde das Nimbus LaF auch auf das OpenJDK 7 gebracht. Es wird dann unter javax.swing.plaf liegen.
Labels: Java 7
Transparente und nicht-rechteckige Fenster
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Samstag, Mai 09, 2009.Kirill Grouchnikov gibt in seinem Blog-Eintrag http://www.pushing-pixels.org/?p=1209 den Hinweis, dass in JDK 7 (b57) transparente und nicht-rechteckige Fenster unterstützt werden. Die Sun-interne Klasse com.sun.awt.AWTUtilities wird nun vom Entwickler nicht mehr benötigt, und die Funktionalität ist in die Windows-Klasse gewandert; sie delegiert aber weiterhin an AWTUtilities.
Labels: Java 7
Größe vom Integer.valueOf Cache ist nun konfigurierbar
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Montag, April 20, 2009.Unter http://hg.openjdk.java.net/jdk7/jdk7/jdk/rev/4c3f752993a5 ist die Änderung sichtbar, mit der in Java 7 der Cache für die Integer-Objekte nun nicht mehr zwangsläufig im Bereich –128 bis +127 liegen muss. Ändern kann man die Cache-Größe auf der Kommandozeile mit -XX:AutoBoxCacheMax=<size>.
Labels: Java 7
JSR 203 (NIO2) im OpenJDK 7
1 Kommentar(e). Veröffentlicht von Christian Ullenboom am Samstag, April 04, 2009.- http://www.youtube.com/watch?gl=DE&v=yNRS1ssLPdQ
- http://today.java.net/pub/a/today/2008/07/03/jsr-203-new-file-apis.html
Labels: Java 7
Na endlich: JDK 7 feature list, build and integration schedule
1 Kommentar(e). Veröffentlicht von Christian Ullenboom am Donnerstag, März 26, 2009.Mark Reinhold schreibt auf seinem Blog:
At Devoxx back in December I presented a list of candidate features for JDK 7(video, interview); more recently, at FOSDEM, I discussed that list as well as the high-level schedule and the means by which we plan to deliver the release (slides, audio/video). The high-level schedule is now available on the JDK 7 Project page in theOpenJDK Community along with a detailed feature list, the near-term build and integration schedule, and the long-term, build-by-build calendar. The JDK 7 feature list, like that of any large software project, is provisional and subject to change based upon new information and new proposals. Who Many of the planned features will be implemented by Sun engineers; the rest will be contributed from outside Sun. We’ll shortly define a process by which additional features can be proposed—so long as you’re willing to write not just the code but also the necessary tests and specification material. There will also be a simpler, lighter-weight process for smaller changes. Where Regardless of who writes the code, the expectation is that all development will take place in the open, either in the JDK 7 Project, in some other OpenJDK Project, or elsewhere. What about the JCP? The JDK 7 Project is creating a prototype of what might—or might not—wind up in the Java SE 7 Platform Specification. When the SE 7 Platform JSR is submitted then the features under development in JDK 7 will be proposed for inclusion therein, except for those that are VM-level or implementation-specific.
Der Kalender gibt Ende Februar an.
M8 2010/02/12 – 2010/02/18 b95
Die Feature-List ist interessant aber ich denke, dass einiges im Detail noch offen ist:
- JSR TBD: Small language enhancements (Project Coin)
- JSR 296: Swing application framework
- Swing updates
Labels: Java 7
Java 7 und Named Capturing Group
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Donnerstag, März 12, 2009.Hatte man bei reguläre Ausdrücken und Gruppen auf die Gruppen über eine Index zugreifen müssen, so kann man dies in Java 7 auch über einen Namen tun; das ganze nennt sich dann named capturing group:
String pStr = "0x(?<bytes>\\p{XDigit}{1,4})\\s++u\\+(?<char>\\p{XDigit}{4})(?:\\s++)?";
Matcher m = Pattern.compile(pStr).matcher(INPUTTEXT);
if (m.matches()) {
int bs = Integer.valueOf(m.group("bytes"), 16);
int c = Integer.valueOf(m.group("char"), 16);
System.out.printf("[%x] -> [%04x]%n", bs, c);
}
Der Blog-Eintrag http://blogs.sun.com/xuemingshen/entry/named_capturing_group_in_jdk7 gibt es in paar mehr Infos.
Labels: Java 7
URLClassLoader schließen in Java 7
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Donnerstag, März 12, 2009.URLClassLoader bekommt in Java 7 eine close()-Methode: http://blogs.sun.com/CoreJavaTechTips/entry/closing_a_urlclassloader.
Labels: Java 7
Video: Java 7, Modularisierung, Jigsaw, was kommt, was geht, 2010
1 Kommentar(e). Veröffentlicht von Christian Ullenboom am Dienstag, Februar 17, 2009.Mark Reinhold spricht über Java 7
Geplante Änderungen:
- re-throw
- null-dereference expressions
- Type-Inference -- Wer zu viel Zeit hat, dem ist http://blog.henning.makholm.net/2008/11/java-50-type-inferences-is.html empfohlen
- Multi-Catch
- JSR 203 (More NIO, NIO2)
- JSR 296: Swing Application Framework
- SCTP
- SDP
- Unicode 5
- Swing-Updates wie JXLayer, DatePicker
- G1 GC
- JSR 308, http://groups.csail.mit.edu/pag/jsr308/
Wird (wohl) nicht in Java 7 kommen wird:
- Closures
- Reified Generics
- First Class Properties
- Überladene Operatoren
- BigDecimal Syntax
- JSR 295: Beans Binding
Der Hammer: Java 7 wird Anfang 2010 erwartet.
Wer mal was inspirierendes hören möchte: Unter http://channel9.msdn.com/posts/Charles/C-40-Meet-the-Design-Team/ sprechen C#-Macher Anders Hejlsberg und weitere C#-Experten über die Zukunft von C# und .NET, insbesondere im Kontext dynamischer Sprachen. Hier läuft man meilenweit vor Java; und das Interview ist schon 1 Jahr alt... Unter http://code.msdn.microsoft.com/csharpfuture geht's es dann mit ein paar Links zur C# 4 und Ideen zu C# 5 weiter.
Labels: Java 7
Auswirkung von neuen Sprachkonstrukturen in Java 7 und Project Coin
1 Kommentar(e). Veröffentlicht von Christian Ullenboom am Montag, Februar 16, 2009.Joseph Darcy veröffentlichte schon vor 2 Jahren einen interessanten Blog-Artikel über die Änderungen, die enum mit sich brachte: JSL, Compiler, Bibliotheken, JVM-Spezifikation, usw. Da Sun sich mit dem neuen Modulsystem Jigsaw wohl ganz gut was vorgenommen hat, wird es große Änderungen an der Sprache (wie Closures oder Reified Generics) wohl nicht in Java 7 geben. Dennoch gibt es mit dem aktuellen Project Coin: Small Language Changes for JDK 7 einen neuen Versuch, zumindest einige Features unterzubringen, die mit wenig Aufwand implementiert werden können. Dazu zählen ein switch mit Strings oder die Möglichkeit, multiple Exceptions in einem catch zu fangen.
Labels: Java 7
Joda Time 1.6/JSR 310
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Samstag, November 29, 2008.Von Joda Time (http://joda-time.sourceforge.net/) gibt es ein neues Update in der Version 1.6 (vom 2008-10-27). Interessant bleibt zu sehen, wie sich Joda Time im Laufe der Zeit gegenüber der JSR 310: A New Java Date/Time API abhebt.
Zur JSR 310, die in Java 7 erwartet wird:
- https://jsr-310.dev.java.net/
- http://jcp.org/en/jsr/detail?id=310
- http://today.java.net/pub/a/today/2008/09/18/jsr-310-new-java-date-time-api.html
- https://jsr-310.dev.java.net/nonav/doc-2008-08-04/index.html
Stephen Colebourne ist einer der treibenden Personen von Joda Time und auch Specification Lead der JSR 310.
Labels: Java 7, Open Source
Erster Java Closures Prototype im openjdk
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Sonntag, August 10, 2008.Von http://gafter.blogspot.com/2008/08/java-closures-prototype-feature.html:
The complete source code, released under GPLv2, is in the project's openjdk repository. A binary build, suitable for use with an existing JDK6, is at http://www.javac.info/closures.tar.gz. Other related documents are on the website http://www.javac.info/
Neu sind Method references aus dem FCM proposal:
{ String => int } parseInt = Integer#parseInt(String);
int x = parseInt.invoke("42");Damit wird es immer wahrscheinlicher, dass es Closures in Java 7 gibt, obwohl Zeitpunkt und Inhalte immer noch nicht feststehen.
Labels: Java 7
BGGA Closures werden wohl das Rennen in Java 7 machen
0 Kommentar(e). Veröffentlicht von Christian Ullenboom am Samstag, Mai 10, 2008.Beim http://openjdk.java.net/ Projekt ist ein Unterprojekt http://openjdk.java.net/projects/closures/ eingerichtet worden:
This goal of this Project is to produce a feature-complete prototype of the Java bytecode compiler (javac) for the draft BGGA Closures specification. This Project is sponsored by the OpenJDK Compiler Group.
Wenn es also Closures in Java schaffen -- einige Stimmen sagen, dass es dafür noch zu früh ist -- dann wohl diese Syntax. Da das Projekt sehr neu ist, befinden sich in der Mailing-Liste noch nicht viele Nachrichten.
Derweil hat sich die Closures-Spezifikation (Homepage http://www.javac.info/) von der Version 0.5 nicht weiter bewegt und auch die Open Issues sind nicht geklärt.
Interessant ist auch die http://www.javac.info/google-position.html (Neal Gafter ist Angestellter bei Google)
As of April 7, 2008, the following is Google's position on proposals to add support for closures to Java: Google believes Java platform will likely benefit from continued research into closures. To arrive at the best solution, Google is open to multiple parallel investigations but is not currently prepared to commit to any particular proposal. We do not expect these investigations to yield results in time for Java 7, and are of the opinion that it is premature to launch a JSR that forces us down any specific path.
Labels: Java 7
Java Closures : Inselupdate für Java 7
4 Kommentar(e). Veröffentlicht von Christian Ullenboom am Donnerstag, Februar 21, 2008.Mit den Closures nach Gilad Bracha, Neal Gafter, James Gosling, Peter von der Ahé.
Innere Klassen, insbesondere anonyme innere Klassen, sind bisher die einzige Möglichkeit, um Programmteile an Methoden zu übergeben. Nehmen wir einen Timer als Beispiel. Dem eigentlichen Zeitgeber muss ein Stücken Code übergeben werden, sodass der Timer weiß, was er zu tun hat. Mit dem Zeitgeber, der Klasse Timer, und der abstrakten Basisklasse TimerTask zur Beschreibung der Aufgaben, ist schnell ein Beispiel programmiert, das wie eine Uhr in jeder Sekunde die Zeit auf dem Bildschirm ausgibt:
public class TimerExample
{
public static void main( String[] args )
{
class MyTimer extends java.util.TimerTask {
@Override public void run() {
System.out.println( new java.util.Date() );
}
}new java.util.Timer().scheduleAtFixedRate( new MyTimer(), 0, 1000 );
}
}
Für die Beschreibung des Programmcodes ist extra eine eigene Klasse erforderlich. Über eine innere anonyme Klasse lässt sich der Programmcode jedoch noch etwas weiter verkürzen:
public class TimerExample
{
public static void main( String[] args )
{
new java.util.Timer().scheduleAtFixedRate( new java.util.TimerTask() {
@Override public void run() {
System.out.println( new java.util.Date() );
}
}, 0, 1000 );
}
}
Aus dem Programm ist deutlich abzulesen, dass zum "Transport" der println()-Anweisung einiges an Schreibarbeit nötig ist. Wünschenswert ist es aber, wenn der Programmcode leichter an die Funktion scheduleAtFixedRate() zu übergeben wäre. Da die bereitgestellte Funktion bisher nicht so programmiert ist, ist das Ziel, dieses nachzuprogrammieren.
Deklaration eines Closures
Ein Closure repräsentiert einen Block Java-Code. Die allgemeine Schreibweise ist
{ formal parameters => statements expression }
Die Schreibweise definiert eine anonyme Funktion. Vergleichen wir einer bekannten Funktionsdeklaration
void out()
{
System.out.printn("Hallo Welt");
}
mit der Deklaration eines Closures:
{ => System.out.printn("Hallo Welt"); }
Während normale Funktionen mit ihrem Namen aufgerufen werden, steht eine Closure für ein Objekt, welches eine invoke()-Methode anbietet.
public class Closures
{
public static void main( String[] args )
{
{ => System.out.println("Hallo Welt"); }.invoke();
}
}
Aus der allgemeinen Schreibweise
{ formal parameters => statements expression }
lässt sich absehen, dass formale Parameter wie bei normalen Funktionsdeklarationen möglich sind. So lassen sich in den Closure-Block Daten einführen. Vergleichen wir wieder eine Funktions- mit einer Closure-Deklaration:
void quote( String s )
{
System.out.println("'" + s + "'");
}
Der Closure mit Beispiel:
public class Closures
{
public static void main( String[] args )
{
{ String s => System.out.println("'" + s + "'"); }.invoke( "tutego" );
}
}
Zwei Dinge fallen an dem Beispiel auf:
• Formale Parameter haben einen Typ (links vom Pfeil steht String s ).
• Die invoke()-Funktion nimmt immer so viele Argumente an, wie es formale Parameter in der Closure-Deklaration gibt.
Die Beispiele bisher zeigen Closures, die in ihrem Rumpf eine Anweisung tragen. Closures können aber auch Ausdrücke enthalten.
public class Closures
{
public static void main( String[] args )
{
System.out.println( { int a, int b => (a + b) / 2 }.invoke( 10, 20) ); // 15
}
}
Im Rumpf endet die Ausdruck nicht mit einem Semikolon, denn es wäre ja auch verboten,
System.out.println( (a + b) / 2; );
zu schreiben.
Ein auffälliger Unterschied zur Funktion ist die fehlende return-Anweisung. Closures selbst sind quasi an die Aufrufstelle eingesetzte Programmteile und ein return würde die Funktion verlassen!
Funktions-Typ
Closures, wie { int a, int b => (a + b) / 2 }, besitzen einen so genannten Funktions-Typ, der durch die Typen der Parameter und der Rückgabe bestimmt ist. Die allgemeine Notation ist
{ formal parameters => return type }
Gibt eine Funktion nicht zurück, so steht rechts vom Pfeil void. Für unsere drei bisherigen Closures sind die Typen:
{ => void }
{ String => void }
{ int, int => int } Da in einer Funktionsdeklaration ohne Parameter ja auch kein void steht – void out(void) ist falsch – steht auch im ersten Fall links vom Pfeil nichts.
Mit diesem Funktions-Typ lassen sich die Closures ausgezeichnet referenzieren:
{ => void } printHelloWorld = { => System.out.println("Hallo Welt"); }; { String => void } printQuoted = { String s => System.out.println("'" + s + "'"); };
{ int, int => int } avg = { int a, int b => (a + b) / 2 };
printHelloWorld.invoke(); // Hallo Welt
printQuoted.invoke( "tutego" ); // 'tutego'
System.out.println( avg.invoke( 10, 20 ) ); // 15
Mit diesem Wissen lassen sich Funktionen schreiben, die ein Closure entgegennehmen. Eine Funktion repeat() soll dabei einen Programmcode so oft wie gewünscht aufrufen:
public class Repeater
{
public static void repeat( int times, { => void } block )
{
for ( int i = 0; i < times; i++ )
block.invoke();
}public static void main( String[] args )
{
repeat( 2, { => System.out.println("Hallo"); } );
}
}
Das Hallo kommt also zweimal auf den Bildschirm.
In der Java-Bibliothek sind nicht alle Funktionen so parametrisiert, so dass Closures als Parameter erlaubt sind. Schreiben wir für den Timer eine eigene Methode scheduleAtFixedRate(), die einen Codeblock entgegennimmt und ausführt:
public class TimerExample
{
public static void scheduleAtFixedRate( { => void } task, long delay, long period )
{
new java.util.Timer().scheduleAtFixedRate( new java.util.TimerTask() {
@Override public void run() {
task.invoke();
}
}, delay, period );
}
public static void main( String[] args )
{
scheduleAtFixedRate(
{ => System.out.println( new java.util.Date() ); }
, 0, 1000 );
}
}
Mit Closures sind auch Funktionszeiger leicht zu realisieren:
public class FunctionPointerWithClosures
{
static void invoker( { => void } block )
{
block.invoke();
}public static void main( String[] args )
{
{ => void } method1 = { => System.out.println("Hello www.tutego.com"); };
{ => void } method2 = { => System.out.println("Hallo www.tutego.com"); };invoker( Math.random() > 0.5 ? method1 : method2 );
}
}
Natürlich lassen sich jetzt die Methoden auch in eine Datenstruktur setzen:
Map<String, { => void }> methods = new HashMap<String, { => void }>();
methods.put( "German", { => System.out.println("Hallo www.tutego.com"); } );
methods.put( "English", { => System.out.println("Hello www.tutego.com"); } ); methods.get( "German" ).invoke();
for ( { => void } method : methods.values() )
invoker( method );
In ein Feld können Closures nicht gesetzt werden! Der Grund liegt an der internen Generics-Umsetzung.
Nehmen wir an, wir wollen eine Funktion each() schreiben, die einen String mit einem gegebenen Delimiter zerlegt und dann eine Operation auf den einzelnen Token ausführt. Soll weiterhin die Operation einen String zurückgeben, so kann das in herkömmlichem Java etwa so implementiert werden:
class StringUtils
{
public static interface StringInStringOutBlock
{
String execute( String in );
}public static String each( String source, String delimiter, StringInStringOutBlock block )
{
StringBuilder result = new StringBuilder();for ( String token : source.split( delimiter ) )
result.append( block.execute( token ) );return result.toString();
}
public static void main( String[] args )
{
class QuoterBlock implements StringInStringOutBlock {
@Override public String execute( String in ) {
return "'" + in + "'";
}
}String s = each( "Hallo Welt!", " ", new QuoterBlock() );
System.out.println( s );
}
}
Closures machen das viel kompakter:
class StringUtils
{
public static String each( String source, String delimiter, { String => String } block )
{
StringBuilder result = new StringBuilder();for ( String token : source.split( delimiter ) )
result.append( block.invoke( token ) );return result.toString();
}public static void main( String[] args )
{
String s = each( "Hallo Welt!", " ", { String in => "'" + in + "'" } );
System.out.println( s );
}
}
Closure mit Variablenzugriff
Closures haben einige Eigenschaften, die innere anonyme Klassen so erst einmal nicht haben. Eine davon ist, dass ein Closure-Block auf auch nicht-finale Werte lesend und schreiben zugreifen kann.
int i = 0;
repeat( 3, { => System.out.println(i); } ); // 0 0 0@Shared int j = 0;
repeat( 3, { => System.out.println(j++); } ); // 0 1 2
System.out.println( j ); // 3
Auffällig ist die Annotation @Shared. Annotiert sie nicht die Variablen, auf die der Closure schreibend zugreift, gibt es eine Compiler-Warnung (kein Fehler!): "warning: [shared] captured variable j not annotated @Shared".
Closure Conversion
Damit automatisch Java-Entwicklung ohne Anpassung der Bibliotheken von den Closures profitieren, haben die Sprachentwickler einen speziellen Mechanismus eingebaut: Ein Closure kann einem Interface mit einer Operation zugewiesen werden, wenn die Rückgabe- und Parametertypen übereinstimmen. Die Schnittstelle Runnable und ActionListener sind wie folgt deklariert:
public interface Runnable {
void run();
}
public interface ActionListener extends EventListener {
void actionPerformed(ActionEvent e);
} Kompatible Closures sind:
Runnable run = { => System.out.println("Nebenläufig!"); };
ActionListener listener = { ActionEvent l => System.out.println("Gedrückt!"); };
Ohne Zuweisung an Variablen ist ein nebenläufiges Programm schnell gestartet und ein Ereignisbehandler ohne viel Programmcode an einer Schaltfläche festgemacht:
new Thread( { => System.out.println("Nebenläufig!"); } ).start();
JButton b = new JButton();
b.addActionListener( { ActionEvent l => System.out.println("Gedrückt!"); } );
Diese automatische Konvertierung ist wirklich sehr praktisch, denn viele wichtige Java-Schnittstellen schreiben nur eine Operation vor.
Nach einem Vormittag mit Java-Closures kann ich sagen, dass ich gut mit der Syntax leben kann. Ich mag's! Schauen wir mal, ob sich noch groß etwas ändert.
Closures - Muss das wirklich sein?
4 Kommentar(e). Veröffentlicht von Christian Ullenboom am Mittwoch, Dezember 20, 2006.Nach dem vierten Proposal (http://www.javac.info/closures-v04.html) für Closures von Gilad Bracha, Neal Gafter (der mit dem Java-Puzzlers), James Gosling und Peter von der Ahé (nach seiner Whishlist unter http://blogs.sun.com/ahe/entry/java_se_7_wish_list werden weitere abgefahrene Sachen möglich sein) wird in der Community heftig diskutiert, ob Closures nötig sind. Ich finde sie toll! Zwar sehe ich schon meine Teilnehmer über den Ausdrücken brüten und zweifeln an der Aussage "Java ist eine einfache objektorientierte Programmiersprache" zweifeln, doch denke ich, dass es unausweichlich ist, Java mehr sprachliche Möglichkeiten zu bieten.
Java: A simple, object-oriented, network-savvy, interpreted, robust, secure, architecture neutral, portable, high-performance, multithreaded, dynamic language [...] Java omits many rarely used, poorly understood, confusing features of C++ that in our experience bring more grief than benefit. [http://java.sun.com/docs/overviews/java/java-overview-1.html]
Dass Java im Moment so viele Veränderungen erfährt ist vielleicht auch eine Frage der Wahrnehmung. Sun war mit Grammatikänderungen bisher sehr konservativ und seit mehr als 10 Jahren haben wir bis auf den Wechsel von Java 1.0 auf Java 1.1 und Java 1.4 auf Java 5.0 kaum Änderungen gesehen; strictfp/Sprachänderungen wäre man so ein Außenseiter. Nun gibt es stärkere Veränderungen, wie wir sie bei anderen Sprachen auch erleben. C# hat sich in den letzten Jahren unglaublich verändert --- was als Java-Klon begann, ist heute eigenständig und als Sprache hoch innovativ. Mitunter starke strukturelle Änderungen lassen sich auch bei Skriptsprachen ausmachen, etwa Python (trinärer Operator, try/except/finally), PHP (die ganzen OOP-Eigenschaften), Perl (für Perl 2.6 ebenfalls Änderungen der OOP-Schreibweise/perl6-language-objects und funktionale Programmierelemente geplant) aber auch C++ und SQL. Als Java-Entwickler hat uns Sun nur in den letzten Jahren so "erzogen" dass es kaum große Änderungen gab, was sich nun ändert. Hier muss Sun uns also wieder "umerziehen".
Dass Java keine einfache Programmiersprache ist, müsste jedem eigentlich bewusst geworden sein, der didaktisch versucht, Konzepte wie Generics zu vermitteln. Unternehmen müssen auch mit nicht-Cracks Software entwickeln können und können nicht darauf bauen, dass alle Mitarbeiter Spezifikationen lesen und Best-Practices befolgen. (IMHO kann man mit OOP-Spezialisten in jeder Programmiersprache exzellente Ergebnisse erziehlen.) Eine Sprache muss daher auch immer ein bisschen Narrensicher sein. (Ein kleiner Seitenhieb auf Perl, der Sprache, der man "Write Once Read Never" (WORN) nachsagt.) Da sehe ich in Closures auf jeden Fall ein Problem, denn während Generics doch noch einfach zu lesen sind (von der Deklaration sprechen wir nicht!) sieht das bei Closure-Nutzungen etwas anders aus. Hier muss der Java-Entwickler wieder eine neue Syntax lernen.
Bisher ist in der neuen Syntax kein Wort von neuen Schlüsselwörtern. Das macht die Anwendung einfacher als es bei Java 5 mit enum war. Ein weiterer Punkt ist, dass man über allgemeine Closures Dinge schreiben kann, die wie spezielle Lösungen aussehen. Ein häufig zitiertes Beispiel ist der Lock:
withLock( lock ) {System.out.println( "Closures in Java 7" );
}
Das ist eine Kurzform für
withLock( lock, {=>System.out.println( "Closures in Java 7" );
} );
Es ist gerade nicht withLock() ein spezielles Java-Konstrukt, sondern nur die Anwendung eines allgemeinen Closures. Intern wird das Umgesetzt über übliche Schnittstellen. Und mit { => } sieht man schon, wie Closures in Java aussehen können.
In der Summe finde ich Closures toll und freue mich schon. Zwar habe ich schon die ersten Ideen, wie man das didaktisch umsetzen kann, aber mal sehen, was bis dahin geschrieben wird. Was Madbean unter http://madbean.com/2006/closure/ zeigt, finde ich schon nett. Doch bis Java 7 "freigelassen" wird, dauert es noch. Wer sich schon einmal vorbereiten möchte, der sollte sich zum Beispiel die Präsentation von Neal Gafter unter http://www.bejug.org/confluenceBeJUG/display/PARLEYS/Closures+for+Java anschauen.
Christian Ullenboom
PS: Wir haben das Seminar "Dynamische Webseiten mit JavaScript und DOM" [http://www.java-tutor.com/seminare/skriptsprachen-schulung/javascript-schulung.html] aktualisiert.
Labels: Java 7
