?
for help
Russische Insel
im finnischen Golf, 32km vor Petersburg
statisch typisierte
Allzweck-Sprache
für JVM und Browser
Freie Software
APL 2.0 – IDE, compiler, libs, build tools
backed by JetBrains
20 Vollzeit & Community
„dogfooding“: 0,5 Mio. LOC
Stabil
Start: ~2010, v1.0 Feb. '16
Long-term backward compatibility
Integrationen
IntelliJ, Eclipse
Maven, Ant, Gradle
import java.util.*
fun main(args: Array<String>) {
val name = if (args.size > 0) args[0] else "Publikum"
val zuschauer = Gast(name, title = Title.wertes)
println(zuschauer)
println("Hallo ${zuschauer.title} ${zuschauer.name}")
}
data class Gast(val name: String,
var zeit: Date = Date(),
val title: Title?)
enum class Title { Herr, Frau, wertes }
← Optional semicolons
← Top-level functions
← Kein new
,
named Params
← String interpolation
← Default values
← val
≙ public final
fun noTypeInfer(vals: List<Int>): Unit {
val distinct: Set<Int> = vals.toSet()
val first: Int = vals[0]
val negs: List<Int> = vals.filter(
fun(n: Int): Boolean {
return n < 0
}
)
}
fun typeInfer(vals: List<Int>){
val distinct = vals.toSet()
val first = vals[0]
val negs = vals.filter(
fun(n) = n < 0
)
}
Typangaben üblicherweise nur an Schnittstellen erforderlich
data class Gast(
val name: String,
var zeit: Date = Date(),
val title: Title?
)
equals()
hashCode()
toString()
copy()
für VariantencomponentN()
für Destructuringpublic final class Gast {
@NotNull
private final String name;
@NotNull
private Date zeit;
@Nullable
private final Title title;
@NotNull
public final String getName() {
return this.name;
}
@NotNull
public final Date getZeit() {
return this.zeit;
}
public final void setZeit(@NotNull Date date) {
checkParameterIsNotNull(date, "<set-?>");
this.zeit = date;
}
@Nullable
public final Title getTitle() {
return this.title;
}
public Gast(@NotNull String name,
@NotNull Date zeit,
@Nullable Title title) {
checkParameterIsNotNull(name, "name");
checkParameterIsNotNull(zeit, "zeit");
this.name = name;
this.zeit = zeit;
this.title = title;
}>
public Gast(String string, Date date,
Title title, int n) {
if ((n & 2) != 0) {
date = new Date();
}
this(string, date, title);
}
@NotNull
public final String component1() {
return this.name;
}
@NotNull
public final Date component2() {
return this.zeit;
}
@Nullable
public final Title component3() {
return this.title;
}
@NotNull
public final Gast copy(@NotNull String name,
@NotNull Date zeit,
@Nullable Title title) {
checkParameterIsNotNull(name, "name");
checkParameterIsNotNull(zeit, "zeit");
return new Gast(name, zeit, title);
}
public String toString() {
return "Gast(name=" + this.name +
", zeit=" + this.zeit +
", title=" + this.title + ")";
}
public int hashCode() {
String s = this.name;
Date d = this.zeit;
Title t = this.title;
return ((s != null ? s.hashCode() : 0) * 31
+ (d != null ? d.hashCode() : 0)) * 31
+ (t != null ? t.hashCode() : 0);
}
public boolean equals(Object object) {
if (this == object) return true;
if (!(object instanceof Gast))
return false;
Gast gast = (Gast)object;
if (!areEqual(this.name, gast.name)
|| !areEqual(this.zeit, gast.zeit)
|| !areEqual(this.title, gast.title))
return false;
return true;
}
}
… 3 Attribute!
getX()
/setX()
-Paare
x
Mehr Ausdrücke (if
, ?:
, ?.
, …)
Pattern matching (when
)
String Templates
Syntactic Sugar
listOf(…)
, repeat(3){…}
,
with(x){…}
, x.apply{…}
, …
public
ist Standard
kein new
; ;
optional
Operatoren & Dekomposition über Namen
Ausdrucksstärke
Standard-Bibliothek
Konventionen
if
ist ein Ausdruckfun isEven(i: Int) = if (i.mod(2) == 0) "gerade" else "ungerade"
with( … ) { }
: Parameter wird im Block zum this
private fun toJSON(aha: Aha): JSONObject {
with(JSONObject()) {
put(JSON_ID, aha.id.toString())
put(JSON_TITLE, aha.title)
put(JSON_USEFUL, aha.isUseful)
return this
}
}
Varianten: apply()
, let()
und use()
als try-with
-Equivalent
Feste Menge & Reihenfolge an Operatoren (vs. Scala).
Diese werden über Namenskonventionen gemapped:
+
→ .plus()
-
→ minus()
..
→ .rangeTo()
[x]
→ get(x)
Nur-lesbare Variablen
aufwandsfrei durch val
statt var
Standard-Collection Interfaces (List
, Set
, …) sind read-only
Zum ändern muss man z.B. MutableList
statt List nutzen
Klassen & Methoden sind final
by Default
open class ExtendableClass {
open fun overridableMethod() { … }
}
null
und seine Folgen
Nur explizit Nullwert-fähige Typen
akzeptieren null
.
Ungeprüfte Zugriffe führen zu Compile-Fehlern!
var nullable: String? = null
var nonNullable: String = "Hi!"
// Ab hier compiliert nichts mehr …
nonNullable = nullable
nonNullable = null
nullable.isEmpty()
An den Schnittstellen baut Kotlin
Prüfungen & @NotNull
Annotationen ein.
nullable = nullable?.toUpperCase()
nonNullable = nullable ?: ""
// equals/orEmpty für String?
nonNullable = nullable.orEmpty()
val b = nullable.equals("foo")
if (nullable != null) {
nonNullable = nullable.trim()
}
//NPE-Gefahr voraus!
nullable!!.trim(3)
nonNullable = nullable!!
TextView child = null;
if (toolbar != null && toolbar.getChildAt(0) instanceof TextView) {
child = (TextView) toolbar.getChildAt(0);
}
textView = child != null ? child : new TextView(getBaseContext());
if (textView instanceof EditText) {
((EditText) textView).selectAll();
}
val child = toolbar?.getChildAt(0) as? TextView
textView = child ?: TextView(baseContext)
if (child is EditText) {
child.selectAll()
}
EditText editTitle = (EditText) v.findViewById(R.id.edit_title);
editTitle.setText(mItem.getTitle());
CheckBox enabledBox = (CheckBox) v.findViewById(R.id.enable_box);
enabledBox.setChecked(true);
Button createButton = (Button) v.findViewById(R.id.create_entry);
createButton.setOnClickListener(new OnClickListener() {
@Override public void onClick(View button) {
createElement();
}
});
… der Zugriff auf die Views in den Activities oder
Fragments über die findViewById()
-Methode:
Das Kotlin Android Extensions erzeugt virtuelle mylayout.xml
-Pakete
import kotlinx.android.synthetic.main.mylayout.*
edit_title.setText(mItem.title)
enable_box.isChecked = true
create_entry.setOnClickListener { createElement() }
Ein einfacher Import erlaubt typsicheren Zugriff auf alle
darin enthaltene View-Elemente direkt als simples Property.
Ohne zusätzlichen Code, Annotations oder Runtime!
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
doSomething(view);
}
};
view.setOnClickListener({ view -> doSomething(view) })
view.setOnClickListener({ view -> doSomething() })
Nur ein Lambda-Parameter? x -> …
kann
durch it
ersetzt werden
view.setOnClickListener({ doSomething(it) })
Lambda ist letzter Aufrufparameter? Runde Klammern optional.
view.setOnClickListener { doSomething(it) }
Einsatz in vielen Bibliotheksfunktionen
println("I will not ...")
repeat(3) { println(" ...repeat myself") }
How it works
public inline fun repeat(times: Int, action: (Int) -> Unit) {
for (index in 0..times - 1) {
action(index)
}
}
inline
: Wirkung wie Makro-Element. Kein Funktionsoverhead!
this
zeigt auf das erweitertete Objekt// Extension method für ein Android Fragment
fun Fragment.toast(message: String, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this.getActivity(), message, duration).show()
}
// Extension property für das Android EditText Widget
var EditText.stringValue: String
get() = text.toString()
set(str) { setText(str) }
// Verwendung
toast("Hallo Zuschauer!")
nameField.stringValue = "Max Muster"
List<String> list = Arrays.asList("a","b","c");
for (int index = 0; index < list.size(); index++) {
if (index % 2 == 0)
System.out.println(index + " -> " + list.get(index));
}
val list = listOf("a", "b", "c")
for (index in list.indices.filter { it % 2 == 0 }) {
println("$index -> ${list[index]}")
}
Ziel: Compiler vergleichbar schnell wie Java (vs. Scala)
Gute Auto-Completion und Hinweise & Inspections in der IDE mit DSLs Grund #1 für Gradle auf Kotlin zu setzen
Compiler: Mischen von Java & Kotlin-Code bei problemlosem Zugriff in beide Richtungen
Automatische Konvertierung von Java-Code
@JvmField
null
– Typische KonstellationWerte für Initialisierung erst im Android-Callbacks bekannt …
class EditPersonActivity : FragmentActivity() {
var person: Person
var anrede: String
var imm: InputMethodManager
…
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val personId = savedInstanceState?.getInt("value",0)
person = PersonManager.load(id)
anrede = resources.getString(R.string.anrede)
imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
}
}
lateinit
Properties (empfohlen)lateinit var person: Person
var anrede: String by Delegates.notNull()
val imm: InputMethodManager by lazy {
getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
}
Funktionen sind Sprachelemente erster Klasse und können daher auch als Variablen, Properties, Parameter und Rückgabewerte genutzt werden.
Durch Verschachtelung lassen sich Funktionen höherer Ordnung (Higher-order functions) erschaffen
class FirstClassFunction(
val f1: (String) -> Int,
val f2: (Int) -> Boolean) {
fun strToBool(str: String): Boolean {
val f: (String) -> Boolean = higherOrderFun()
return f(str)
}
private fun higherOrderFun(): (String) -> Boolean {
return { x -> f2(f1(x)) }
}
}
fun main(args: Array<String>) {
val c = FirstClassFunction(
Integer::parseInt,
{ it % 2 == 0 }
)
val strings = listOf("2", "7", "8")
println(strings.filter(c::strToBool)) // Kotlin 1.1+
}
← Funktion als Parameter
← Funktion als Variable
← Funk. als Rückgabewert
← Kombination via Lambda
← Funk.-Referenz
← Lambda-Ausdruck
→ Was kommt raus?
Es ist möglich, Funktionssignaturen zu definieren, die Lambda-Ausdrücke
erwarten bei denen this
auf Zielobjekte eines bestimmten Typs zeigen.
Darüber sind typsichere DSLs möglich:
class HTML {
fun body() { ... }
}
fun html(init: HTML.() -> Unit): HTML {
val html = HTML()
html.init()
return html
}
html {
body()
}
← Erwartet Funktion mit this
vom Typ HTML
← Erstellung des receiver object
← Führe Lambda auf receiver object aus
← Kurzform von html({…})
← Lambda-Ausdruck für einen HTML
-„Empfänger“
relativeLayout {
lparams(width = matchParent, height = matchParent)
backgroundResource = R.drawable.background_activated
checkBox {
enabled = false
gravity = Gravity.CENTER
padding = dip(4)
lparams(width = wrapContent, height = wrapContent) {
alignParentRight()
}
}
textView {
textResource = R.string.aha_title
rightPadding = dip(4)
leftPadding = dip(4)
lparams(width = matchParent, height = wrapContent) {
leftOf(cb)
}
}
}
infix fun Int.shl(x: Int): Int {
…
}
1 shl 2 // als Equivalent zu 1.shl(2)
infix
Schlüsselwort markiertBildnachweis: Kotlin Islands Maps, Atomic bomb explosion, Android Robot, Kotlin Logo, Hand-written arrows