Default-Methoden aus speziellen Oberschnittstellen ansprechen

Eine Unterklasse kann eine konkrete Methode der Oberklasse überschreiben, aber dennoch auf die Implementierung der überschriebenen Methode zugreifen. Allerdings muss der Aufruf über super erfolgen, da sich sonst ein Methodenaufruf rekursiv verfängt.

Default-Methoden können andere Default-Methoden aus Oberschnittstellen ebenfalls überschreiben und mit neuem Verhalten implementieren. Doch genauso wie normale Methoden können sie mit super auf Default-Verhalten aus dem übergeordneten Typ zurückgreifen.

Nehmen wir für ein Beispiel wieder zwei erweiterte Schnittstelle Prices und Buyable an:

interface Priced {

double price();

default boolean hasPrice() { return price() > 0; }

}

interface Buyable extends Priced {

@Override double price();

@Override default boolean hasPrice() { return Priced.super.hasPrice() || price() == 0; }

}

In der Schnittstelle Priced sagt der Default-Code von hasPrice() aus, dass alles einen Preis hat, was echt über 0 liegt. Buyable dagegen nutzt eine andere Definition und implementiert daher das Default-Verhalten neu. Denn Buyable definiert hasPrice() so, dass auch ein Preis von 0 letztendlich bedeutet, dass es einen Preis hat. In der Implementierung von hasPrice() greift Buyable auf den Default-Code von Priced zurück, um vom Obertyp eine Entscheidung über die Preiseigenschaft zu bekommen, die aber mit der Oder-Verknüpfung noch verallgemeinert wird.

Darf man in einer Java EE-Anwendung mit Class.forName(…) einen Klasse laden?

Ja, das ist erlaubt. Siehe auch http://www.oracle.com/technetwork/java/restrictions-142267.html:

Why is there a restriction against using the Java Reflection APIs to obtain declared member information that the Java language security rules would not allow? Doesn’t Java automatically enforce those rules?
Contrary to common belief, most of the Java Reflection API can be used from EJB components. For example, loadClass() and invoke() can both be used by enterprise beans. Only certain reflection methods are forbidden.

und

Why all the restrictions on creating class loaders and redirection of input, output, and error streams?
Class loading is allowed, but creating custom loaders is not, for security reasons. These restrictions exist because the EJB container has responsibility for class loading and I/O control. Allowing the EJB to perform these functions would interfere with proper operation of the Container, and are a security hazard.

Abstrakte überschriebe Schnittellenoperationen nehmen Default-Methoden weg

Hintergrund zur Einführung von Default-Methoden war die Notwendigkeit, die Java-API von Schnittstellen im Nachhinein ohne nennenswerte Compilerfehler verändern zu können. Ideal ist, wenn neue Default-Methoden hinzukommen und Standard-Verhalten definieren, und es dadurch zu keinem Compiler-Fehler für implementierende Klassen kommt, oder zu Fehlern bei Schnittstellen, die erweiterte Schnittstellen erweitern.

In unserem Beispiel haben wir eine Schnittstelle Buyable für käufliche Dinge vorgesehen. Dazu müssen die „Dinge“ aber erst einmal einen Preis haben. Führen wir eine weitere Schnittstelle Priced ein:

interface Priced {}

interface Buyable extends Priced {

double price();

}

Die Schnittstelle Prices hat keine Vorgaben, sie soll nur Typen markieren. Nehmen wir an, es gibt viele Klassen, die Priced und auch Buyable implementieren.

In der Entwicklung stellt sich nun heraus, dass Priced auch eine Methode double price() bekommen soll. Kann die Methoden einfach als abstrakte Methode ohne Probleme hinzugekommen werden? Nicht wirklich, denn wenn es viele implementierende Klassen gibt, wird es auch viele Compilerfehler geben. Aber zur problemlosen Erweiterung von Schnittstellen sind Default-Methoden da. Kann also eine Default-Methode price() in Priced aufgenommen werden, also die Schnittstelle nachträglich verändert werden, ohne dass es Compilerfehlen gibt? Ja, das geht.

interface Priced {

default double price() { return -1; }

}

interface Buyable extends Priced {

@Override double price();

}

