Dieser Artikel zeigt auf, wie sich ein existierendes Java Spring Boot Projekt in ein Kotlin Spring Boot Projekt überführen lässt. Dabei kann das Projekt schrittweise migriert werden. Dies ermöglicht Kotlin durch seine 100 % Interoperabilität zu Java. Entwickler können somit neue Funktionen in Kotlin schreiben und auf den bestehenden Java-Code zugreifen. Den Entwicklern steht zudem frei, ob bestehender Java-Code in Kotlin überführt werden soll. Hierfür zeigt dieser Blogeintrag auf, wie mithilfe der IDE IntelliJ IDEA aus Java Code Kotlin Code generiert werden kann.
Da wir ein gemischtes Projekt aus Java und Kotlin haben möchten sieht die Ziel-Projektstruktur wie folgt aus:
1.
2|-- "pom.xml"
3|-- "src"
4| |-- "main"
5| | |-- "java"
6| | |-- "kotlin"
7| | `-- "resources"
8| `-- "test"
9| |-- "java"
10| |-- "kotlin"
11| `-- "resources"
Java-Code behält wie gewohnt den “java” Ordner. Zusätzlich hinzu kommt der Ordner “kotlin”, der in src/main und in src/test eingeordnet wird. Benötigte Ressourcen werden wie gewohnt in src/main/resources bzw. src/test/resources abgelegt.
In diesem Beispiel wird Kotlin in der Version 1.3.61, sowie Java 8 genutzt.
1<properties>
2 <java.version>8</java.version>
3 <kotlin.version>1.3.61</kotlin.version>
4</properties>
Dem Projekt werden diverse Kotlin Abhängigkeiten hinzugefügt.
1<!-- Kotlin-Erweiterungen -->
2<dependency>
3 <groupId>org.jetbrains.kotlin</groupId>
4 <artifactId>kotlin-stdlib-jdk8</artifactId>
5</dependency>
6<dependency>
7 <groupId>org.jetbrains.kotlin</groupId>
8 <artifactId>kotlin-reflect</artifactId>
9</dependency>
10<dependency>
11 <groupId>com.fasterxml.jackson.module</groupId>
12 <artifactId>jackson-module-kotlin</artifactId>
13</dependency>
Neben den Abhängigkeiten wird das Projekt noch mit diversen Plugins versehen. So wird das Spring Boot Projekt mit dem Spring Compiler Plugin versehen. Dies wird benötigt, da in Kotlin standardmäßig alle Klassen final sind. Mit Hilfe dieses Compiler Plugins werden Kotlin Klassen auf “open” gesetzt, was in Java einer nicht “final Klasse” entspricht.
Projekte die JPA verwenden sollten zusätzlich das JPA Kotlin Plugin aktivieren. Dies wird benötigt um Non-Argument-Konstruktoren für @Entity, @MappedSupperclass und @Embeddable Klassen generieren zu lassen.
Da unser Projekt Java und Kotlin unterstützen soll und diese sauber getrennt in eigenen Source-Folder abgelegt werden, muss Maven entsprechend für diese Source-Folder konfiguriert werden.
Des weiteren wird der Default Compiler von Maven überschrieben. Damit wird ermöglicht, dass der Kotlin-Compiler vor dem Java-Compiler ausgeführt wird.
1<plugin>
2 <groupId>org.jetbrains.kotlin</groupId>
3 <artifactId>kotlin-maven-plugin</artifactId>
4 <configuration>
5 <args>
6 <arg>-Xjsr305=strict</arg>
7 </args>
8 <compilerPlugins>
9 <plugin>spring</plugin>
10 <plugin>jpa</plugin>
11 </compilerPlugins>
12 <jvmTarget>${java.version}</jvmTarget>
13 </configuration>
14 <executions>
15 <execution>
16 <id>compile</id>
17 <goals>
18 <goal>compile</goal>
19 </goals>
20 <configuration>
21 <sourceDirs>
22 <sourceDir>src/main/kotlin</sourceDir>
23 <sourceDir>src/main/java</sourceDir>
24 </sourceDirs>
25 </configuration>
26 </execution>
27 <execution>
28 <id>test-compile</id>
29 <goals>
30 <goal>test-compile</goal>
31 </goals>
32 <configuration>
33 <sourceDirs>
34 <sourceDir>src/test/kotlin</sourceDir>
35 <sourceDir>src/test/java</sourceDir>
36 </sourceDirs>
37 </configuration>
38 </execution>
39 </executions>
40 <dependencies>
41 <dependency>
42 <groupId>org.jetbrains.kotlin</groupId>
43 <artifactId>kotlin-maven-noarg</artifactId>
44 <version>${kotlin.version}</version>
45 </dependency>
46 <dependency>
47 <groupId>org.jetbrains.kotlin</groupId>
48 <artifactId>kotlin-maven-allopen</artifactId>
49 <version>${kotlin.version}</version>
50 </dependency>
51 </dependencies>
52</plugin>
53<plugin>
54 <groupId>org.apache.maven.plugins</groupId>
55 <artifactId>maven-compiler-plugin</artifactId>
56 <version>3.5.1</version>
57 <executions>
58 <!-- Replacing default-compile as it is treated specially by maven -->
59 <execution>
60 <id>default-compile</id>
61 <phase>none</phase>
62 </execution>
63 <!-- Replacing default-testCompile as it is treated specially by maven -->
64 <execution>
65 <id>default-testCompile</id>
66 <phase>none</phase>
67 </execution>
68 <execution>
69 <id>java-compile</id>
70 <phase>compile</phase>
71 <goals>
72 <goal>compile</goal>
73 </goals>
74 </execution>
75 <execution>
76 <id>java-test-compile</id>
77 <phase>test-compile</phase>
78 <goals>
79 <goal>testCompile</goal>
80 </goals>
81 </execution>
82 </executions>
83</plugin>
IntelliJ IDEA ermöglicht es, Java-Dateien in Kotlin-Dateien zu konvertieren. Über den Shortcut Ctrl+Shift+Alt+K (Win/Linux) bzw. ⌥+⇧+⌘+K (MacOS) wird so aus einer Java-Datei eine Kotlin-Datei (siehe Dokumentation).
Es fällt allerdings auf, dass der generierte Kotlin-Programmcode nicht wie erwartet konvertiert wird. Wie die nachfolgende beiden Beispiele aufzeigen:
Original:
1package de.byteleaf.example.springbootkotlinexample;
2
3import org.springframework.boot.SpringApplication;
4import org.springframework.boot.autoconfigure.SpringBootApplication;
5
6@SpringBootApplication
7public class SpringBootKotlinExampleApplication {
8
9 public static void main(String[] args) {
10 SpringApplication.run(SpringBootKotlinExampleApplication.class, args);
11 }
12
13}
Ergebnis der Generierung:
1package de.byteleaf.example.springbootkotlinexample
2
3import org.springframework.boot.SpringApplication
4
5object SpringBootKotlinExampleApplication {
6 @JvmStatic
7 fun main(args: Array<String>) {
8 SpringApplication.run(SpringBootKotlinExampleApplication::class.java, *args)
9 }
10}
Erwartetes Ergebnis:
1package de.byteleaf.example.springbootkotlinexample
2
3import org.springframework.boot.autoconfigure.SpringBootApplication
4import org.springframework.boot.runApplication
5
6@SpringBootApplication
7class SpringBootKotlinExampleApplication
8
9fun main(args: Array<String>) {
10 runApplication<SpringBootKotlinExampleApplication>(*args)
11}
Original:
1package de.byteleaf.example.springbootkotlinexample.pojo;
2
3public class DataClass {
4
5 private String value;
6
7 public String getValue() {
8 return value;
9 }
10
11 public void setValue(String value) {
12 this.value = value;
13 }
14}
Ergebnis der Generierung:
1package de.byteleaf.example.springbootkotlinexample.pojo
2
3class DataClass {
4 var value: String? = null
5}
Erwartetes Ergebnis:
1package de.byteleaf.example.springbootkotlinexample.pojo
2
3data class DataClass(val value: String?)
Ein bestehendes Spring Boot Projekt lässt sich gut in ein Java / Kotlin Spring Boot Projekt ändern. Entwickler können somit Kotlin nicht nur in neuen, sondern auch in bestehende Spring Boot Projekte nutzen und damit über moderne Sprachkonstrukte verfügen. Durch die von IntelliJ bereitgestellt Java-zu-Kotlin-Konvertierung kann bestehender Java-Code in Kotlin-Code konvertiert werden. Allerdings ist der generierte Programmcode oftmals nicht so wie man es erwarten würde, sodass es doch besser sein kann die Code-Tranformation manuell vorzunehmen. Durch die Interoprabilität besteht jedoch keinen Zwang, sodass der Entwickler frei entscheiden kann, ob eine Datei schlussendlich zu einem Kotlin-Code geändert werden soll.