Siehe https://www.eclipse.org/eclipse/news/4.16/jdt.php zu den Neuerungen.
Autor: Christian Ullenboom
Google Guava in Version 29 erschienen
Zu den Details: https://github.com/google/guava/releases.
Changelog
- Guava types can no longer be sent over GWT-RPC. To temporarily reenable support, set the
guava.gwt.emergency_reenable_rpc
system property totrue
. (5214a10)- This is the only breaking change in this release, and it affects only users of the
guava-gwt
artifact, not people who use only theguava
artifact. This release contains no changes that break binary compatibility for any users.
- This is the only breaking change in this release, and it affects only users of the
- API documentation for Guava classes is now easier to reach. For example, for
ImmutableList
, visit guava.dev/ImmutableList. Also, more easily access the index at guava.dev/api. collect
: AnnotatedFluentIterable.from(FluentIterable)
with@DoNotCall
. (b1c77b7)collect
: Madeceiling
,floor
,headSet(E, boolean)
, andtailSet(E, boolean)
methods available in the GWT-emulatedImmutableSortedSet
. (7e0fe90, 5f2fbf2)graph
: Made it possible to set a stable incident edge order by calling the newly added method[Value]Graph.Builder.incidentEdgeOrder(ElementOrder.stable())
. (7016402)graph
: AddedincidentEdgeOrder()
to the[Value]Graph
interfaces. (cde576e)util.concurrent
: AddedDuration
-baseddefault
methods toListeningScheduledExecutorService
. (931e83f)util.concurrent
: AddedimmediateVoidFuture
. (9f3bae5)util.concurrent
: Removed@Beta
fromService
and related classes. (dc46627)util.concurrent
: Deprecated the 1-arg overload ofServiceManager.addListener
. (86e3620)util.concurrent
: Changed the return type ofServiceManager.servicesByState()
toImmutableSetMultimap
(but also retained a method with the old signature for binary compatibility). (31999ae)util.concurrent
: Made it safe to load theAbstractFuture
class from aForkJoinPool
thread under a security manager. (6e0c5b5)
Update der Java-Aufgaben
Die Aufgaben gibt es unter http://tutego.de/javabuch/aufgaben/ — viele Aufgaben sind mit Lösungsvorschlägen. Neue Lösungen nehme ich gerne entgegen und können auch gerne unter den Kommentaren gepostet werden.
Java 14 ist fertig
http://www.tutego.de/java/Java-14-OpenJDK-14-Oracle-JDK-14-Java-SE-Development-Kit-14.html
Warten wir nun auf Java 15 und darauf, dass es weiter geht und nicht alles nur ein Preview bleibt. Und hoffen wir, dass viel mehr auf Java 11 aufwärts setzen und wir endlich von Java 8 wegkommen.
Switch Expressions
Java hat seit Version 1.0 eine switch-Anweisung zum Kontrollfluss. Im Wesentlichen basiert die Syntax auf der Programmiersprache C, die auf die 1970er Jahre zurückgeht. In Java 12 wurde eine neue Syntax probeweise eingeführt, in Java 13 verändert und in Java 14 endgültig integriert.
Insgesamt kann switch in vier Formen auftauchen:
Anweisung/Ausdruck | Ab Java-Version | Syntax | Durchfall | vollständige Abdeckung |
Anweisung | 1.0 | : | Ja | Nein |
Anweisung | 14 | -> | Nein | Nein |
Ausdruck | 14 | : | Ja | Ja |
Ausdruck | 14 | -> | Nein | Ja |
Vier Typen von switch
Den ersten Typ haben wir schon ausgiebig betrachtet, schauen wir uns die weiteren Varianten an.
Vereinfachte Switch-Anweisung, kein Durchfall, keine vollständige Abdeckung
Bei der vereinfachten switch-Anweisung steht hinter dem Label bzw. default kein Doppelpunkt, sondern ein ->. Dieser hat nichts mit Lambda-Ausdrucken zu tun, auch wenn die Symbole gleich sind. Hinter dem Pfeil steht entweder ein Ausdruck, ein Block in geschweiften Klammern oder ein throw-Anweisung, die eine Ausnahme auslöst. Implizit beendet ein break jeden Zweig, es gibt also kein Durchfallen mehr.
Beispiel
String operator = "+"; switch ( operator ) { case "+" -> System.out.println( "Plus" ); case "-" -> { String minus = "Minus"; System.out.println( minus ); } }
Dadurch, dass bei mehreren Anweisungen immer Blöcke gesetzt werden müssen, tritt eine lokale Variable auch nicht aus dem Bereich aus.
Ein default kann gesetzt werden, muss aber nicht. Das switch muss nicht jede Möglichkeit abdecken, was bei Zahlen und Strings eh nicht funktioniert.
Beispiel
String operator = "+"; switch ( operator ) { case "+" -> System.out.println( "Plus" ); case "-" -> System.out.println( "Minus" ); default -> System.out.println( "Unbekannter Operator" ); }
Bei vereinfachten switch-Anweisungen sind mehrere Labels möglich, die die gleiche Behandlung haben. Kommas trennen die Labels.
Beispiel
String operator = "+"; switch ( operator ) { case "+" -> System.out.println( "Plus" ); case "*", "×" -> System.out.println( "Mal" ); }
Switch-Ausdrücke, kein Durchfall, vollständige Abdeckung
Traditionell finden sich die Fallunterscheidungen mit switch als Anweisung und Anweisungen geben nichts zurück. In Java 14 ist es möglich, switch als Ausdruck mit Ergebnis zu nutzen.
Beispiel
String operator = "+"; String writtenOperator = (switch ( operator ) { case "+" -> "Plus"; case "-" -> "Minus"; default -> "Unbekannter Operator"; } ).toUpperCase(); System.out.println( writtenOperator );
Ausdrücke müssen immer Ergebnisse liefern, und folglich muss switch immer einen Pfad auf einen Wert nehmen. Der übliche Fall ist default wie gezeigt, es gibt allerdings Sonderfälle, wie bei Aufzählungen, wo der Compiler prüfen kann, dass alle Möglichkeiten abgedeckt sind.
Beispiel
DayOfWeek today = LocalDate.now().getDayOfWeek(); System.out.println( switch ( today ) { case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Arbeit"; case SATURDAY, SUNDAY -> "Party"; } );
Falls rechts neben dem Pfeil kein einfacher Ausdruck steht, sondern ein Block muss auch dieser Block ein Ergebnis zurückgeben. Dafür wird das neue Schlüsselwort yield eingesetzt, hinter dem ein Ausdruck kommt.
Beispiel
String operator = "+"; System.out.println( switch ( operator ) { case "+" -> "Plus"; case "-" -> { String minus = "Minus"; yield minus; } default -> throw new IllegalArgumentException( "Unknown operator" ); } );
Ein Block muss ein yield besitzen oder eine ungeprüfte Ausnahme auslösen.
Switch-Expression mit :-Syntax, mit Durchfall, vollständige Abdeckung
Auch die Doppelpunkt-Syntax lässt sich als Ausdruck einsetzten, mit ihr ist auch ein Durchfall wieder möglich; ein yield ist zwingend, oder eine ungeprüfte Ausnahme. Die Syntax birgt mit dem Durchfallen eine Fehlerquelle, sodass es vielleicht die schlechteste Variante ist.
Beispiel
String operator = "+"; System.out.println( switch ( operator ) { case "+" : yield "Plus"; case "*" : System.out.println( "Sternchen" ); case "×" : yield "Mal"; default : throw new IllegalArgumentException( "Unknown operator" ); } );
Groovy 3 mit vielen Sprachänderungen
Groovy ist eine Programmiersprache auf der JVM, die bald schon 20 Jahre in der Entwicklung ist. In den letzten Jahren ist es leise um die Sprache geworden und man bekommt den Eindruck, dass Groovy zur Konfigurationssprache für Gradle-Builds degradiert ist.
Nun ist Groovy 3 erschienen mit einem ganz neuen Parser, Parrot. Die Anzahl der Neuerungen ist vielfältig, weil man Groovy nun als echte Erweiterung von Java aufgebaut hat. An vielen kleinen Stellen gab es vorher Fehler, Lambda-Ausdrücke waren anders, Array-Initialisierungen waren mit {} nicht möglich, und vieles mehr.
Zum Nachlesen aller Neuerungen: https://groovy-lang.org/releasenotes/groovy-3.0.html
Dummy, Fake, Stub und Mock
Gute objektorientiert entworfene Systeme zeichnen sich dadurch aus, dass es eine hohe Interaktion mit anderen Objekten gibt. Idealerweise zerlegt eine Klasse ein Problem nur bis zu dem Punkt, an dem es sich einer anderen Klasse bedienen kann, die dieses einfachere Problem löst. Schwierig wird es, wenn eine eigene Klasse auf eine andere komplexe Klasse zurückgreift und das Objekt nur dann sinnvoll arbeitet, wenn das referenzierte Objekt da ist und irgendwie sinnvoll antwortet. Diese Abhängigkeit ist ungünstig, denn das Ziel eines guten Tests besteht ja darin, lokal zu sein, also die eigentliche Klasse zu testen und nicht alle referenzierten Klassen um sie herum gleich mit.
In der Praxis begegnen uns drei Hilfskonstrukte, die die Lokalität von Tests ermöglichen:
- Fake-Objekte: Sie sind eine gültige Implementierung einer Schnittstelle. Wenn zum Beispiel ein Repository auf die Datenbank geht, kann ein Fake-Implementierung Datensätze in einer Datenstruktur speichern. Das Verhalten ist nachgebildet und vereinfacht, aber funktionsfähig. So liefert ein Fake-Repository statt Kunden aus der Datenbank immer die gleichen N vorgefertigten Kunden. Fake-Objekte sind auch praktisch, wenn zum Beispiel eine GUI-Anwendung programmiert wird, die statt echter Datenbankdaten erst einmal mit den Fake-Objekten entwickelt wird und so die Demodaten anzeigt. Wenn ein Team die GUI baut und ein anderes Team den Service, so können beide Gruppen unabhängig arbeiten, und das GUI-Team muss nicht erst auf die Implementierung warten.
- Stub-Objekte: Stub-Objekte implementieren ein bestimmtes Protokoll, sodass sie für den Testfall immer die gleichen Antworten geben können. Wenn etwa ein E-Mail-Service eine Methode isTransmitted() anbietet, so kann der Stub immer true liefern. Stubs haben also kein Verhalten, sondern der Rumpf der Methoden ist quasi leer und minimal. Sie gibt es nur für die Testfälle.
- Mock-Objekte: Mock-Objekte werden von einem Testfall »aufgeladen« und zeigen dann das gewünschte Verhalten – sie liefern also nicht wie Stubs immer das gleiche Ergebnis. In der Regel werden Mock-Objekte durch Bibliotheken wie mockito(http://mockito.org) oder EasyMock (http://easymock.org) automatisch zur Laufzeit erzeugt.
Diese drei Typen können wir unter dem Oberbegriff Dummy-Objekt zusammenfassen. Grundsätzlich gilt bei den vier Begriffen aber, dass sie von Autoren nicht einheitlich verwendet werden.[1]
Beispiel: Mockito-Beispiel
Nehmen wir an, alles aus org.mockito.Mockito.* ist statisch importiert und wir wollen eine java.util.List aufbauen. Dazu muss Mockito erst etwas aufbauen, was sich wie List verhält:
List<?> mockedList = mock( List.class );
Im nächsten Schritt muss das Verhalten der speziellen Liste bestimmt werden:
when( mockedList.get(0) ).thenReturn( „tutego“ );
Anschließend ist die Liste bereit zur Nutzung:
System.out.println( mockedList.get(0) ); // tutego
[1] Die Seite http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dummies.html stellt einige Autoren mit ihrer Begriffsnutzung vor.
Maven 3.6.3 freigegeben
Snippet: JAXB and CDATA ContentHandler with CharacterEscapeHandler
Main program:
package com.tutego.jaxb; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import java.io.PrintWriter; public class App { public static void main( String[] args ) throws Exception { Dog dog = new Dog(); dog.name = "Wüffi"; Flea flea = new Flea(); flea.name = "<><> Böser Floh <><>"; dog.flea = flea; JAXBContext jaxbContext = JAXBContext.newInstance( dog.getClass() ); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); jaxbMarshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true ); jaxbMarshaller.marshal( dog, new CDATAContentHandler( new PrintWriter( System.out ) ) ); } }
Dog and Flea:
package com.tutego.jaxb; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class Dog { public String name; public Flea flea; } class Flea { public String name; }
CDATAContentHandler:
package com.tutego.jaxb; import com.sun.xml.txw2.output.CharacterEscapeHandler; import com.sun.xml.txw2.output.DataWriter; import org.xml.sax.SAXException; import java.io.IOException; import java.io.Writer; public class CDATAContentHandler extends DataWriter { public CDATAContentHandler( Writer writer ) { super( writer, "UTF-8", MinimumEscapeHandler.theInstance ); } @Override public void characters( char[] ch, int start, int length ) throws SAXException { boolean useCData = false; loop: for ( int i = start; i < start + length; i++ ) switch ( ch[ i ] ) { case '<': case '>': case '&': useCData = true; break loop; } if ( useCData ) super.startCDATA(); super.characters( ch, start, length ); if ( useCData ) super.endCDATA(); } } /** * Performs no character escaping. Usable only when the output encoding * is UTF, but this handler gives the maximum performance. * * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) */ class MinimumEscapeHandler implements CharacterEscapeHandler { private MinimumEscapeHandler() { } // no instanciation please public static final CharacterEscapeHandler theInstance = new MinimumEscapeHandler(); public void escape( char[] ch, int start, int length, boolean isAttVal, Writer out ) throws IOException { // avoid calling the Writerwrite method too much by assuming // that the escaping occurs rarely. // profiling revealed that this is faster than the naive code. int limit = start + length; for ( int i = start; i < limit; i++ ) { char c = ch[ i ]; if ( c == '&' || c == '<' || c == '>' || c == '\r' || (c == '\n' && isAttVal) || (c == '\"' && isAttVal) ) { if ( i != start ) out.write( ch, start, i - start ); start = i + 1; switch ( ch[ i ] ) { case '&': out.write( "&" ); break; case '<': out.write( "<" ); break; case '>': out.write( ">" ); break; case '\"': out.write( """ ); break; case '\n': case '\r': out.write( "&#" ); out.write( Integer.toString( c ) ); out.write( ';' ); break; default: throw new IllegalArgumentException( "Cannot escape: '" + c + "'" ); } } } if ( start != limit ) out.write( ch, start, limit - start ); } }
Visual Studio Code bekommt ein Java-Update
Gradle 6.0 freigegeben
Zu den Details https://docs.gradle.org/6.0/release-notes.html:
Red Hat CodeReady Studio 12.13.0.GA und JBoss Tools 4.13.0.Final
Zu den Details siehe https://developers.redhat.com/blog/2019/11/11/new-features-in-red-hat-codeready-studio-12-13-0-ga-and-jboss-tools-4-13-0-final-for-eclipse-2019-09/
Bei der Update-Liste ist allerdings das meiste von Eclipse …
Quarkus 1.0.0 erschienen
Das „Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM, crafted from the best of breed Java libraries and standards.“ ist in der ersten Version erschienen und positioniert sich gegen Spring Boot und Jakarata EE.
Zum Weiterlesen und schauen:
Snippet: int-Arrays kombinieren
int[] ints1 = { 0 }; int[] ints2 = { 1,2 }; int[] ints3 = { }; int[] ints4 = { 3 }; int[] ints = Stream.of(ints1, ints2, ints3, ints4).flatMapToInt(IntStream::of).toArray(); System.out.println(Arrays.toString( ints ));
this für kaskadierte Methoden und Builder
Die append(…)-Methoden bei StringBuilder liefern die this-Referenz, sodass sich Folgendes schreiben lässt:
StringBuilder sb = new StringBuilder(); sb.append( "Android oder iPhone" ).append( '?' );
Jedes append(…) liefert das StringBuilder-Objekt, auf dem es aufgerufen wird – wir können also Methoden kaskadiert anhängen oder es bleiben lassen.
Wir wollen diese Möglichkeit bei einem Zauberer (Klasse Wizard) programmieren, sodass die Methoden name(String) und age(int) Spielername und Alter zuweisen. Beide Methoden liefern ihr eigenes Wizard-Objekt über die this-Referenz zurück:
class Wizard { String name = ""; int age; Wizard name( String name ) { this.name = name; return this; } String name() { return name; } Wizard age( int item ) { this.age = item; return this; } int age() { return age; } String format() { return name + " ist " + age; } }
Erzeugen wir einen Wizard, und kaskadieren wir einige Methoden:
Wizard gundalf = new Wizard().name( "Gundalf" ).age( 60 ); System.out.println( gundalf.name() ); // Gundalf System.out.println( gundalf.format() ); // Gundalf ist 60
Der Ausdruck new Wizard() liefert eine Referenz, die wir sofort für den Methodenaufruf nutzen. Da name(String) wiederum eine Objektreferenz vom Typ Wizard liefert, ist dahinter direkt .age(int) möglich. Die Verschachtelung von name(„Gunalf“).age(60) bewirkt, dass Name und Alter gesetzt werden und der jeweils nächste Methodenaufruf in der Kette über this eine Referenz auf dasselbe Objekt, aber mit verändertem internem Zustand bekommt.
Beispiele dieser Bauart sind in der Java-Bibliothek an einigen Stellen zu finden. Sie werden auch Builder genannt.
Hinweis: Die Methode Wizard name(String) ist mit ihrer Rückgabe praktisch, verstößt aber aus zwei Gründen gegen die JavaBeans-Konvention: Setter dürfen keine Rückgabe haben und müssen immer mit set beginnen. JavaBeans sind also nicht so dieser kompakten Builder-Schreibweise „kompatibel“.
Besser dokumentierte NullPointerException in Java 14
Microsoft arbeitet am OpenJDK mit
https://mail.openjdk.java.net/pipermail/discuss/2019-October/005173.html:
Subject: Microsoft’s Ready do Contribute to OpenJDK Hi OpenJDK Community, In the past week Microsoft formally signed the Oracle Contributor Agreement, in which Oracle Inc. promptly acknowledged and welcomed us to the project. On behalf of the Microsoft Java Engineering Team, I’d like to say that we are thrilled to officially join the OpenJDK project and be ready to work with you. As many of you may know, Microsoft and its subsidiaries are heavily dependent on Java in many aspects, and also offers Java runtimes in its Microsoft Azure cloud to its customers. Microsoft recognizes the immense value that Oracle’s successful and effective stewardship of the OpenJDK project has bought Java and the wider software ecosystem and we look forward to playing our part in contributing back! The team will initially be working on smaller bug fixes and backports so that we can learn how to be good citizens within OpenJDK. For example, we already understand that discussing changes first before posting patches is preferred and I'm sure there's more for us to learn as well. The Java engineering team led by Martijn Verburg [1] is already engaged with other Microsoft groups and its subsidiaries who are using Java, as well as its partners in the Java ecosystem such as Azul Systems, Oracle, Pivotal, Red Hat, Intel, SAP and others, and the overall team will be joining the many OpenJDK mailing lists to start conversations and participating. We look forward to participating in the future of Java. [1] martijn.verburg at microsoft.com<mailto:martijn.verburg at microsoft.com> Best regards Bruno Borges Product Management for Java, Microsoft Developer Division
Mehr zu der Geschichte unter
- https://jaxenter.com/microsoft-ready-contribute-openjdk-163550.html
Spring Framework 5.2 fertig
Ankündigung im Blog https://spring.io/blog/2019/09/30/spring-framework-5-2-goes-ga:
Spring Framework 5.2 requires JDK 8 or higher and specifically supports JDK 11 as the current long-term support branch as well as JDK 13 as the latest OpenJDK release. It comes with many performance improvements (affecting startup time as well as peak performance) and further steps taken towards GraalVM native image support.
This release deeply integrates with Kotlin 1.3 and provides first-class support for Kotlin coroutines on top of Spring WebFlux. Furthermore, it comes with reactive messaging integration for the RSocket protocol as well as reactive transaction management for R2DBC, MongoDB and Neo4j (with datastore integration provided by Spring Data’s modules).
As of the upcoming Spring Boot 2.2 RC1 release, you’ll be able to consume Spring Framework 5.2 GA through start.spring.io!
Wichtige Links:
Finale Variablen und der Modifizierer final
Variablen können mit dem Modifizierer final deklariert werden, sodass genau eine Zuweisung möglich ist. Dieses zusätzliche Schlüsselwort verbietet folglich eine weitere Zuweisung an diese Variable, sodass sie nicht mehr verändert werden kann. Ein üblicher Anwendungsfall sind Konstanten.
int width = 40, height = 12; final int area = width * height; final int perimeter; final var random = Math.random() * 100; perimeter = width * 2 + height * 2; area = 200; // Compilerfehler
perimeter = 100; // Compilerfehler
Im Fall einer versuchten zweiten Zuweisung meldet der Compiler von Eclipse: »The final local variable … cannot be assigned. It must be blank and not using a compound assignment.«; IntelliJ meldet über den Java-Compiler »cannot assign a value to final variable …«.
Java erlaubt bei finalen Werten eine aufgeschobene Initialisierung. Das heißt, dass nicht zwingend zum Zeitpunkt der Variablendeklaration ein Wert zugewiesen werden muss. Das sehen wir im Beispiel an der Variablen perimeter.
Werden Variablen deklariert und initialisiert können final und var zusammen eingesetzt werden; einige Programmiersprachen bieten hier ein eigenes Schlüsselwort, wie val, Java jedoch nicht.
Ausblick
Auch Objektvariablen und Klassenvariablen können final sein. Allerdings müssen die Variablen dann entweder bei der Deklaration belegt werden, oder in einer aufgeschobenen Initialisierung im Konstruktor. Das Schlüsselwort final hat noch zusätzliche Bedeutungen im Zusammenhang mit Vererbung.