Interessant sind zwei Details. Zunächst einmal ist in Buyable die Methode weiterhin abstrakt, was bedeutet, eine abstrakte Methode überschreib eine Default-Methode. Klassen, die Buyable implementieren, müssen also weiterhin eine price()-Methode implementieren, wenn sie nicht selbst abstrakt sein wollen. Die nachträgliche Veränderung der Oberschnittstelle hat also keine Auswirkungen auf die anderen Klassen. Im Nachhinein kann aber zur Dokumentation die Annotation @Override and die Unterschnittstelle gesetzt werden. Das ist schon spannend, dass die Implementierung einer Default-Methode weggenommen werden kann. Bei der Sichtbarkeit ist das zum Beispiel nicht möglich: ist eine Methode einmal öffentlich, kann eine Unterklasse die Sichtbarkeit nicht einschränken.

Default-Methoden, Teil 1, Update mit aktueller Java 8 Syntax

Ist eine Schnittstelle einmal verbreitet, so sollte es dennoch möglich sein, Operationen hinzuzufügen. Java 8 bringt dafür eine Sprachänderung mit, die es Entwicklern erlaubt, neue Operationen einzuführen, ohne dass Unterklassen verpflichtet werden, diese Methoden zu implementieren. Damit das möglich ist, muss die Schnittstelle eine Standard-Implementierung mitbringen. Auf diese Weise ist das Problem gelöst, denn wenn eine Implementierung vorhanden ist, haben die implementierenden Klassen nichts zu meckern, und wenn sie das Standardverhalten überschreiben möchten, können sie das gerne machen. Oracle nennt diese Methoden in Schnittstelle mit vordefinierter Implementierung Default-Methoden[1]. Schnittstellen mit Default-Methoden heißen erweiterte Schnittstellen.

Eine Default-Methode unterscheidet sich syntaktisch in zwei Aspekten von herkömmlichen implizit abstrakten Methoden-Deklarationen.

· Die Deklaration einer Default-Methode beginnt mit dem Schlüsselwort default.

· Statt dass ein Semikolon das Ende der Deklaration anzeigt, steht bei einer Default-Methode stattdessen in geschweiften Klammen ein Block mit Implementierung. Die Implementierung wollen wir Default-Code nennen.

Sonst verhalten sich erweiterte Schnittstellen wie normale Schnittstellen. Eine Klasse, die eine Schnittstelle implementiert, erbt alle Operationen, sei es die abstrakten Methoden oder die Default-Methoden. Falls die Klasse nicht abstrakt sein soll muss sie alle von der Schnittstelle geerbten abstrakten Methoden realisieren; sie kann die Default-Methoden überschreiben, muss das aber nicht, denn eine Vorimplementierung ist ja schon gegeben.

Realisieren wir dies in einem Beispiel. Für Spielobjekte soll ein Lebenszyklus möglich sein; der besteht aus start() und finish(). Der Lebenszyklus ist als Schnittstelle vorgegeben, die Spielobjektklasse implementieren können. Version 1 der Schnittstelle sieht also aus:

interface GameLifecycle {

void start();

void finish();

}

Klassen wie Player, Room, Door können die Schnittstellen erweitern, und wenn sie dies tun, müssen sie die beiden Methoden implementieren. Bei Spielobjekten, die diese Schnittstelle implementieren, kann unser Hauptprogramm, das Spiel, diese Methoden aufrufen und den Spielobjekten Rückmeldung geben, ob sie gerade in das Spiel gebracht wurden, oder sie aus dem Spiel entfernt wurden.

Je länger Software lebt, desto mehr bedauern Entwickler Designentscheidungen. Die Umstellung einer ganzen Architektur ist eine Mammutaufgabe, einfache Änderungen wie das Umbenennen sind über ein Refactoring schnell erledigt. Nehmen wir an, auch bei unserer Schnittstelle gibt es einen Änderungswunsch – nur die Initialisierung und das Ende zu melden reicht nicht. Geht das Spiel in einen Pausenmodus, soll ein Spielobjekt die Möglichkeit bekommen, im Hintergrund laufende Programme anzuhalten. Das soll durch eine zusätzliche pause()-Methode in der Schnittstelle realisiert werden. Hier spielen uns die Default-Methoden perfekt in die Hand, denn wir können die Schnittstelle erweitern, aber eine leere Standardimplementierung mitgeben. So müssen Unterklassen die pause()-Methode nicht implementieren, können dies aber; Version 2 der nun erweiterten Schnittstelle GameLifecycle:

