{"id":672292,"date":"2026-01-07T04:47:07","date_gmt":"2026-01-07T03:47:07","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=kotlin&#038;p=672292"},"modified":"2026-01-07T04:47:13","modified_gmt":"2026-01-07T03:47:13","slug":"evaluierung-von-kotlin-in-realen-projekten","status":"publish","type":"kotlin","link":"https:\/\/blog.jetbrains.com\/de\/kotlin\/2026\/01\/evaluierung-von-kotlin-in-realen-projekten\/","title":{"rendered":"Evaluierung von Kotlin in realen Projekten"},"content":{"rendered":"<p><em>Gastbeitrag von <a href=\"https:\/\/www.linkedin.com\/in\/urs-peter-70a2882\/\" target=\"_blank\" rel=\"noreferrer noopener\" data-type=\"link\" data-id=\"https:\/\/bit.ly\/urs-peter-linked-in\">Urs Peter<\/a>, Senior Software Engineer und JetBrains-zertifizierter Kotlin Trainer. F\u00fcr Leser*innen, beim Ausbau ihrer Kotlin-Kenntnisse strukturierter vorgehen m\u00f6chten, leitet Urs auch das <\/em><a href=\"https:\/\/academy.xebia.com\/upskilling\/kotlin-academy\/\" target=\"_blank\" rel=\"noreferrer noopener\"><em>Kotlin Upskill Program<\/em><\/a><em> an der Xebia Academy.<\/em><\/p>\n<p><em>Dies ist der zweite Beitrag der Reihe <\/em><strong><em>Der ultimative Leitfaden f\u00fcr den erfolgreichen Einstieg in Kotlin in einer Java-dominierten Umgebung<\/em><\/strong><em>, die der Frage nachgeht, wie sich Kotlin in Teams in der realen Welt verbreitet, beginnend mit der Neugier einer einzelnen Person bis hin zum unternehmensweiten Wechsel.<\/em><\/p>\n<p>Hier finden Sie den ersten Teil: <a href=\"https:\/\/blog.jetbrains.com\/kotlin\/2025\/10\/getting-started-with-kotlin-for-java-developers\/\" target=\"_blank\" rel=\"noreferrer noopener\">Einf\u00fchrung in Kotlin f\u00fcr Java-Entwickler*innen<\/a><\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n<h2 class=\"wp-block-heading\"><strong>Die Evaluierungsphase: Wenn Kotlin mehr als nur eine Spielwiese ist<\/strong><\/h2>\n<p>Sobald Sie Kotlin durch den Einsatz in Tests kennengelernt haben, wird es Zeit f\u00fcr eine gr\u00fcndlichere Evaluierung. Sie haben zwei Hauptans\u00e4tze zur Auswahl:<\/p>\n<ol>\n<li><a href=\"#1.-build-a-new-microservice\/application-in-kotlin\" data-type=\"link\" data-id=\"https:\/\/blog.jetbrains.com\/#1.-build-a-new-microservice\/application-in-kotlin\">Einen neuen Microservice oder eine neue Anwendung in Kotlin entwickeln<\/a><\/li>\n<li><a href=\"#2.-extend\/convert-an-existing-java-application\" data-type=\"link\" data-id=\"https:\/\/blog.jetbrains.com\/#2.-extend\/convert-an-existing-java-application\">Eine bestehende Java-Anwendung erweitern\/konvertieren<\/a><\/li>\n<\/ol>\n<h3 id=\"1.-build-a-new-microservice\/application-in-kotlin\" class=\"wp-block-heading\"><strong>1. Einen neuen Microservice oder eine neue Anwendung in Kotlin entwickeln<\/strong><\/h3>\n<p>Wenn Sie mit einer neuen Anwendung oder einem neuen Microservice von vorn beginnen, k\u00f6nnen Sie die vollst\u00e4ndige Kotlin-Erfahrung testen, ohne durch bestehenden Code eingeschr\u00e4nkt zu werden. Dieser Ansatz bietet h\u00e4ufig die beste Lernerfahrung und die klarsten Einblicke in die St\u00e4rken von Kotlin.<\/p>\n<div class=\"wp-block-columns is-layout-flex wp-container-3 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis: 66.66%;\">\n<p><strong>Profitipp<\/strong>: Lassen Sie sich in dieser Phase von Fachleuten unterst\u00fctzen. Entwickler*innen sind zwar naturgem\u00e4\u00df von ihren F\u00e4higkeiten \u00fcberzeugt, jedoch kann Ihnen das Vermeiden fr\u00fcher Fehler in Form von Java-\u00e4hnlichem Kotlin und fehlenden Kotlin-basierten Bibliotheken Monate an technischen Schulden einsparen.<\/p>\n<\/div>\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis: 33.33%;\">\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-646384\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/10\/unnamed-5.png\" alt=\"\" width=\"600\" height=\"400\" \/><\/figure>\n<\/div>\n<\/div>\n<p>So vermeiden Sie h\u00e4ufige Fehler, wenn Sie mit einem Java-Hintergrund in Kotlin einsteigen:<\/p>\n<p><em>Fehler: Ein anderes Framework in Kotlin als in Java verwenden.<\/em><\/p>\n<p>Tipp: <strong>Bleiben Sie bei Ihrem bestehenden Framework<\/strong>.<\/p>\n<p>H\u00f6chstwahrscheinlich haben Sie Spring Boot mit Java verwendet, und das k\u00f6nnen Sie auch mit Kotlin verwenden. Spring Boot wird von Kotlin hervorragend unterst\u00fctzt, die Verwendung einer anderen L\u00f6sung bietet also keine Vorteile. Wenn Sie nicht nur eine neue Sprache, sondern auch ein neues Framework erlernen m\u00fcssen, handeln Sie sich nur zus\u00e4tzliche Komplexit\u00e4t ohne besondere Vorteile ein.<\/p>\n<p><strong>Wichtig<\/strong>: Spring kommt dem Kotlin-Prinzip der \u201estandardm\u00e4\u00dfigen Vererbung\u201c in die Quere, sodass Sie Klassen explizit als \u201eopen\u201c markieren m\u00fcssen, um sie erweitern zu k\u00f6nnen.<\/p>\n<p>Um nicht alle Spring-bezogenen Klassen (etwa <code>@Configuration<\/code> usw.) als \u201eopen\u201c markieren zu m\u00fcssen, verwenden Sie das folgende Build-Plugin: <a href=\"https:\/\/kotlinlang.org\/docs\/all-open-plugin.html#spring-support\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/kotlinlang.org\/docs\/all-open-plugin.html#spring-support<\/a>. Wenn Sie ein Spring-Projekt mit dem bekannten Online-Tool <a href=\"https:\/\/start.spring.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">Spring Initializr<\/a> anlegen, ist dieses Build-Plugin bereits f\u00fcr Sie konfiguriert.<\/p>\n<p><em>Fehler: Kotlin in einer an Java angelehnten Weise schreiben und sich eher auf g\u00e4ngige Java-APIs als auf die Kotlin-Standardbibliothek verlassen:<\/em><em>\u00a0<\/em><\/p>\n<p>Diese Liste k\u00f6nnte sehr lang werden, daher gehen wir hier nur auf die h\u00e4ufigsten Fallstricke ein:<\/p>\n<h4 class=\"wp-block-heading\"><em>Fehler 1: Java-Streams statt Kotlin-Collections verwenden<\/em><\/h4>\n<p>Tipp: <strong>Verwenden Sie stets Kotlin-Collections.<\/strong><\/p>\n<p>Kotlin-Collections sind vollst\u00e4ndig kompatibel mit Java-Collections, verf\u00fcgen jedoch \u00fcber einfache und reichhaltige Funktionen h\u00f6herer Ordnung, die Java-Streams \u00fcberfl\u00fcssig machen.<\/p>\n<p>Im Folgenden finden Sie ein Beispiel, das die drei umsatzst\u00e4rksten Produkte (Preis * Absatzmenge) je Produktkategorie ausweist:<\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">record Product(String name, String category, double price, int sold){}\n\nList products = List.of(\n           new Product(\"Lollipop\", \"sweets\", 1.2, 321),\n           new Product(\"Broccoli\", \"vegetable\", 1.8, 5);\n\nMap&lt;String, List&gt; top3RevenueByCategory =\n       products.stream()\n          .collect(Collectors.groupingBy(\n                Product::category,\n                Collectors.collectingAndThen(\n                    Collectors.toList(),\n                    list -&gt; list.stream()\n                              .sorted(Comparator.comparingDouble(\n                                  (Product p) -&gt; p.price() * p.sold())\n                                   .reversed())\n                                   .limit(3)\n                                   .toList()\n                       \t\t)\n          )\n);<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">val top3RevenueByCategory: Map&lt;String, List&gt; =\n   products.groupBy { it.category }\n       .mapValues { (_, list) -&gt;\n           list.sortedByDescending { it.price * it.sold }.take(3)\n       }<\/pre>\n<p>Dank der Java-Interoperabilit\u00e4t von Kotlin k\u00f6nnen Sie mit Java-Klassen und -Records arbeiten, als w\u00e4ren sie native Kotlin-Klassen \u2013 wobei Sie stattdessen auch eine Kotlin-(Daten-)Klasse verwenden k\u00f6nnten.<\/p>\n<h4 class=\"wp-block-heading\"><em>Fehler 2: Optional aus Java weiterhin verwenden.<\/em><\/h4>\n<p>Tipp: <strong>Nutzen Sie Nullable-Typen<\/strong>.<\/p>\n<p>Einer der Hauptgr\u00fcnde, warum Java-Entwickler*innen zu Kotlin wechseln, ist die integrierte Nullability-Unterst\u00fctzung von Kotlin, die NullPointerExceptions ein f\u00fcr alle Mal den Garaus macht. Versuchen Sie daher, ausschlie\u00dflich Nullable-Typen statt Optionals zu verwenden. Sie haben noch Optionals in Ihren Schnittstellen? So k\u00f6nnen Sie sie unkompliziert loswerden, indem Sie sie in Nullable-Typen konvertieren:<\/p>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/Let\u2019s assume this repository is hard to change, because it\u2019s a library you depend on\nclass OrderRepository {\n      \/\/it returns Optional, but we want nullable types\n      fun getOrderBy(id: Long): Optional = \u2026\n}\n\n\/\/Simply add an extension method and apply the orElse(null) trick\nfun OrderRepository.getOrderByOrNull(id: Long): Order? = \n                                    getOrderBy(id).orElse(null)\n\n\/\/Now enjoy the safety and ease of use of nullable types:\n\n\/\/Past:\n val g = repository.getOrderBy(12).flatMap { product -&gt;\n     product.goody.map { it.name }\n}.orElse(\"No goody found\")\n\n\/\/Future:\n val g = repository.getOrderByOrNull(12)?.goody?.name ?: \"No goody found\"<\/pre>\n<h4 class=\"wp-block-heading\"><em>Fehler 3: Weiterhin statische Wrapper verwenden.<\/em><\/h4>\n<p>Tipp: <strong>Nutzen Sie Extension-Methoden<\/strong>.<\/p>\n<p>Extension-Methoden haben zahlreiche Vorteile:<\/p>\n<ul>\n<li>Sie machen Ihren Code wesentlich fl\u00fcssiger und lesbarer als Wrapper.<\/li>\n<li>Sie k\u00f6nnen von der Code-Completion gefunden werden, was auf Wrapper nicht zutrifft.<\/li>\n<li>Da Extensions importiert werden m\u00fcssen, erm\u00f6glichen sie die selektive Nutzung erweiterter Funktionalit\u00e4ten in bestimmten Bereichen Ihrer Anwendung.<\/li>\n<\/ul>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/Very common approach in Java to add additional helper methods\npublic class DateUtils {\n      public static final DateTimeFormatter DEFAULT_DATE_TIME_FORMATTER = \n           DateTimeFormatter.ofPattern(\"yyyy-MM-dd HH:mm:ss\");\n\n      public String formatted(LocalDateTime dateTime, \n\t\t              DateTimeFormatter formatter) {\n         return dateTime.format(formatter);\n      }\n\n      public String formatted(LocalDateTime dateTime) {\n         return formatted(dateTime, DEFAULT_DATE_TIME_FORMATTER);\n      }\n}\n\n\/\/Usage\n formatted(LocalDateTime.now());\n<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">val DEFAULT_DATE_TIME_FORMATTER: DateTimeFormatter = \nDateTimeFormatter.ofPattern(\"yyyy-MM-dd HH:mm:ss\")\n\n\/\/Use an extension method, including a default argument, which omits the need for an overloaded method.\u00a0\nfun LocalDateTime.asString(\n\u00a0\u00a0\u00a0formatter: DateTimeFormatter = DEFAULT_DATE_TIME_FORMATTER): String =\u00a0\n      this.format(formatter)\n\n\/\/Usage\nLocalDateTime.now().formatted()<\/pre>\n<p>Beachten Sie, dass Kotlin Top-Level-Methoden und -Variablen erlaubt. Dies bedeutet, dass wir beispielsweise einen <em><code>DEFAULT_DATE_TIME_FORMATTER<\/code><\/em> einfach auf der obersten Ebene deklarieren k\u00f6nnen, ohne ihn wie in Java an ein Objekt binden zu m\u00fcssen.<\/p>\n<h4 class=\"wp-block-heading\"><em>Fehler 4: (Umst\u00e4ndliche) Java-APIs verwenden.<\/em><\/h4>\n<p>Tipp: <strong>Nutzen Sie das elegante Kotlin-Gegenst\u00fcck.\u00a0<\/strong><\/p>\n<p>Die Kotlin-Standardbibliothek bietet Erweiterungsmethoden, die eine erheblich benutzerfreundlichere Nutzung der Java-Bibliotheken erm\u00f6glichen, obwohl weiterhin die Java-Implementierung zugrunde liegt. Fast alle g\u00e4ngigen Bibliotheken und Frameworks von Drittanbietern, darunter auch Spring, gehen ebenso vor.<\/p>\n<p><strong>Beispiel \u2013 Standardbibliothek:<\/strong><\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">String text;\ntry (\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var reader = new BufferedReader(\n                  new InputStreamReader(new FileInputStream(\"out.txt\"), \n            StandardCharsets.UTF_8))) {\n\u00a0\u00a0\u00a0text = reader\n            .lines()\n            .collect(Collectors.joining(System.lineSeparator()));\n}\nSystem.out.println(\"Downloaded text: \" +\u00a0 text + \"n\");<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/Kotlin has enhanced the Java standard library with many powerful extension methods, like on java.io.*, which makes input stream processing a snap due to its fluent nature, fully supported by code completion\n\nval text = FileInputStream(\"path\").use {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0     it.bufferedReader().readText()\n           }\nprintln(\"Downloaded text: $textn\");<\/pre>\n<p><strong>Beispiel \u2013 Spring:<\/strong><br \/><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">final var books =\u00a0 RestClient.create()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.get()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.uri(\"http:\/\/...\/api\/books\")\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.retrieve()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.body( new ParameterizedTypeReference&lt;List&gt;(){}); \/\/ \u21e6 inconvenient ParameterizedTypeReference<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import org.springframework.web.client.body\nval books = RestClient.create()\n\u00a0\u00a0\u00a0.get()\n\u00a0\u00a0\u00a0.uri(\"http:\/\/...\/api\/books\")\n\u00a0\u00a0\u00a0.retrieve()\n\u00a0\u00a0\u00a0.body&lt;List&gt;() \/\/\u21e6 Kotlin offers an extension that only requires the type without the need for a ParameterizedTypeReference<\/pre>\n<h4 class=\"wp-block-heading\"><em>Fehler 5: F\u00fcr jede \u00f6ffentliche Klasse eine separate Datei verwenden.<\/em><\/h4>\n<p>Tipp: <strong>Fassen Sie verwandte \u00f6ffentliche Klassen in einer einzigen Datei zusammen.\u00a0<\/strong><\/p>\n<p>Auf diese Weise k\u00f6nnen Sie sich einen guten \u00dcberblick \u00fcber die Struktur eines (Teil-)Bereichs verschaffen, ohne Dutzende Dateien durchlesen zu m\u00fcssen.<\/p>\n<p><strong>Java<\/strong><\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-647331\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/10\/unnamed-15.png\" alt=\"\" width=\"1114\" height=\"934\" \/><\/figure>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/For domain classes consider data classes - see why below\ndata class User(val email: String,\n            \/\/Use nullable types for safety and expressiveness\n\u00a0 \u00a0        val avatarUrl: URL? = null,\u00a0\n           var isEmailVerified: Boolean)\n\ndata class Account(val user:User,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0val address: Address,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0val mfaEnabled:Boolean,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0val createdAt: Instant)\n\ndata class Address(val street: String,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0val city: String,\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 val postalCode: String)<\/pre>\n<h4 class=\"wp-block-heading\"><em>Fehler 6: Am Paradigma der Mutabilit\u00e4t festhalten.<\/em><\/h4>\n<p>Tipp: <strong>Auf Immutabilit\u00e4t setzen \u2013 den Standard in Kotlin<\/strong><\/p>\n<p>In vielen Programmiersprachen \u2013 einschlie\u00dflich Java \u2013 gibt es einen eindeutigen Trend: Immutabilit\u00e4t sticht Mutabilit\u00e4t aus.<\/p>\n<p>Der Grund daf\u00fcr ist einfach: Immutabilit\u00e4t (Unver\u00e4nderlichkeit) verhindert unbeabsichtigte Nebenwirkungen, und dadurch wird der Code sicherer, vorhersehbarer und leichter nachvollziehbar. Auch die Nebenl\u00e4ufigkeit wird dadurch vereinfacht, da unver\u00e4nderliche Daten problemlos von mehreren Threads gemeinsam genutzt werden k\u00f6nnen, ohne dass Race-Conditions auftreten k\u00f6nnen.<\/p>\n<p>Aus diesem Grund setzen die meisten modernen Sprachen \u2013 darunter auch Kotlin \u2013 entweder standardm\u00e4\u00dfig auf Immutabilit\u00e4t oder empfehlen diese nachdr\u00fccklich. In Kotlin ist Immutabilit\u00e4t der Standard, obwohl auch Mutabilit\u00e4t eine Option ist, wenn sie tats\u00e4chlich gebraucht wird.<\/p>\n<p>Hier ist eine kurze Einf\u00fchrung in die <strong>Immutabilit\u00e4t in Kotlin<\/strong>:<\/p>\n<p>1. Nutzen Sie <code>val<\/code> anstelle von <code>var<\/code>.<\/p>\n<p>Sie sollten <code>val<\/code> gegen\u00fcber <code>var<\/code> den Vorzug geben. IntelliJ IDEA warnt Sie, wenn Sie an einer Stelle <code>var<\/code> verwenden, wo auch <code>val<\/code> ausreichen w\u00fcrde.<\/p>\n<p>2. Verwenden Sie (unver\u00e4nderliche) Datenklassen mit <code>copy(...)<\/code>.<\/p>\n<p>F\u00fcr dom\u00e4nenbezogene Klassen verwenden Sie <code>data<\/code>-Klassen mit <code>val<\/code>. Die <code>data<\/code>-Klassen von Kotlin werden h\u00e4ufig mit den <code>records<\/code> von Java verglichen. Zwar gibt es einige \u00dcberlappungen, jedoch bieten <code>data<\/code>-Klassen das Killer-Feature <code>copy(...)<\/code>, dessen Fehlen in Java das Ver\u00e4ndern von <code>Records<\/code> sehr m\u00fchsam macht, obwohl dies in der Anwendungslogik ein allt\u00e4glicher Vorgang ist:<\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/only immutable state\npublic record Person(String name, int age) {\n\u00a0\u00a0\u00a0\/\/Lack of default parameters requires overloaded constructor\n\u00a0\u00a0\u00a0public Person(String name) {\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this(name, 0);\n\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\/\/+ due to lack of String interpolation\n\u00a0 public String sayHi() {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return \"Hello, my name is \" + name + \" and I am \" + age + \" years old.\";\n\u00a0\u00a0\u00a0}\n}\n\n\/\/Usage\nfinal var jack = new Person(\"Jack\", 42);\njack: Person[name=Jack, age=5]\n\n\/\/The issue is here: transforming a record requires manually copying the identical state to the new instance \u2639\ufe0f\nfinal var fred = new Person(\"Fred\", jack.name);<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/also supports mutable state (var)\ndata class Person(val name: String,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0val age: Int = 0) {\n\u00a0 \/\/string interpolation\n\u00a0 fun sayHi() = \"Hi, my name is $name and I am $age years old.\"\n}\nval jack = Person(\"Jack\", 42)\njack: Person(name=Jack, age=42)\n\n\/\/Kotlin offers the copy method, which, due to the \u2018named argument\u2019 feature, allows you to only adjust the state you want to change \ud83d\ude03\nval fred = jack.copy(name = \"Fred\")\nfred: Person(name=Fred, age=42)<\/pre>\n<p>Verwenden Sie au\u00dferdem nach M\u00f6glichkeit Datenklassen f\u00fcr dom\u00e4nenbezogene Klassen. Durch ihre Unver\u00e4nderlichkeit gew\u00e4hrleisten diese eine sichere, pr\u00e4gnante und reibungslose Nutzung im Kernbereich Ihrer Anwendung.<\/p>\n<p>Tipp: <strong>Verwenden Sie unver\u00e4nderliche statt ver\u00e4nderliche Collections<\/strong><\/p>\n<p>Unver\u00e4nderliche Collections bieten eindeutige Vorteile hinsichtlich Thread-Sicherheit, k\u00f6nnen sicher weitergegeben werden und sind einfacher zu verstehen. Java bietet zwar einige Funktionen in Bezug auf die Immutabilit\u00e4t von Collections, aber diese sind mit Vorsicht zu genie\u00dfen, da sie leicht zu Laufzeitfehlern f\u00fchren k\u00f6nnen:<\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">List.of(1,2,3).add(4); \u274cunsafe \ud83d\ude2c! .add(...) compiles, but throws UnsupportedOperationException<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/The default collections in Kotlin are immutable (read-only)\nlistOf(1,2,3).add(4); \u00a0\/\/\u2705safe: does not compile\n\nval l0 = listOf(1,2,3)\u00a0\nval l1 = l0 + 4 \/\/\u2705safe: it will return a new list containing the added element\nl1 shouldBe listOf(1,2,3,4) \/\/\u2705<\/pre>\n<p>Das Gleiche gilt f\u00fcr die Verwendung von <code>Collections.<em>unmodifiableList(...)<\/em><\/code>, was nicht nur unsicher ist, sondern auch eine zus\u00e4tzliche Zuweisung erfordert:<\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class PersonRepo {\n\u00a0\u00a0\u00a0private final List cache = new ArrayList();\n\u00a0\u00a0\u00a0\/\/ Java \u2013 must clone or wrap every call\n\u00a0\u00a0\u00a0public List getItems() {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return Collections.unmodifiableList(cache); \u00a0 \/\/\u26a0\ufe0fextra alloc\n\u00a0\u00a0\u00a0}\n}\n\n\/\/Usage\npersonRepo.getItems().add(joe) \u274cunsafe \ud83d\ude2c! .add(...) can be called but throws UnsupportedOperationException<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class PersonRepo {\n\n\/\/The need to type \u2018mutable\u2019 for mutable collections is intentional: Kotlin wants you to use immutable ones by default. But sometimes you need them:\n\n\u00a0\u00a0\u00a0private val cache: MutableList = mutableListOf()\n\n\u00a0\u00a0\u00a0fun items(): List = cache \/\/\u2705safe: though the underlying collection is mutable, by returning it as its superclass List, it only exposes the read-only interface\n\n}\n\n\/\/Usage\npersonRepo.items().add(joe) \/\/\u2705safe:\ud83d\ude2c! Does not compile<\/pre>\n<p>In Bezug auf Nebenl\u00e4ufigkeit sollten unver\u00e4nderliche Datenstrukturen bevorzugt werden, und dies gilt auch f\u00fcr Collections. In Java ist erh\u00f6hter Aufwand mit speziellen Collections erforderlich, die eine unterschiedliche oder eingeschr\u00e4nkte API bieten, wie beispielsweise <code>CopyOnWriteArrayList<\/code>. In Kotlin hingegen eignet sich die schreibgesch\u00fctzte <code>List<\/code> f\u00fcr nahezu alle Anwendungsf\u00e4lle.<\/p>\n<p>Wenn Sie ver\u00e4nderbare, threadsichere Collections ben\u00f6tigen, bietet Kotlin Persistent Collections (<code>persistentListOf(...)<\/code>, <code>persistentMapOf(...)<\/code>), die alle dieselbe leistungsstarke Schnittstelle nutzen.<\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ConcurrentHashMap persons = new ConcurrentHashMap();\npersons.put(\"Alice\", 23);\npersons.put(\"Bob\", \u00a0 21);\n\n\/\/not fluent and data copying going on\nMap incPersons = new HashMap(persons.size());\npersons.forEach((k, v) -&gt; incPersons.put(k, v + 1));\n\n\/\/wordy and data copying going on\npersons\n\u00a0\u00a0\u00a0.entrySet()\n\u00a0\u00a0\u00a0.stream()\n\u00a0\u00a0\u00a0.forEach(entry -&gt;\u00a0\n      entry.setValue(entry.getValue() + 1));<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">persistentMapOf(\"Alice\" to 23, \"Bob\" to 21)\n\u00a0\u00a0\u00a0      .mapValues { (key, value) -&gt; value + 1 } \/\/\u2705same rich API like any other Kotlin Map type and not data copying going on<\/pre>\n<h4 class=\"wp-block-heading\"><em>Fehler 7: Weiterhin Builder einsetzen (oder noch schlimmer: Lombok verwenden)\u00a0<\/em><\/h4>\n<p>Tipp: <strong>Verwenden Sie benannte Argumente.<\/strong><\/p>\n<p>Builder sind in Java weit verbreitet. Sie sind zwar praktisch, doch sie bedeuten zus\u00e4tzlichen Code, sind unsicher und erh\u00f6hen die Komplexit\u00e4t. In Kotlin sind sie nutzlos, da sie durch ein einfaches Sprachmerkmal abgel\u00f6st wurden: benannte Argumente.<\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public record Person(String name, int age) {\n\n\u00a0\u00a0\u00a0\/\/ Builder for Person\n\u00a0\u00a0\u00a0public static class Builder {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0private String name;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0private int age;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0public Builder() {}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0public Builder name(String name) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.name = name;\n\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return this;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0public Builder age(int age) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.age = age;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return this;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0public Person build() {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return new Person(name, age);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0}\n}\n\n\/\/Usage\nnew JPerson.Builder().name(\"Jack\").age(36).build(); \/\/compiles and succeeds at runtime\n\nnew JPerson.Builder().age(36).build(); \/\/\u274cunsafe \ud83d\ude2c: compiles but fails at runtime.<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">data class Person(val name: String, val age: Int = 0)\n\n\/\/Usage - no builder, only named arguments.\nPerson(name = \"Jack\") \/\/\u2705safe: if it compiles, it always succeeds at runtime\nPerson(name = \"Jack\", age = 36) \/\/\u2705<\/pre>\n<h3 id=\"2.-extend\/convert-an-existing-java-application\" class=\"wp-block-heading\"><strong>2. Eine bestehende Java-Anwendung erweitern\/konvertieren<\/strong><\/h3>\n<p>Wenn Sie keine M\u00f6glichkeit haben, Kotlin \u201eauf der gr\u00fcnen Wiese\u201c auszuprobieren, k\u00f6nnen Sie <a href=\"https:\/\/www.jetbrains.com\/help\/idea\/get-started-with-kotlin.html#add-kotlin-dependency\" target=\"_blank\" rel=\"noreferrer noopener\">neue Kotlin-Features oder ganze Kotlin-Module zu einem bestehenden Java-Codebestand hinzuf\u00fcgen<\/a>. Dank der m\u00fchelosen Java-Interoperabilit\u00e4t von Kotlin k\u00f6nnen Sie Kotlin-Code schreiben, der f\u00fcr Java-Aufrufer wie Java aussieht. Dieser Ansatz bietet Ihnen folgende M\u00f6glichkeiten:<\/p>\n<ul>\n<li>Schrittweise Migration ohne umfassende Neuprogrammierung<\/li>\n<li>Praxistests mit Kotlin in Ihrem spezifischen Kontext<\/li>\n<li>St\u00e4rkung des Teamvertrauens durch Kotlin-Produktionscode<\/li>\n<\/ul>\n<p>Anstatt einfach an einer beliebigen Stelle zu beginnen, w\u00e4hlen Sie einen der folgenden Ans\u00e4tze:<\/p>\n<p><strong>Von au\u00dfen nach innen:<\/strong><\/p>\n<p>Sie beginnen in den \u00e4u\u00dferen Abschnitten Ihrer Anwendung, z.\u00a0B. Controller, Batch-Job usw., und arbeiten sich dann in Richtung Anwendungskern vor. Dies bietet die folgenden Vorteile:<\/p>\n<ul>\n<li><strong>Isolierte Kompilierung<\/strong>: Au\u00dfen liegende Klassen werden <em>selten als Abh\u00e4ngigkeiten genutzt<\/em>, sodass Sie sie auf Kotlin umstellen k\u00f6nnen, ohne den Rest des Systems zu ver\u00e4ndern.<\/li>\n<li><strong>Weniger Folge\u00e4nderungen.<\/strong> Wenn Sie eine Bedienoberfl\u00e4che oder einen Controller konvertieren, k\u00f6nnen diese dank der problemlosen Interoperabilit\u00e4t bestehenden Java-Code nahezu unver\u00e4ndert aufrufen.<\/li>\n<li><strong>Kleinere PRs, einfachere Reviews.<\/strong> Sie k\u00f6nnen entweder Datei f\u00fcr Datei oder Feature f\u00fcr Feature migrieren.<\/li>\n<\/ul>\n<p><strong>Von innen nach au\u00dfen:<\/strong><\/p>\n<p>Im Kern zu beginnen und dann zu den \u00e4u\u00dferen Schichten \u00fcberzugehen ist h\u00e4ufig riskanter, da die oben erw\u00e4hnten Vorteile des Von-au\u00dfen-nach-innen-Ansatzes nicht zur Geltung kommen. In den folgenden F\u00e4llen ist jedoch auch dies eine praktikable Option:<\/p>\n<ul>\n<li><strong>Sehr kleiner oder in sich geschlossener Kern.<\/strong> Wenn Ihre Dom\u00e4nenschicht nur aus einigen POJOs und Services besteht, kann eine fr\u00fchzeitige Umstellung mit wenig Aufwand zu schaffen sein und sofort den Zugang zu idiomatischen Konstrukten (Datenklassen, Werteklassen, Sealed-Hierarchien) er\u00f6ffnen.<\/li>\n<li><strong>Architektur\u00e4nderung geplant.<\/strong> Wenn Sie ohnehin Invarianten refaktorieren oder DDD-Codemuster (Wertobjekte, Aggregate) einf\u00fchren m\u00f6chten, ist es manchmal sinnvoller, erst die Dom\u00e4ne in Kotlin neu zu konzipieren.<\/li>\n<li><strong>Strenge Nullsicherheits-Kontrakte.<\/strong> Durch die Umstellung auf Kotlin wird die Dom\u00e4ne zu einer \u201enullsicheren Festung\u201c; \u00e4u\u00dfere Java-Schichten k\u00f6nnen zwar weiterhin Nullwerte senden, die Grenzen sind jedoch explizit und leichter zu kontrollieren.<\/li>\n<\/ul>\n<p><strong>Modul f\u00fcr Modul<\/strong><\/p>\n<ul>\n<li>Wenn Ihre Architektur eher nach Funktionalit\u00e4ten als nach Schichten strukturiert ist und die Modulgr\u00f6\u00dfen \u00fcberschaubar sind, ist die st\u00fcckweise Konvertierung eine gute Strategie.<\/li>\n<\/ul>\n<p><strong>Sprachmerkmale f\u00fcr die Konvertierung von Java in Kotlin<\/strong><\/p>\n<p>Kotlin bietet verschiedene Funktionalit\u00e4ten \u2013 in erster Linie Annotationen \u2013, die daf\u00fcr sorgen, dass sich Ihr Kotlin-Code wie nativer Java-Code verh\u00e4lt. Besonders n\u00fctzlich ist dies in hybriden Umgebungen, in denen Kotlin und Java innerhalb desselben Codebestands koexistieren.<br \/><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class Person @JvmOverloads constructor(val name: String,\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 var age: Int = 0) {\n\u00a0 companion object {\n\n\u00a0 @JvmStatic\n\u00a0 @Throws(InvalidNameException::class)\n\u00a0 fun newBorn(name: String): Person = if (name.isEmpty())\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0throw InvalidNameException(\"name not set\")\n\u00a0\u00a0\u00a0\u00a0\u00a0else Person(name, 0)\n\n\u00a0\u00a0\u00a0@JvmField\n\u00a0\u00a0\u00a0val LOG = LoggerFactory.getLogger(KPerson.javaClass)\n\u00a0\u00a0}\n}<\/pre>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/thanks to @JvmOverloads an additional constructor is created, propagating Kotlin\u2019s default arguments to Java\nvar john =\u00a0 new Person(\"John\");\n\n\/\/Kotlin automatically generates getters (val) and setters (var) for Java\njohn.setAge(23);\nvar name = ken.getName();\n\n\/\/@JvmStatic and @JvmField all accessing (companion) object fields and methods as statics in Java\n\n\/\/Without @JvmStatic it would be: Person.Companion.newBorn(...)\nvar ken =\u00a0 Person.newBorn(\"Ken\");\u00a0\n\n\/\/Without @JvmField it would be: Person.Companion.LOG\nPerson.LOG.info(\"Hello World, Ken ;-)\");\n\n\/\/@Throws(...) will put the checked Exception in the method signature \ntry {\n\u00a0 Person ken =\u00a0 Person.newBorn(\"Ken\");\n} catch (InvalidNameException e) {\n\u00a0 \/\/\u2026\n}<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@file:JvmName(\"Persons\")\npackage org.abc\n\n@JvmName(\"prettyPrint\")\n\nfun Person.pretty() =\n\u00a0 \u00a0 \u00a0 Person.LOG.info(\"$name is $age old\")<\/pre>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/@JvmName for files and methods makes accessing static fields look like Java: without it would be: PersonKt.pretty(...)\nPersons.prettyPrint(ken)<\/pre>\n<p><strong>Der Java-zu-Kotlin-Konverter von IntelliJ IDEA<\/strong><\/p>\n<p>IntelliJ IDEA hat einen Java-zu-Kotlin-Konverter, sodass die IDE Ihnen die Umstellung theoretisch abnehmen kann. Der resultierende Code ist jedoch alles andere als perfekt, daher sollte er nur als Ausgangspunkt verwendet werden. Diese Ausgangsbasis sollten Sie so \u00fcberarbeiten, dass das Wesen von Kotlin st\u00e4rker zum Tragen kommt. Weitere Details zu diesem Thema folgen im letzten Abschnitt dieser Blogreihe:<em> Erfolgsfaktoren f\u00fcr eine gro\u00dffl\u00e4chige Einf\u00fchrung von Kotlin.<\/em><\/p>\n<p>Wenn Sie Java als Ausgangspunkt nehmen, werden Sie wahrscheinlich Java-\u00e4hnliches Kotlin schreiben, was Ihnen zwar auch schon einige Vorteile einbringt, aber bei weitem nicht das volle Potenzial von Kotlin aussch\u00f6pft. Daher bevorzuge ich den Ansatz, eine neue Anwendung zu schreiben.<\/p>\n<h3 class=\"wp-block-heading\">Im n\u00e4chsten Teil der Reihe<\/h3>\n<p>In diesem Teil unserer Blogreihe <em>Der ultimative Leitfaden f\u00fcr den erfolgreichen Einstieg in Kotlin in einer Java-dominierten Umgebung<\/em> haben wir gezeigt, wie Kotlin-Experimente zu Produktionscode weiterentwickelt werden k\u00f6nnen. In unserem n\u00e4chsten Artikel geht es um den menschlichen Faktor: wie Sie Ihre Kolleg*innen \u00fcberzeugen k\u00f6nnen. Sie werden erfahren, wie Sie klare, codebezogene Argumente pr\u00e4sentieren, neue Entwickler*innen anleiten und eine kleine, aber ausdauernde Kotlin-Community innerhalb Ihres Teams aufbauen k\u00f6nnen.<\/p>\n<div class=\"about-author \">\n<div class=\"about-author__box\">\n<div class=\"row\">\n<div class=\"about-author__box-img\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/10\/a3f7-400o400o1-QYUavRYyARnAVJLCpM1m8M.webp\" alt=\"\" \/><\/div>\n<div class=\"about-author__box-text\">\n<h4>Urs Peter<\/h4>\n<p>Urs ist erfahrener Softwareentwickler, L\u00f6sungsarchitekt, Vortragsredner und Dozent mit \u00fcber 20 Jahren Erfahrung in der Entwicklung robuster, skalierbarer und unternehmenskritischer Systeme, haupts\u00e4chlich in Kotlin und Scala.<\/p>\n<p>Neben seiner T\u00e4tigkeit als Berater ist er leidenschaftlicher Dozent und Autor einer gro\u00dfen Vielfalt von Kursen, die von Spracheinf\u00fchrungen f\u00fcr Kotlin und Scala bis hin zu Architekturschulungen f\u00fcr Microservices und ereignisgesteuerte Architekturen reichen.<\/p>\n<p>Als kontaktfreudiger Mensch ist er gerne auf Treffen und Konferenzen, um sein Wissen zu teilen, andere zu inspirieren und selbst inspiriert zu werden. Urs ist JetBrains-zertifizierter Kotlin Trainer.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p><strong>Autorin des urspr\u00fcnglichen Blogposts<\/strong><\/p>\n\n    <div class=\"about-author \">\n        <div class=\"about-author__box\">\n            <div class=\"row\">\n                <div class=\"about-author__box-img\">\n                    <img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2021\/08\/photo_2021-08-03_15-27-43-200x200.jpg\" width=\"200\" height=\"200\" alt=\"Alyona Chernyaeva\" loading=\"lazy\"  class=\"avatar avatar-200 wp-user-avatar wp-user-avatar-200 photo avatar-default\">\n                <\/div>\n                <div class=\"about-author__box-text\">\n                                            <h4>Alyona Chernyaeva<\/h4>\n                                                        <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n","protected":false},"author":964,"featured_media":672293,"comment_status":"closed","ping_status":"closed","template":"","categories":[],"tags":[],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/kotlin\/672292"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/kotlin"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/types\/kotlin"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/users\/964"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/comments?post=672292"}],"version-history":[{"count":2,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/kotlin\/672292\/revisions"}],"predecessor-version":[{"id":672316,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/kotlin\/672292\/revisions\/672316"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/media\/672293"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/media?parent=672292"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/categories?post=672292"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/tags?post=672292"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/cross-post-tag?post=672292"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}