Entwicklung & Code
Programmiersprache C++26: Reflexion zur Kompilierungszeit
Im heutigen Beitrag meines C++-Blogs möchte ich über C++26 und eine der wahrscheinlich wirkungsvollsten Funktionen schreiben, die dem Arbeitsentwurf hinzugefügt wurden. Auch wenn C++26 noch ein paar Wochen bis zur offiziellen Fertigstellung braucht, wissen wir seit dem WG21-Sommertreffen im Juni 2025, was in C++26 enthalten sein wird.
Weiterlesen nach der Anzeige

Andreas Fertig ist erfahrener C++-Trainer und Berater, der weltweit Präsenz- sowie Remote-Kurse anbietet. Er engagiert sich im C++-Standardisierungskomitee und spricht regelmäßig auf internationalen Konferenzen. Mit C++ Insights ( hat er ein international anerkanntes Tool entwickelt, das C++-Programmierenden hilft, C++ noch besser zu verstehen.
Der neue Standard wird viele spannende Verbesserungen bringen, aber die wahrscheinlich größte Veränderung ist die Reflexion (Reflection) zur Kompilierungszeit! In Sofia hat das Standardisierungs-Komitee sieben Reflection-Papiere für C++26 angenommen:
- P1306R5: Expansion statements
- P2996R13: Reflection for C++26
- P3096R12: Function parameter reflection in reflection for C++26
- P3293R3: Splicing a base class subobject
- P3394R4: Annotations for reflection
- P3491R3: define_static_{string,object,array}
- P3560R2: Error handling in reflection
Die verlinkten Beiträge bieten genügend theoretischen Lesestoff.
Kommen wir zur Praxis
Die wichtigste Frage ist: Was kannst du mit dieser neuen Funktion machen? Einige haben bereits ihre Ideen veröffentlicht.
Steve Downey hat ein Beispiel, das eine JSON-Zeichenkette zur Kompilierungszeit analysiert und daraus C++-Objekte erstellt. Der direkte Link zum Compiler Explorer lautet godbolt.org/z/YsEK418K6.
Weiterlesen nach der Anzeige
Das zweite Beispiel stammt von Jason Turner und ermöglicht es, Bindungen zu anderen Sprachen zu generieren. Der direkte Link zum Compiler Explorer lautet godbolt.org/z/6Y17EG984.
Ich finde beide Beispiele prima, aber will auch ein eigenes zeigen. Das Problem, das ich jahrelang zu lösen versucht habe und das auch in verschiedenen Schulungen und sogar in meinem eigenen Buch Programming with C++20 – Concepts, Coroutines, Ranges, and more auftaucht. Ich musste die bittere Pille schlucken, einen nicht so tollen Code zu zeigen.
Reflexion, Reflexion an der Wand, was kann ich mit dir alles machen?
Ich rede von Enums und nicht davon, wie man ein Enum in einen String konvertiert und umgekehrt. Der Code dafür ist übrigens in den oben verlinkten Beiträgen zu finden.
Nein, ich hab mindestens noch ein anderes Problem mit Enums: Iteration. Wie oft wollte ich schon über ein enum iterieren. Es gibt Lösungen, die meist makrobasiert und mit vielen Regeln sind. Zum Beispiel nur aufeinanderfolgende Zahlen und ein letztes Mitglied namens Last oder MAX. Aber was ist, wenn es Lücken in einem enum gibt? Wie
enum class Color { Transparent, Red = 2, Green, Blue = 8, Yellow };
Genau, dann greift die Regel, dass nicht aufeinanderfolgende Nummerierungen nicht erlaubt sind.
Folgender Code zeigt einen Ansatz, der anderen Sprachen wie C# ähnelt, in denen das Iterieren der enum-Werte ohne weitere Umstände möglich ist:
// #A
template
requires std::is_enum_v
constexpr inline auto num_enumerators_of{
std::meta::enumerators_of(^^E).size()};
// #B
template
requires std::is_enum_v
consteval auto get_enum_values()
{
std::array> res;
template for(size_t i{}; constexpr auto& e : std::define_static_array(
std::meta::enumerators_of(^^E)))
{
res[i++] = [:e:];
}
return res;
}
Ich habe in #A eine Hilfsvariable erstellt, einfach weil es für sich schon hilfreich ist, die Anzahl der Werte in einem enum zu ermitteln.
Die Implementierung für die Aufgabe selbst befindet sich dann in #B. Du kannst dir eine andere Implementierung ausdenken, die für größere Enums besser geeignet ist, aber diese hier ist schön kurz und bündig.
Die Utility-Funktion sieht in Aktion nicht besonders aus, und man merkt nicht, dass im Hintergrund eine Reflexion stattfindet:
for(const auto e : get_enum_values()) {
std::print("{} ", std::to_underlying(e));
}
std::println();
Wie du siehst, gibt #B den stark typisierten Enum-Wert zurück. Deshalb ist std::to_underlying erforderlich, wenn der Wert mit std::print verwendet wird. Das ist eine Designentscheidung: Der Code bleibt so lange wie möglich stark typisiert.
Es gibt noch weitere Designüberlegungen, beispielsweise ob get_enum_values auch eine Variable sein sollte, da sie für jeden Typ konstant ist.
An dieser Stelle werde ich nicht alle neuen Teile erklären, da ich nur zeigen möchte, was mit C++26 möglich ist.
Den vollständigen Code zum Experimentieren findest du im Compiler Explorer.
P.S.: Falls du dich fragst, ob die Implementierung von #B für ein leeres enum, welches ein std::array der Größe Null ergibt, korrekt ist, lautet die Antwort: Ja, der Code ist korrekt. Einer der Vorteile von std::array ist, dass es einen Sonderfall für den Fall der Größe Null gibt. Ein Array im C-Stil wäre nicht gültig.
(rme)
Entwicklung & Code
comments_outline_white
Weiterlesen nach der Anzeige
Im Herbst 2025 erschien mit dem OpenJDK 25 die aktuelle Version, für die viele Hersteller einen Long-Term-Support (LTS) anbieten. Viele Unternehmen nutzen solche Releases als Stabilitätsanker für Migrationen und langfristige Planung. Java 26 ist dagegen wieder ein reguläres Halbjahres-Release und damit ein Schritt in einer kontinuierlichen Evolution der Plattform: Sprache, Runtime und Standardbibliothek werden systematisch modernisiert, ohne die Stabilität und Abwärtskompatibilität zu gefährden, für die Java seit Jahrzehnten bekannt ist.

Falk Sippach ist bei der embarc Software Consulting GmbH als Softwarearchitekt, Berater und Trainer tätig. Seit über 15 Jahren begleitet er vorwiegend agile Softwareentwicklungsprojekte im Java-Umfeld. Als aktives Mitglied der Java-Community gibt er sein Wissen gerne in Artikeln und Blogbeiträgen sowie als Referent auf Konferenzen weiter.
Ein kleines Release mit strategischer Bedeutung
Insgesamt enthält Java 26 zehn JEPs (JDK Enhancement Proposals). Ein Teil davon setzt bekannte Entwicklungen fort, etwa bei Pattern Matching, Structured Concurrency oder der Vector API. Andere Änderungen betreffen die Performance der JVM, neue Netzwerkprotokolle oder Verbesserungen im Security-Stack. Hinzu kommen einige Aufräumarbeiten im JDK, etwa die endgültige Entfernung der alten Applet-API.
Auch wenn Java 26 auf den ersten Blick unspektakulär wirkt, ist seine strategische Bedeutung nicht zu unterschätzen. Einige der Änderungen bereiten die Plattform auf größere Entwicklungen vor, die in den kommenden Jahren anstehen. Dazu gehören insbesondere Project Valhalla mit den Value Types, eine stärkere Integrität des Objektmodells unter dem Leitgedanken „Integrity by Default“ sowie die Optimierungen für moderne Hardware, KI- und Cloud-Workloads.
HTTP/3 Unterstützung im Java HTTP Client
Der in Java 11 eingeführte java.net.http.HttpClient hat sich in den vergangenen Jahren zum Standardwerkzeug für HTTP-Kommunikation in Java-Anwendungen entwickelt. Mit Java 26 unterstützt diese API nun auch HTTP/3 (JEP 517). HTTP/3 setzt nicht mehr auf TCP, sondern auf QUIC, einem UDP-basierten Transportprotokoll. Dadurch ergeben sich mehrere Vorteile gegenüber HTTP/1.1 und HTTP/2:
Weiterlesen nach der Anzeige
- geringere Latenz beim Verbindungsaufbau,
- bessere Performance bei Paketverlust,
- kein Head-of-Line-Blocking zwischen parallelen Streams und
- stabilere Verbindungen bei Netzwechsel, etwa bei mobilen Clients.
Gerade in Cloud-Umgebungen oder bei global verteilten Anwendungen kann HTTP/3 spürbare Vorteile bringen. Für Entwicklerinnen und Entwickler bleibt die Nutzung der API dagegen weitgehend unverändert. Die gewünschte HTTP-Version können sie einfach beim Erstellen des Clients angeben:
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("
.GET()
.build();
HttpResponse response =
client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
Lazy Constants: Initialisierung von Konstanten erst bei Bedarf
Mit JEP 526 bringt Java 26 eine weitere Iteration der sogenannten Lazy Constants. Das Feature war bereits in Java 25 unter dem Namen Stable Values enthalten und erscheint nun als zweite Preview mit einer überarbeiteten API. Die Idee dahinter ist einfach: Konstanten sollen erst dann berechnet werden, wenn die Anwendung sie benötigt. Bisher erzeugt die JVM final-Felder im Rahmen der Klasseninitialisierung. Das ist effizient, wenn die Anwendung die Werte verwendet, kostet aber Zeit beim Start. In der Praxis können aufwendig zu erzeugende Objekte enthalten sein, die eine Anwendung möglicherweise nie oder erst viel später benötigt. Darum ist es sinnvoll, sie auch erst später zu initialisieren. Ein typisches Beispiel sind vorbereitete reguläre Ausdrücke oder Lookup-Strukturen:
static final Map PATTERNS = Map.of(
"email", Pattern.compile("..."),
"phone", Pattern.compile("..."),
"zip", Pattern.compile("...")
);
Beim Laden der Klasse werden hier alle Patterns sofort kompiliert – unabhängig davon, ob sie später verwendet werden oder nicht. Eine solche Initialisierung selbst lazy umzusetzen, ist schwierig. Sie muss Thread-sicher sein und darf gleichzeitig möglichst wenig Synchronisations-Overhead erzeugen. Ein klassischer Ansatz wäre das sogenannte Double-Checked Locking:
private volatile Logger logger;
Logger logger() {
if (logger == null) {
synchronized (this) {
if (logger == null) {
logger = Logger.create(OrderController.class);
}
}
}
return logger;
}
Solche Konstrukte sind nicht nur fehleranfällig, sondern vor allem schwer lesbar und führen schnell zu subtilen Nebenläufigkeitsproblemen. Lazy Constants lösen dieses Problem direkt auf Plattformebene:
private final LazyConstant logger = LazyConstant.of(() -> Logger.create(OrderController.class));
void submitOrder(User user) {
logger.get().info("order started");
}
Die JVM übernimmt die Thread-sichere Initialisierung beim ersten Zugriff, sodass Entwicklerinnen und Entwickler keine eigenen Synchronisationsmechanismen implementieren müssen. Die JVM hat zudem die Möglichkeit, Optimierungen vorzunehmen. Das kann zudem in späteren Java-Releases zu Performanceverbesserungen führen.
Die aktuelle Preview bringt eine im Vergleich zum ersten Entwurf deutlich vereinfachte API. Niedrigstufige Methoden wie orElseSet() oder trySet() sind nicht mehr enthalten. Stattdessen konzentriert sich die API nun auf Fabrikmethoden, die den Inhalt über eine Berechnungsfunktion erzeugen. Zusätzlich soll der von StableValue zu LazyConstant geänderte Name den Zweck klarer beschreiben: einen konstanten Wert, der erst bei Bedarf erzeugt wird. Gerade in Cloud- und Microservice-Architekturen kann das Vorteile bringen. Anwendungen starten schneller, unnötige Objektallokationen werden vermieden und der Speicherbedarf in der frühen Laufphase einer Anwendung sinkt.
JVM-Optimierungen für moderne Workloads
Neben neuen APIs enthält Java 26 mehrere Verbesserungen in der Laufzeitumgebung. Sie zielen vor allem auf zwei typische Anforderungen moderner Anwendungen ab: hoher Durchsatz bei paralleler Last und schnelleres Startverhalten in Cloudumgebungen.
Mehr Durchsatz im G1 Garbage Collector
Der Garbage Collector (GC) G1 ist seit vielen Jahren der Standard-GC der JVM. Seine Stärke liegt in relativ stabilen Pausenzeiten bei gleichzeitig gutem Durchsatz. In Systemen mit vielen CPU-Kernen stieß G1 jedoch bislang an interne Skalierungsgrenzen, weil bestimmte Operationen stark synchronisiert waren. Mit JEP 522 reduziert Java 26 gezielt diese Synchronisationspunkte. Dadurch entsteht weniger Lock Contention im Garbage Collector, und die Parallelisierung lässt sich besser nutzen. Besonders profitieren davon Anwendungen mit vielen Threads und hoher Objektallokation – etwa hoch parallele Backend-Systeme oder datenintensive Batch-Workloads.
Ahead-of-Time Object Caching
Ein weiterer Schritt in Richtung schnellerer Starts ist JEP 516. Dahinter verbirgt sich ein Mechanismus, der es erlaubt, bestimmte Objekte bereits im Voraus zu erzeugen und wiederzuverwenden. Typische Kandidaten dafür sind:
- Lookup-Tabellen,
- Konfigurationsstrukturen,
- Metadatenobjekte und
- häufig verwendete String- oder Datenstrukturen.
Statt diese Strukturen bei jedem Start neu aufzubauen, können sie vorbereitet und beim Start direkt wiederverwendet werden. Das reduziert die Initialisierungsarbeit und verkürzt die Warm-up-Phase einer Anwendung. Ein wichtiger Unterschied zu früheren Ansätzen ist, dass dieses Ahead-of-Time Object Caching nun unabhängig vom verwendeten Garbage Collector funktioniert. Dadurch lässt sich die Optimierung mit unterschiedlichen GC-Konfigurationen kombinieren. Gerade in containerisierten Umgebungen kann das relevant sein. Anwendungen starten schneller, Auto-Scaling reagiert effizienter und der Ressourcenbedarf während der Startphase sinkt.
PEM Encodings im JDK
Mit JEP 524 verbessert Java 26 in der zweiten Preview den Umgang mit kryptografischen Schlüsseln und Zertifikaten. Das JDK erhält eine native Unterstützung für PEM-kodierte kryptografische Objekte.
PEM („Privacy-Enhanced Mail“) ist ein textbasiertes Format zur Darstellung von Zertifikaten und Schlüsseln, das in vielen Bereichen zum Einsatz kommt, etwa für X.509-Zertifikate, Public und Private Keys oder Certificate Signing Requests. Eine typische PEM-Datei sieht folgendermaßen aus:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEi/kRGOL7wCPTN4KJ2ppeSt5UYB6u
cPjjuKDtFTXbguOIFDdZ65O/8HTUqS/sVzRF+dg7H3/tkQ/36KdtuADbwQ==
-----END PUBLIC KEY-----
Obwohl dieses Format im TLS- und Cloud-Umfeld weitverbreitet ist, war die direkte Unterstützung im JDK bislang begrenzt. Entwickler und Entwicklerinnen mussten häufig auf Drittbibliotheken zurückgreifen oder eigene Parser implementieren. Die neue API übernimmt zentrale Aufgaben wie das Einlesen von PEM-Daten, das Entfernen der Header und Footer sowie das Base64-Decoding. Dadurch lassen sich Zertifikate und Schlüssel direkt mit Bordmitteln des JDK verarbeiten. Gerade in aktuellen Deployment-Szenarien tauchen PEM-Dateien häufig auf, etwa in Kubernetes-Secrets, TLS-Konfigurationen, ACME-/Let’s-Encrypt-Integrationen oder mTLS-Set-ups zwischen Services. Die native Unterstützung reduziert Boilerplate-Code und verringert die Abhängigkeit von externen Security-Bibliotheken.
Aufräumarbeiten im JDK
Zur Evolution einer Plattform gehört nicht nur das Hinzufügen neuer Funktionen, sondern auch das konsequente Entfernen überholter Technologien und das Korrigieren von Fehlverhalten. Java 26 enthält zwei Änderungen, die auf den ersten Blick unspektakulär wirken, aber eine klare strategische Richtung zeigen.
Das JEP 504 entfernt die Applet-API endgültig aus dem JDK. Applets waren in den 1990er-Jahren ein zentrales Versprechen der Java-Plattform: Java-Code sollte direkt im Browser laufen und so interaktive Webanwendungen ermöglichen. Diese Zeit ist längst vorbei. Moderne Browser unterstützen keine NPAPI-Plug-ins mehr, Sicherheitsanforderungen sind deutlich gestiegen und Webanwendungen basieren heute auf völlig anderen Architekturen – etwa JavaScript-Frameworks, REST-APIs oder Single-Page-Applications. Die Applet-API ist bereits seit mehreren Java-Versionen als „deprecated for removal“ markiert und verschwindet nun endgültig aus dem JDK. Für aktuelle Anwendungen hat diese Änderung praktisch keine Auswirkungen.
Deutlich weitreichender ist JEP 500: „Prepare to Make Final Mean Final“. In der Java-Sprache garantiert das Schlüsselwort final, dass ein Feld nach der Konstruktion nicht mehr verändert werden kann. In der Praxis ließ sich diese Einschränkung jedoch über Reflection oder Low-Level-APIs wie Unsafe umgehen. Viele Frameworks – insbesondere Serialisierungsbibliotheken – nutzten diese Möglichkeit, um Objekte ohne Aufruf des Konstruktors zu erzeugen oder Felder direkt zu manipulieren. Das Vorgehen kann jedoch wichtige Validierungsregeln (Invarianten) eines Objekts verletzen.
Als Beispiel dient folgende Java-Klasse mit Preconditions:
public class Adult {
private final Instant birthdate;
public Adult(Instant birthdate) {
this.birthdate = birthdate;
if (isYoungerThan18(birthdate)) {
throw new IllegalArgumentException("Not 18 yet");
}
}
}
Wird ein Objekt dieser Klasse beispielsweise per Reflection erzeugt, kann eine Anwendung birthdate setzen, ohne dass die Validierung im Konstruktor ausgeführt wird. Das Ergebnis ist ein Objekt in einem ungültigen Zustand. JEP 500 bereitet daher den Weg für ein strengeres Integritätsmodell. Ziel ist es, dass final künftig standardmäßig tatsächlich die Unveränderlichkeit garantiert. Unsichere Zugriffe auf solche Felder sollen eingeschränkt werden, während Übergangsmechanismen über JVM-Parameter weiterhin Kompatibilität mit bestehendem Code ermöglichen. Diese Entwicklung ist Teil eines größeren Trends unter dem Stichwort „Integrity by Default“. Die Plattform bewegt sich weg von einem Modell, in dem Frameworks beliebig tief in das Objektmodell eingreifen können, und hin zu einem Ansatz, der Invarianten stärker schützt und bei dem das Verhalten von Objekten verlässlicher bleibt.
Während die bisher beschriebenen JEPs hauptsächlich neue Themen umfassten, gibt es wie bei jedem JDK-Release einige langlaufende Preview- und Inkubator-Features. Dazu zählen die Primitive Types in Patterns (mittlerweile in der vierten Fassung), die Structured Concurrency (sechste Preview) und die Vector API (zum elften Mal als Inkubator-Feature).
Pattern Matching über primitive Typen
Seit Java 21 entwickelt sich Pattern Matching schrittweise zu einem zentralen Sprachkonzept. Ziel ist es, komplexe Typprüfungen und Fallunterscheidungen einfacher und sicherer auszudrücken. Gemeinsam mit Records und Sealed Classes lassen sich damit Strukturen ähnlich wie algebraische Datentypen modellieren, wodurch viele ungültige Zustände bereits auf Typebene ausgeschlossen werden können. Nach Type Patterns, Pattern Matching im switch, Record Patterns und Unnamed Patterns sind Primitive Type Patterns der nächste Schritt in der Entwicklung von Java hin zum datenorientierten Paradigma. Das Feature tauchte erstmals bereits in Java 23 auf und erscheint in Java 26 mit JEP 530 als vierte Preview. Inhaltlich hat sich gegenüber dem letzten Release wenig geändert – das OpenJDK-Team sammelt weiterhin Feedback, bevor es die Funktion finalisiert.
Pattern Matching erlaubt es, Datenstrukturen mit einem Muster zu vergleichen und gleichzeitig Teile daraus zu extrahieren. Ein Pattern kombiniert dabei eine Bedingung („passt dieser Typ oder Wertebereich?“) mit Variablen, in die passende Werte gebunden werden. Während dieses Konzept bisher nur für Referenztypen galt, erweitert Java 26 es auf primitive Datentypen und erlaubt es, primitive und Referenztypen austauschbar im Pattern Matching-Kontext zu verwenden. Das folgende Beispiel zeigt, wie man ein int verlustfrei in einen byte konvertiert:
private static String checkByte(int value) {
if (value instanceof byte b) {
return "byte b = " + b;
} else {
return "kein byte: " + value;
}
}
System.out.println(checkByte(127)); // byte b = 127
System.out.println(checkByte(128)); // kein byte: 128
Neben den vereinfachten, verlustfreien Konvertierungen schärft das aktuelle JEP vor allem die Dominanzregeln im switch weiter. Sie stellen sicher, dass kein case unerreichbar ist, weil ein vorheriges Pattern bereits alle möglichen Werte abdeckt. Der Compiler erkennt solche Situationen inzwischen zuverlässiger und meldet sie als Fehler. Zusammen mit den bereits eingeführten Pattern-Typen ist das ein wichtiger Schritt hin zu einer konsistenteren und deklarativeren Sprache. Entwicklerinnen und Entwickler können Datenstrukturen direkt auswerten, Boilerplate-Code reduzieren und komplexe Fallunterscheidungen klarer formulieren. Weitere Mustertypen wie Array Patterns oder die Dekonstruktion beliebiger Klassen (nicht nur Records) sollen in zukünftigen OpenJDK-Releases folgen.
Die Structured Concurrency reift weiter
Die Einführung von Virtual Threads in Java 21 hat die Nebenläufigkeit in Java deutlich vereinfacht. Doch diese schlanken Threads allein lösen noch nicht alle strukturellen Probleme paralleler Programme. Wie werden mehrere Aufgaben gemeinsam orchestriert? Was passiert, wenn eine Teilaufgabe fehlschlägt? Und wie stellt man sicher, dass keine Hintergrund-Tasks unkontrolliert weiterlaufen? Genau hier setzt die Structured-Concurrency-API an. Das Feature kam ebenfalls erstmals als Preview in Java 21 und erscheint in Java 26 mit JEP 525 in der sechsten Preview. Nachdem es in Java 25 größere Änderungen an der API gab, dient die aktuelle Iteration vor allem dazu, weitere Erfahrungen aus der Praxis zu sammeln und das API-Design zu stabilisieren.
Klassische Java-Anwendungen starten parallele Aufgaben häufig über ExecutorService und Future:
ExecutorService executor = Executors.newFixedThreadPool(2);
Future user = executor.submit(() -> loadUser());
Future> orders = executor.submit(() -> loadOrders());
String userResult = user.get();
List orderResult = orders.get();
Dieses Modell bringt einige Nachteile mit sich: Die Fehlerbehandlung ist fragmentiert, Tasks können weiterlaufen, obwohl andere bereits fehlgeschlagen sind, und die Lebensdauer der Aufgaben ist nicht klar an einen Scope gebunden. Structured Concurrency verfolgt einen anderen Ansatz: Parallele Aufgaben werden innerhalb eines gemeinsamen Lebenszyklus gestartet und verwaltet. Ähnlich wie bei try-with-resources ist damit klar definiert, wann Tasks beginnen und wann sie garantiert beendet sind:
try (var scope = StructuredTaskScope.open()) {
Subtask user = scope.fork(() -> loadUser());
Subtask> orders = scope.fork(() -> loadOrders());
scope.join(); // Join subtasks, propagating exceptions
return new UserSummary(user.get(), orders.get());
}
Alle gestarteten Tasks gehören hier zu einem gemeinsamen Scope. Schlägt eine Aufgabe fehl, werden die anderen automatisch abgebrochen. Nach Verlassen des try-Blocks ist garantiert, dass keine Tasks weiterlaufen. Ein wichtiger Bestandteil des Konzepts ist die zentrale Fehlerbehandlung. Statt Exceptions einzelner Tasks separat zu behandeln, führt join() die Fehler des Scopes zusammen. Wenn ein Task fehlschlägt, löst join() eine StructuredTaskScope.FailedException aus, die die ursprüngliche Exception der fehlgeschlagenen Subtask kapselt. Gleichzeitig werden verbleibende Tasks abgebrochen.
Sogenannte Joiner definieren, wie Ergebnisse der Subtasks kombiniert und wie Fehler oder Zeitüberschreitungen behandelt werden sollen. Häufig verwendete Strategien sind bereits als Fabrikmethoden vorhanden, etwa Joiner.awaitAllSuccessfulOrThrow() (Default-Joiner) oder Joiner.anySuccessfulOrThrow(). Außerdem kann man einfach eigene Joiner implementieren. Java 26 führt zudem den Callback onTimeout() ein. Damit können Anwendungen entscheiden, wie sie mit Zeitüberschreitungen umgehen – etwa indem sie nur die bis dahin erfolgreichen Ergebnisse zurückgeben.
Ihre volle Stärke entfaltet die Structured Concurrency in Kombination mit Virtual Threads. Jede Aufgabe kann ohne komplexes Thread-Pool-Management in einem eigenen Virtual Thread laufen. Das ermöglicht ein Programmiermodell, das synchron wirkt, intern aber stark parallel skaliert. Gerade in Backend-Architekturen eignet sich dieser Ansatz für typische Muster wie parallele Serviceaufrufe, Datenaggregation oder Fan-Out/Fan-In-Strukturen. Structured Concurrency verbindet eine hohe Skalierbarkeit mit einem deutlich verständlicheren Programmiermodell für nebenläufige Anwendungen. Komplizierte und fehleranfällige Alternativen wie reaktive Programmierbibliotheken oder manuelle Thread-Verwaltung werden damit überflüssig.
Vector API: SIMD für Java
Ein weiteres langfristiges Performance-Projekt setzt Java 26 mit JEP 529 fort: die Vector API. Sie erscheint inzwischen zum elften Mal im Inkubator-Status und gehört damit zu den langlebigsten experimentellen Features der letzten Jahre.
Ziel der Vector API ist es, SIMD-Instruktionen (Single Instruction, Multiple Data) aktueller CPUs direkt aus Java heraus nutzbar zu machen. Dabei lassen sich mehrere Werte in einem einzigen CPU-Befehl parallel verarbeiten. Diese Technik spielt eine wichtige Rolle in Bereichen wie numerischen Simulationen, Bildverarbeitung, Signalverarbeitung oder Machine-Learning-Algorithmen. Ein einfacher Vergleich zeigt den Unterschied. Ohne Vector API erfolgt die Verarbeitung der Werte in einer Schleife:
void scalarComputation(float[] a, float[] b, float[] c) {
for (int i = 0; i < a.length; i++) {
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
}
}
Mit der Vector API lassen sich mehrere Werte gleichzeitig berechnen:
Listing 11: SIMD-Instruktionen mit Vector-API
```java
static final VectorSpecies SPECIES = FloatVector.SPECIES_PREFERRED;
void vectorComputation(float[] a, float[] b, float[] c) {
int i = 0;
int upperBound = SPECIES.loopBound(a.length);
for (; i < upperBound; i += SPECIES.length()) {
// FloatVector va, vb, vc;
var va = FloatVector.fromArray(SPECIES, a, i);
var vb = FloatVector.fromArray(SPECIES, b, i);
var vc = va.mul(va)
.add(vb.mul(vb))
.neg();
vc.intoArray(c, i);
}
for (; i < a.length; i++) {
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
}
}
Die Breite der Operation hängt von der CPU ab – beispielsweise 128 oder 256 Bit. Der JIT-Compiler kann diese Operationen direkt auf die SIMD-Instruktionen der jeweiligen Hardware abbilden. Die Vector API ist strategisch wichtig für datenintensive Anwendungen, etwa für Vektorberechnungen in Datenbanken, Embedding-Vergleiche in KI-Systemen oder andere numerische Workloads. Allerdings wartet sie noch auf eine wichtige Grundlage: Project Valhalla und die geplanten Value Types. Erst mit einem kompakteren Speicherlayout für Werte kann die API ihr volles Potenzial entfalten. Auch wenn die Vector API weiterhin im Inkubator bleibt, zeigt ihre kontinuierliche Weiterentwicklung deutlich, wohin sich die Plattform bewegt: Java soll aktuelle Hardware effizient ausnutzen können und auch für stark daten- und rechenintensive Anwendungen wie KI konkurrenzfähig bleiben.
Wichtiger Baustein in der langfristigen Entwicklung
Java 26 ist kein spektakuläres Feature-Release, aber ein wichtiges Puzzlestück in der langfristigen Weiterentwicklung der Plattform. Viele Änderungen wirken auf den ersten Blick klein, bereiten jedoch größere Entwicklungen vor. Eine der wichtigsten Baustellen bleibt Project Valhalla, das mit Value Types das Typsystem von Java grundlegend erweitert. Value Types versprechen kompaktere Datenstrukturen ohne Objekt-Header, bessere Cache-Lokalität und weniger Druck auf den Garbage Collector. Gerade für numerische Anwendungen, Datenverarbeitung oder KI-nahe Workloads wird das erhebliche Performancegewinne bringen. Jüngsten Gerüchten zufolge könnten Value Classes bereits im Sommer 2026 in das OpenJDK-Projekt einfließen und mit Java 28 im März 2027 erstmals als Preview-Feature erscheinen.
Parallel dazu entwickelt sich das Sicherheits- und Integritätsmodell der Plattform weiter. Unter dem Leitgedanken „Integrity by Default“ gibt es Einschränkungen für Mechanismen, die Objektinvarianten über Reflection oder unsichere APIs umgehen können. Ziel ist ein stabileres und besser optimierbares Objektmodell. Die Neuerungen in Java 26 zeigen eine klare Richtung: Die Plattform wird systematisch moderner – von der Sprache über das Nebenläufigkeitsmodell bis hin zur Runtime und den Netzwerkprotokollen. Dabei bleibt Java seinem bewährten Ansatz treu, größere Veränderungen schrittweise einzuführen und möglichst abwärtskompatibel zu bleiben.
Neben den vorgestellten zehn JEPs enthält Java 26 zahlreiche kleinere Verbesserungen. Details dazu finden sich in den Release-Notes. Änderungen an der Java-Klassenbibliothek lassen sich zudem sehr übersichtlich im Java Almanac nachvollziehen, der die Unterschiede zwischen den JDK-Versionen auflistet. Und auch ein Blick nach vorn lohnt sich: Im JEP-Index unter Draft and submitted JEPs sammeln sich bereits zahlreiche Ideen für kommende Java-Versionen.
(rme)
Entwicklung & Code
Software Testing: Effektives Testreporting ohne Overhead
In dieser Folge spricht Richard Seidl mit Matthias Groß über Testreporting und dessen Umsetzung im Projektalltag. Im Fokus steht eine pragmatische Herangehensweise: Statt überladener Dashboards setzt Matthias Groß auf eine klare 3×3-Matrix und automatisierte Datenaufbereitung mit Python und Excel.
Weiterlesen nach der Anzeige
Die beiden beleuchten, wie Testreporting nicht nur den Status sichtbar macht, sondern auch als Führungsinstrument genutzt werden kann. Das Gespräch zeigt, wie wichtig Zieldefinition, Datenqualität und flexible Sichten sind. Besonders spannend sind die Einblicke zu KI-Experimenten im Reporting und die ehrlichen Reflexionen darüber, was wirklich zählt.
Matthias Groß ist Partner der TestGilde GmbH und seit 2007 als Berater für Softwarequalitätssicherung und Testmanagement tätig. Seine Schwerpunkte liegen im operativen Testmanagement, der Einführung und Weiterentwicklung von Testmanagementstrukturen sowie der Betreuung kundenspezifischer Testservices. Er engagiert sich zudem an der Dualen Hochschule Baden-Württemberg, ist Mitgründer der Testcommunity The TestLänd und Mitglied des Programmkomitees des QS-Tags.
Software Testing im Gespräch
Bei diesem Format dreht sich alles um Softwarequalität: Ob Testautomatisierung, Qualität in agilen Projekten, Testdaten oder Testteams – Richard Seidl und seine Gäste schauen sich Dinge an, die mehr Qualität in die Softwareentwicklung bringen.
Die aktuelle Ausgabe ist auch auf Richard Seidls Blog verfügbar: „Effektives Testreporting ohne Overhead – Matthias Groß“.
Weiterlesen nach der Anzeige
(mai)
Entwicklung & Code
PHP-Team stimmt über anerkannte Open-Source-Lizenzen ab
Die PHP Internals, also die Kernmitglieder des Projekts, stimmen derzeit über eine neue Open-Source-Lizenz für PHP und die Zend Engine ab. Die vorgeschlagene Modifizierte- oder 3-Klausel-BSD-Lizenz ist von der Open Source Initiative (OSI) und der Free Software Foundation (FSF) anerkannt und zur GPL kompatibel. An den bisherigen garantierten Rechten für Nutzer und Kontributoren soll sich dadurch nichts ändern.
Weiterlesen nach der Anzeige
Der RFC-Vorschlag begründet die Einführung einer neuen Lizenz mit dem bisherigen Durcheinander an Lizenzen, das historische Gründe hat. Derzeit erkennt die OSI zwar die PHP-Lizenz v3.01 als Open Source an, die Zend-Engine-Lizenz 2.0 jedoch nicht. Beide sind auch nicht zur GPL kompatibel.
Die beiden Komponenten werden aber gemeinsam entwickelt und liegen in einem Repository vor. Das ist für Außenstehende und Nutzer schwierig nachzuvollziehen: „Nach 25 Jahren Zusammenlebens im selben Quellcode-Repository sind beide so eng miteinander verflochten, dass die Zend Engine nicht mehr davon getrennt und als eigenständiges Produkt genutzt werden kann. Zusammen bilden sie die Referenzimplementierung der Programmiersprache PHP.“
Die 3-Klausel-BSD-Lizenz ist sehr freizügig und besagt als Besonderheit, dass Nutzer der Software die Namen der Urheber und Kontributoren nur nach vorheriger Erlaubnis für Werbung verwenden dürfen. Sie schränkt die bislang garantierten Rechte der vorangegangenen Lizenzen nicht ein, da diese eine ähnliche Klausel enthielten.
Die Abstimmung der PHP Internals über den im Juli 2025 veröffentlichten Vorschlag läuft bis zum 4. April, derzeit gibt es 23 Ja-Stimmen und eine Enthaltung. Der Kreis der Abstimmungsberechtigten liegt bei ca. vierzig Personen, und eine Zweidrittelmehrheit ist für den Erfolg notwendig.
Die Geschichte von PHP umfasst eine Vielzahl an Lizenzversuchen, die seit der ersten Veröffentlichung zum Einsatz gekommen sind, der RFC-Aufruf liefert einen kleinen historischen Abriss.
Weiterlesen nach der Anzeige
(who)
-
Künstliche Intelligenzvor 3 MonatenSchnelles Boot statt Bus und Bahn: Was sich von London und New York lernen lässt
-
Social Mediavor 2 WochenCommunity Management und Zielgruppen-Analyse: Die besten Insights aus Blog und Podcast
-
Social Mediavor 1 MonatCommunity Management zwischen Reichweite und Verantwortung
-
Künstliche Intelligenzvor 4 Wochen
Top 10: Die beste kabellose Überwachungskamera im Test – Akku, WLAN, LTE & Solar
-
Social Mediavor 3 MonatenDie meistgehörten Gastfolgen 2025 im Feed & Fudder Podcast – Social Media, Recruiting und Karriere-Insights
-
UX/UI & Webdesignvor 2 MonatenEindrucksvolle neue Identity für White Ribbon › PAGE online
-
Künstliche Intelligenzvor 2 MonatenAumovio: neue Displaykonzepte und Zentralrechner mit NXP‑Prozessor
-
Künstliche Intelligenzvor 3 MonatenÜber 220 m³ Fläche: Neuer Satellit von AST SpaceMobile ist noch größer