interface GameLifecycle {

void start();

void finish();

default void pause() {}

}

Klassen, die GameLifecycle schon genutzt haben, bekommen von der Änderung nichts mit. Der Vorteil: Die Schnittstelle kann sich weiter entwickeln, aber alles bleibt binärkompatibel und nichts muss neu compiliert werden. Vorhandener Code kann auf die neue Methode zurückgreifen, die automatisch mit der Implementierung vorhanden ist. Weiterhin verhalten sich Default-Methoden wie andere Methoden von Schnittstellen auch: es bleibt bei der dynamischen Bindung, wenn implementierende Klassen die Methoden überschreiben. Wenn eine Unterklasse wie Flower zum Beispiel bei der Spielpause nicht mehr blühen möchte, so überschreibt sie die Methode und lässt den Timer pausieren. Eine Tür dagegen hat nichts zu stoppen und kann pause() mit dem Default-Code so übernehmen.

Hinweis

Statt des leeren Blocks könnte der Rumpf auch throw new UnsupportedOperationException("Not yet implemented"); beinhalten, um anzukündigen, dass es keine Implementierung gibt. So führt eine hinzugenommene Default-Methode zwar zu keinem Compilerfehler, aber zur Laufzeit führen nicht überschriebene Methoden zu einer Ausnahme. Erreicht ist das Gegenteil vom Default-Code, weil eben keine Logik standardmäßig ausgeführt wird;das Auslösen einer Ausnahme zum Melden eines Fehlers wollen wir nicht als Logik ansehen.

Kontext der Default-Methoden

Default-Methoden verhalten sich wie Methoden in abstrakten Klassen und können alle Methoden der Schnittstelle (inklusive der geerbten Methoden) aufrufen. Die Methoden werden später dynamisch zur Laufzeit gebunden.

Nehmen wir eine Schnittstelle Buyable für käufliche Objekte:

interface Buyable {
  double price();
}

Leider schreibt die Schnittstelle nicht vor, ob Dinge überhaupt käuflich sind. Eine Methode wie isBuyable() wäre in Buyable ganz gut aufgehoben. Was kann aber die Default-Implementierung sein? Wir können auf price() zurückgreifen und testen, ob die Rückgabe ein gültiger Preis ist. Das soll gegeben sein, wenn der Preis echt größer 0 ist.

interface Buyable {
  double price();

default boolean isBuyable() { return price() > 0; }
}

Implementierende Klassen erben die Methode isBuyable() und beim Aufruf geht der interne Aufruf von price() an genau die Klasse, die Buyable und die Methode implementiert.

Hinweis

Eine Schnittstelle kann die Methoden der absoluten Oberklasse java.lang.Object ebenfalls deklarieren, etwa um mit Javadoc eine Beschreibung hinzuzufügen. Allerdings ist es nicht möglich, mit Default-Code Methoden wie toString() oder hashCode() vorzubelegen.

Neben der Möglichkeit auf Methoden zuzugreifen, steht auch die this-Referenz zur Verfügung. Das ist sehr wichtig, denn so kann der Default-Code an Utility-Methoden weiterreichen und einen Verweis auf sich selbst übergeben. Hätten wir zum Beispiel schon eine isBuyable(Buyable)-Methode in einer Utiltiy-Klasse PriceUtils implementiert, so könnte der Default-Code aus einer einfachen Weiterleitung bestehen:

class PriceUtils {

public static boolean isBuyable( Buyable b ) { return b.price() > 0; }

}

interface Buyable {

  double price();

default boolean isBuyable() { return PriceUtils.isBuyable( this ); }
}

Dass die Methode PriceUtils.isBuyable(Buyable) für den Parameter den Typ Buyable vorsieht und sich der Default-Code mit this auf genau so ein Buyable-Objekt bezieht, ist natürlich kein Zufall, sondern bewusst gewählt. Der Typ der this-Referenz zur Laufzeit entspricht dem der Klasse, die die Schnittstelle implementiert hat und dessen Objektexemplar gebildet wurde.

