Touchè
Ich könnte natürlich jetzt noch seitenweise über das Vermischen von Prä- und Postfix Notation ranten…
Mal eine Frage, da Kotlin immer so positiv erwähnt wird: Wie sind da eure Erfahrungen? Fühlt es sich an wie Java mit gewissen Vorzügen, rechtfertigt der Aufwand die neuen Konstrukte zu lernen einen Boost an Produktivität? Ist es hauptsächlich Syntax Sugar, oder gibt es da noch mehr?
Edit: Mit Clojure hatte ich hauptsächlich angefangen, weil das Konstrukt so radikal anders als Java ist, und ich die 1000e Webbasierte Objektorientierte Anwendung so langsam nicht mehr sehen konnte. Mittlerweile habe ich aus dem funktionalen Paradigma einiges in meinen normalen Programmierstil aufgenommen. Und bei der Sprache geblieben bin ich dann wegen der großartigen Community.
Habs mir bisher nur im privaten angesehen und bin echt begeistert von der Sprache. Es hat zwar was von Java - fühlt sich aber nicht unbedingt danach an (dafür ist kotlin einfach viel moderner gestaltet).
Wovon ich ein großer Fan bin (auch wenn ichs bisher nur in Spielereien einsetzen konnte) ist die Möglichkeit eigene DSLs in Kotlin selber zu schreiben. Das fw kotlintest gebraucht es z.B. ganz gerne:
class MyTests : StringSpec() {
init {
"length should return size of string" {
"hello".length shouldBe 5
}
}
}
das obige wird mit extension methods und infix-functions erreicht. Das schöne, das ganze lässt sich mit einem scope umsetzen. D.h. extensions & co sind dann z.B. nur für eine bestimmte Klasse verfügbar.
KSKB:
fun main(args: Array<String>) {
// "funst hier nicht" {} <-- Geht nicht, da out of scope
MyTest()
// (3+5) shouldBe 5 <-- Ebenso hier: shouldBe ist außerhalb der Klasse Test nicht bekannt
}
class MyTest : Test() {
init {
"Successfull test" {
Thread.sleep(10)
(1+2) shouldBe 3
}
"shouldBe fails" {
(1+2) shouldBe 5
}
"Default exception" {
throw RuntimeException("Depp")
}
}
}
open class Test {
infix fun Any.shouldBe(value: Any) {
if(!this.equals(value)) {
throw RuntimeException("$this is not equal to $value")
}
}
operator fun String.invoke(testCase: () -> Unit) {
println("Execute test: $this")
val timeStamp = System.currentTimeMillis()
try {
testCase()
} catch(e: Exception) {
println("$this died with exception $e")
}
println("$this took ${System.currentTimeMillis() - timeStamp}ms")
println()
}
}
Ausgabe:
Execute test: Successfull test
Successfull test took 12ms
Execute test: shouldBe fails
shouldBe fails died with exception java.lang.RuntimeException: 3 is not equal to 5
shouldBe fails took 0ms
Execute test: Default exception
Default exception died with exception java.lang.RuntimeException: Depp
Default exception took 1ms
Process finished with exit code 0
Dinge die für den Alltag vllt etwas gebräuchlicher sind:
-
Data-klassen (für reine Datenhaltung. Implementieren dann direkt Sachen wie toString, equals und hashcode und bieten noch en paar mehr gimmiks.
-
Method delegation: man kann eine Klasse von einem Interface erben lassen, über den Konstruktor eine Implementierung angeben und mittels dem by-keyword weiß kotlin dann, dass alle methoden des interfaces an die implementierung delegiert werden sollen.
-
Felder mit „Überwachung“:
class Demo {
val abc: String by Delegates.observable(…)
}
nach dem by kannst du in diesem Fall eine Implementierung anbieten, welche das Verhalten vom getter/setter definieren. Für JavaFx kann man sich dann etwas schreiben, dass das hier funst:
val textProperty: StringProperty = SimpleStringProperty(„“)
val doneProperty: BooleanProperty = SimpleBooleanProperty(false)
var text: String by textProperty
var done: Boolean by doneProperty
In diesem Beispiel würde text = „abc“ dazu führen, dass textProperty.setText(„abc“) aufgerufen würde.
Also kotlin hat schon einiges zu bieten. Würde auf jeden Fall empfehlen das mal anzuschauen.
1 „Gefällt mir“
Danke für die ausführliche Antwort, interessant klingt es auf jeden Fall.
Inwiefern kann ich denn steuern, ob die Infix Funktionen links- oder rechtsassoziativ sind, kann ich die auch verketten?
Müsste glaube ich immer links-assoziativ sein. Und ja, man kann es verketten. Siehe diese Spielerei:
fun main(args: Array<String>) {
modules {
+ AppModule activate true title "Test" config {
user = getUser() ?: "Anonymous"
println("Active: $active title: $title user: $user")
}
+ ServerModule activate true address "192.168.178.0.1" config {
connect()
}
}
}
fun getUser(): String? = null
fun modules(init: Modules.() -> Unit): Modules {
val modules = Modules()
modules.init()
return modules
}
class Modules {
val list = arrayListOf<Module>()
operator fun <M: Module> M.unaryPlus(): M {
list.add(this)
return this
}
infix fun <M: Module> M.activate(active: Boolean): M {
this.active = active
return this
}
infix fun <M : Module> M.config(moduleConfig: M.() -> Unit) {
this.moduleConfig()
}
}
object AppModule : Module() {
var title: String = ""
var user: String = "";
infix fun title (title: String): AppModule {
this.title = title
return this
}
}
object ServerModule: Module() {
var address = ""
infix fun address(address: String): ServerModule {
this.address = address;
return this;
}
fun connect() {
}
}
open class Module {
var active = false
}
Ausgabe:
Active: true title: Test user: Anonymous
Simple ausgedrückt: hier hättest du eine DSL welche es erlaubt Module zu registrieren. Jedes Modul kennt die Eigenschaft active
. Einzelne Module können aber noch weitere Eigenschaften haben, die sie dann selber über infix-functions zur Pflege bereitstellen können.
Scheint ein Fehler in der Matrix zu sein
1 „Gefällt mir“
Oder ein stumpfes Codebeispiel … man weiß es nicht
https://www.golem.de/news/google-kotlin-wird-offizielle-sprache-fuer-android-1705-127892.html
Kotlin für die Entwicklung von Android-Apps zu verwenden war schon vorher möglich - aber schön das es jetzt eine offizielle Integration erhält