Haben die Default-Methoden weitere Parameter, so lassen sie auch diese weiter an die statische Methode reichen:

class PriceUtils {

public static boolean isBuyable( Buyable b ) { return b.price() > 0; }

public static double defaultPrice( Buyable b, double defaultPrice ) {

if ( b != null && b.price() > 0 )

return b.price();

return defaultPrice;

}

}

interface Buyable {
  double price();

default boolean isBuyable() { return PriceUtils.isBuyable( this ); }

default double defaultPrice( double defaultPrice ) {

return PriceUtils.defaultPrice( this, defaultPrice ); }
}

Es ist vorzuziehen, die Implementierung auszulagern, um die Schnittstellen nicht so Code-lastig werden zu lassen. Nutzt das JDK Default-Code, so gibt es in der Regel immer eine statische Methode in einer Utility-Klasse.

Neue Möglichkeiten mit Default-Methoden *

Default-Methoden geben Bibliotheksdesignern ganz neue Möglichkeiten. Heute ist noch gar nicht richtig abzusehen, was Entwickler damit machen werden und welche Richtung die Java-API einschlagen wird. Auf jeden Fall wird sich die Frage stellen, ob Standard-Implementierung als Default-Code in Schnittstellen wandert, oder wie bisher, Standard-Implementierungen als abstrakte Klasse bereitgestellt wird, von dem wiederum andere Klassen ableiten. Als Beispiel sei auf die Datenstrukturen verwiesen: Eine Schnittstelle Collection schreibt Standardverhalten vor, AbstractCollection gibt eine Implementierung soweit möglich vor, und Unterklassen wie Listen setzen dann noch einmal auf diese Basisimplementierung auf. Erweiterte Schnittstellen können Hierarchien abbauen, denn auf eine abstrakte Basisimplementierung kann verzichtet werden. Auf der anderen Seite kann aber eine abstrakte Klasse Zustand über Objektvariablen einführen, was eine Schnittstelle nie könnte.

Default-Methoden können aber noch etwas ganz anderes: Sie können als Bauelemente für Klassen dienen. Eine Klasse kann mehrere Schnittstellen mit Default-Methoden implementieren und erbt im Grunde damit Basisfunktionalität von verschiedenen Stellen. In anderen Programmiersprachen ist das als Mixin bzw. Trait bekannt.


[1] Der Name hat sich während der Planung für dieses Feature mehrfach gewandelt. Ganz am Anfang war der Name „defender methods“ im Umlauf, dann lange Zeit virtuelle Erweiterungsmethoden (engl. virtual extension methods).

PDFs in/mit/aus Java erstellen, ein leidiges Thema

  • Selbst die PDF erstellen, mit allen Linien, Grafiken, Texten. Das geht etwa mit iText. Nachteil: Sehr aufwändig, insbesondere wenn der Kunde einmal sagt: Die Box bitte noch ein wenig nach links oben.
  • Mit Report-Programmen wie BIRT oder Japser arbeiten, die dann über iText die PDF rausspucken. Vorteil: Netter Report-Designer. Nachteil: Die Designer sind im Sekretariat, die nur Word oder vielleicht OO (OpenOffice, Libre Office) kennen, ungewohnt.
  • PDF mit XForms erstellen. Siehe OpenOffice Draw + XForms Export + iText = PDF. Das mache ich bei unseren Rechnungen so. Läuft gut. Die Vorlagen erstelle ich mit OO. Weiterer Vorteil: Sekretariat kann selbst das aussehen verändern.
  • Word/Excel/OO/RTF-Templates nutzen, dann in PDF konvertieren. Zum Füllen der Vorlagen gibt es einige (auch open-source) Lösungen, siehe Apache POI, JExelApi, xdocreport, jRTF. Doch dann kommt das Problem: Das in PDF zu konvertieren. Mann kann nun die eigentlichen Programme nutzen und einen PDF-Export mit einem Druckertreiber verwenden, oder das automatisieren. Entweder über Java oder nicht-Java-Programme (etwa der Shell). Im Java-open-source Bereich gibt es hier nach meinem Kenntnisstand nichts wirklich Funktionierendes. Mit OO kann man die UNO-Brücke nutzen, das kappt mit ODT usw. sehr gut, aber man braucht eine Installation und das ist relativ langsam für Massenexports. jOpenDocument Homepage. Open Document library macht das in purem Java, ist aber noch nicht so weit. xdocreport sollte das für Word können, das Resultat ist bei meiner Vorlage aber unbrauchbar. xdocreport kann auch mit FOP -> iText -> PDD arbeiten, das habe ich noch nicht getestet.

Repeating Annotations in Java 8 Build 68 eingezogen

Was das ist: http://openjdk.java.net/jeps/120

In der Klasse Class ist hinzugekommen:

public <A extends Annotation> A[] getAnnotations(Class<A> annotationClass)

public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass)

public <A extends Annotation> A[] getDeclaredAnnotations(Class<A> annotationClass)

Und noch ein paar weitere Ergänzungen in Package, System, …

Neu sind ebenfalls ContainerFor und ConainedBy.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7154390, http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/735b93462eed

Buchkritik: The Clean Coder von Robert C. Martin

Ich mag das Buch von Robert Martin (Uncle Bob), weil es offen und ehrlich ist. Selten sagen Entwickler und Berater: „Da habe ich Mist gemacht, ich war blöd, das war dumm“. Robert ist da schonungslos und legt offen, an welchen Stellen er in seinem Leben vor die Wand fuhr. Daraus formuliert er Grundsätze, von denen wir alle lernen können. Dabei ist das Buch relativ untechnisch. Vielmehr steht der Entwickler als Mensch als soziales und mitunter selbstherrliches Wesen im Vordergrund; Coding-Tipps wie Pattern oder Idiome fehlen gänzlich. Aber das ist gut so, denn viel zu oft denken gerade Neueinsteiger, es kommt auf die coole Programmiersprache an, oder Meister der Kommandozeile zu sein. Alles falsch bzw. das ist nur ein Teil. Softwareentwicklung ist ein kollaborativer Prozess. Man muss „Nein“ sagen zu Dingen, die man nicht einhalten kann und ein „Ja“ bedeutet, ein Commitment zu machen, um verlässlich im Team zu arbeiten. So ist auch „Ja“/“Nein“ zu sagen, und die Konsequenzen zu tragen, ein zentraler Bestandteil des Buchs. Weitere Themen sind Zeiteinteilungen, Arbeiten unter Druck und immer wieder, was ein professionellen Entwickler auszeichnet: Testen, Testen, Testen. Da der perfekte professionelle Entwickler nicht geboren, sondern im Laufe der Jahre heranreift, nimmt auch diese Entwicklung im Buch Platz ein. Zentrale Themen bei Martin sind: Programmieraufgaben (was man selbst tun kann) und Mentoring (was die Umgebung/Arbeitgeber tun kann). Wer schön länger im Geschäft ist muss das Buch nicht lesen, denn jeder wird sich dort wiedererkennen; jeder angehende Softwareentwickler sollte das Buch aber lesen und einen Eindruck gewinnen, worauf man sich einlässt und was es heißt, professionell zu arbeiten.

http://www.amazon.de/Clean-Coder-Professional-Programmers-Paperback/dp/B006V37B1G/ref=sr_1_5?s=books&ie=UTF8&qid=1355651332&sr=1-5%20&tag=tutego-21

jdep: Paket-Abhängigkeiten anzeigen, neues Tool für Java 8 in Planung

http://mail.openjdk.java.net/pipermail/core-libs-dev/2012-December/012684.html. Aufruf:

$ jdep -h
Usage: jdeps <options> <files....>
where possible options include:
   -version                 Version information
   -classpath <path>        Specify where to find class files
   -summary                 Print dependency summary only
   -v:class                 Print class-level dependencies
   -v:package               Print package-level dependencies
   -p <package name>        Restrict analysis to classes in this package
                            (may be given multiple times)
   -e <regex>               Restrict analysis to packages matching pattern
                            (-p and -e are exclusive)
   -P  --profile            Show profile or the file containing a package
   -R  --recursive          Traverse all dependencies recursively
   -all                     Process all classes specified in -classpath

$ jdep Notepad.jar Ensemble.jar
Notepad.jar -> D:\tools\devtools\jdk8\windows-i586\jre\lib\rt.jar
<unnamed> (Notepad.jar)
       -> java.awt
       -> java.awt.event
       -> java.beans
       -> java.io
       -> java.lang
       -> java.net
       -> java.util
       -> java.util.logging
       -> javax.swing
       -> javax.swing.border
       -> javax.swing.event
       -> javax.swing.text
       -> javax.swing.tree
       -> javax.swing.undo

Ensemble.jar -> D:\tools\devtools\jdk8\windows-i586\jre\lib\jfxrt.jar
Ensemble.jar -> D:\tools\devtools\jdk8\windows-i586\jre\lib\rt.jar
    com.javafx.main (Ensemble.jar)
       -> java.applet
       -> java.awt
       -> java.awt.event
       -> java.io
       -> java.lang
       -> java.lang.reflect
       -> java.net
       -> java.security
       -> java.util
       -> java.util.jar
       -> javax.swing
       -> sun.misc                                 JDK internal API (rt.jar)

Thema der Woche: Servlet-Filter

Was Nordkorea kann, können wir auch. Lies zur Einleitung http://www.spiegel.de/netzwelt/web/html-trick-nordkorea-macht-seinen-fuehrer-groesser-a-872291.html, um eine Idee zu bekommen, was wir vorhaben.

Lege in der IDE ein Web-Projekt an. Setze eine index.jsp in das Wurzelverzeichnis mit Text, der den Namen “Kim Jong Un” enthält (alternativ “Angela Merkel”; je nachdem, wer einem sympathischer ist). Teste die Seite.

Lies http://www.oracle.com/technetwork/java/filters-137243.html und verstehe das Konzept der Filter. Der Code ist mit den Eintragungen in die web.xml nicht mehr ganz aktuell, den letzten Teil kann man daher überspringen.

Kopiere http://www.roseindia.net/java/javaee6/webFilterExample.shtml und probiere es aus. Das Beispiel nutzt die Annotationen aus der aktuellen Servlet-Spezifikation.

Ändere den Filter, so dass er den Namen unseres gewählten Staatsoberhaupts größer setzt. Wie das realisiert ist, ist im Prinzip egal, die einfachster Lösung ist Ok (etwa mit <font size=+1>name</font>).

Java-Versionen gehen mit Unicode-Standard Hand in Hand

In den letzten Jahren hat sich der Unicode-Standard erweitert, und Java ist den Erweiterungen gefolgt.

Java-Version

Unicode-Version

1.0

1.1.5

1.1

2.0

1.1.7

2.1

1.2, 1.3

2.1

1.4

3.0

5

4.0

6

4.0

7

6.0

8

6.2

Java-Versionen und ihr unterstützter Unicode-Standard

Die Java-Versionen von 1.0 bis 1.4 nutzen einen Unicode-Standard, der für jedes Zeichen 16 Bit reserviert. So legt Java jedes Zeichen in 2 Byte ab und ermöglicht die Kodierung von mehr als 65.000 Zeichen aus dem Bereich U+0000 bis U+FFFF. Der Bereich heißt auch BMP (Basic Multilingual Plane). Java 5 unterstützt erstmalig den Unicode 4.0-Standard, der 32 Bit (also 4 Byte) für die Abbildung eines Zeichens nötig macht. Doch mit dem Wechsel auf Unicode 4 wurde nicht die interne Länge für ein Java-Zeichen angehoben, sondern es bleibt dabei, dass ein char 2 Byte groß ist. Das heißt aber auch, dass Zeichen, die größer als 65.536 sind, irgendwie anders kodiert werden müssen. Der Trick ist, ein ein »großes« Unicode-Zeichen aus zwei chars zusammenzusetzen. Dieses Pärchen aus zwei 16-Bit-Zeichen heißt Surrogate-Paar. Sie bilden in der UTF-16-Kodierung ein Unicode 4.0-Zeichen. Diese Surrogate vergrößern den Bereich der Basic Multilingual Plane.

Mit der Einführung von Unicode 4 unter Java 5 gab es an den Klassen für Zeichen- und Zeichenkettenverarbeitung einige Änderungen, sodass etwa eine Methode, die nach einem Zeichen sucht, nun nicht nur mit einem char parametrisiert ist, sondern auch mit int, und der Methode damit auch ein Surrogate-Paar übergeben werden kann. In diesem Buch spielt das aber keine Rolle, da Unicode-Zeichen aus dem höheren Bereichen, etwa für die phönizische Schrift, die im Unicode-Block U+10900 bis U+1091F liegt – also kurz hinter 65536, was durch 2 Byte abbildbar ist –, nur für eine ganz kleine Gruppe von Interessenten wichtig sind.

Statische sum(…)/max(…)/min(…) Methoden in numerischen Wrapper-Klassen

In den numerischen Wrapper-Klassen, also Byte, Short, Integer, Long, Float, Double und auch Character – obwohl Character nicht die Basisklasse Number erweitert – gibt es seit Java 8 je drei neue Methoden: sum(…)/max(…)/min(…), die genau das machen, was der Methodenname verspricht.

final class java.lang.Byte|Short|Integer|Long|Float|Double
extends Number
implements Comparable<Integer>

§ static Typ sum(Typ a, Typ b)
Bildet die Summe zweier Werte und liefert diese zurück. Es entspricht einem einfachen a + b. Die Angabe Typ steht dabei für den entsprechenden primitiven Typ byte short, int, long, float oder double, etwa in int sum(int a, int b).

§ static Typ min(Typ a, Typ b)
Liefert das Minimum der zwei Zahlen.

§ static Typ max(Typ a, Typ b)
Liefert das Maximum der zwei Zahlen.

final class java.lang.Character
implements Comparable<Character>, Serializable

§ static Typ sum(Typ a, Typ b)
Liefert (char)(a + b) zurück.

§ static Typ min(Typ a, Typ b)
Liefert das kleinere der beiden Zeichen bezüglich der Unicode-Position.

§ static Typ max(Typ a, Typ b)
Liefert das größere der beiden Zeichen.

Die Methoden sich für sich genommen nicht spannend. Für die Summe (Addition) tut es genauso gut der +-Operator – er steckt sowieso hinter den sum(…)-Methoden – und so wird keiner auf die Idee kommen i = Integer.sum(i, 1) statt i++ zu schreiben. Für das Maximum/Minimum bietet die Math-Klasse auch schon entsprechende Methoden min(a,b)/max(a,b). Der Grund für diese drei Methoden ist vielmehr, dass sie im Zusammenhang mit Lambda-Ausdrücken interessant sind – dazu später mehr.

Java 8: Division mit Rundung Richtung negativ unendlich

Die Ganzzahldivision in Java ist simpel gestrickt. Vereinfacht ausgedrückt: Konvertiere die Ganzzahlen in Fließkommazahlen, führe die Division durch und schneide alles hinter dem Komma ab. So ergeben zum Beispiel 3 / 2 = 1 und 9 / 2 = 4. Bei negativem Ergebnis, durch entweder negativen Dividenden oder Divisor, das gleiche Spiel: -9 / 2 = -4 und 9 / -2 = -4. Schauen wir uns einmal die Rundungen an.

Ist das Ergebnis einer Division positiv und mit Nachkommaanteil, so wird das Ergebnis durch das Abschneiden der Nachkommastellen ein wenig kleiner, also abgerundet. Wäre 3/2 bei Fließkommazahlen 1,5, ist es bei einer Ganzzahldivision abgerundet 1. Bei negativen Ergebnissen einer Division ist das genau anders herum. Denn durch das Abschneiden der Nachkommastellen wird die Zahl etwas größer. -3/2 ist genau genommen -1,5, aber bei der Ganzzahldivision -1. Doch -1 ist größer als -1,5. Java wendet ein Verfahren an, was gegen null rundet.

In Java 8 hat die Mathe-Klasse zwei neue Methoden bekommen, die bei negativem Ergebnis einer Division nicht gegen null runden, sondern gegen negativ unendlich, also auch in Richtung der kleineren Zahl, wie es bei den positiven Ergebnissen ist.

class java.lang.Math

– static int floorDiv(int x, int y)

– static long floorDiv(long x, long y)

Ganz praktisch heißt das: 4/3 = Math.floorDiv(4, 3) = 1, aber wo -4 / 3 = -1 ergibt, liefert Math.floorDiv(-4, 3) = -2.