Java 1.0 kom ut 21. januar 1996.
Java er altså over 20 år gammelt, og mye er lagt til i ettertid (som ArrayList i '98, regex i '02, generics og for-each i '04).
Men alle valgene som ble gjort i starten må fortsatt støttes.
Det finnes ganske mange språk til JVM (Java Virtual Machine)
Hvorfor i det hele tatt bruke JVM til nye språk?
Skripting-språk
Språket som brukes til Gradle
Støtter 99% av Java-kode direkte
+ dynamisk typing, funksjonell programmering
println "Hello world"
// eller
System.out.println("Hello world");
def x = 1
// eller
int x = 1;
Avansert funksjonell programmering kombinert med objekt-orientering
Statisk typet
object HelloWorld {
def main(args: Array[String]): Unit = {
println("Hello, world!")
}
def matchTest(x: Int): String = x match {
case 1 => "one"
case 2 => "two"
case _ => "many"
}
}
Funksjonell programmering med Lisp-basert syntaks
Dynamisk typet
Kraftig makro-system
(defn -main []
(println "Hello, World!"))
(defmacro and
([] true)
([x] x)
([x & rest]
`(let [and# ~x]
(if and# (and ~@rest) and#))))
Funksjonelt og objekt-orientert
Avansert statisk "flow-sensitive" typing
shared void run() => newServer {
Endpoint {
path = startsWith("/");
acceptMethod = { get };
(request, response) =>
response.writeString("Say more, more clearly");
}
}.start();
Funksjonelt og objekt-orientert
Statisk typing (++)
Laget av JetBrains (som lager IntelliJ)
Relativt "tynt" lag over Java
fun main(args: Array<String>) {
println("Hello World!")
}
Ny syntaks
Ikke behov for semikolon
"Bare" println
Ikke klasse rundt alt
fun main(args: Array<String>) {
println("Hello World!")
}
To typer variabler:
Vi trenger vanligvis ikke spesifisere typer på variabler, men kan hvis vi vil
fun main(args: Array<String>) {
var sum = 0
for (s in args) {
sum += s.toInt()
}
println(sum)
val x = 25
val y : Int = 10
println(x + y)
}
Alt er nå objekter, ikke lenger noe skille mellom int og Integer i Kotlin
(men fortsatt samme ytelse som java)
int
Integer
double
Double
int[]
Int
Int?
Double
Double?
Array<Int>
Hvis vi snakker med Java:
Når noe kan være null markerer vi det med ?
For å bruke verdien må vi da sjekke eller konvertere, f.eks. med ?: (elvis-operatoren)
fun factorial(n: Int) : Int? {
if (n < 0) return null
var product = 1
for (i in 1..n) {
product *= i
}
return product
}
fun main(args: Array<String>) {
val x = factorial(3) ?: 0
println(x + 2)
}
class Person(val name: String, var age: Int) {
fun sayHi() {
println("$name says: Hi!")
}
}
fun main(args: Array<String>) {
val erik = Person("Erik", 28)
println(erik.name)
erik.sayHi()
}
Konstruktør i toppen av klassen.
Klasser er final som standard
Metoder er public som standard
Ikke noe "new"
data class Person(val name: String, var age: Int)
fun main(args: Array<String>) {
val erik = Person("Erik", 28)
println(erik) // Person(name=Erik, age=28)
// destrukturering:
val (name, age) = erik
}
Hvis man markerer en klasse med "data" genereres metoder som toString og equals, og man kan destrukturere klassen.
data class Vec(var x: Int, var y: Int) {
operator fun plus(b: Vec): Vec {
return Vec(x + b.x, y + b.y)
}
}
fun main(args: Array<String>) {
val a = Vec(2, 3)
val b = Vec(4, 5)
println(a + b)
}
Kotlin konverterer bruk av operatorer som a + b til a.plus(b)
Men selvsagt kun dersom man definerer de riktige metodene.
data class Vec(var x: Int, var y: Int) {
operator fun plus(b: Vec): Vec {
return Vec(x + b.x, y + b.y)
}
}
fun main(args: Array<String>) {
val a = Vec(2, 3)
val b = Vec(3, 5)
println(a + b == Vec(6, 8)) // true
// I Java:
System.out.println(a.plus(b).equals(new Vec(6, 8)));
}
Ved hjelp av operator overloading konverteres a == b til a.equals(b).
Hvis man faktisk vil sjekke referanselikhet (a == b for objekter i Java) bruker man a === b
val x = ...
when (x) {
is TypeALegemiddel -> println(x.styrke)
is TypeBLegemiddel -> println(x.volum)
else -> println("Type C kanskje?")
}
When er som en moderne versjon av Javas switch/case. Du kan ikke bare sjekke tall og strenger, men bl.a. sjekke type eller verdi.
I tillegg sjekker Kotlin at du har dekket alle muligheter dersom du bruker when som et uttrykk.
fun silly(x: Int) : Any = when (x) {
1 -> 2.0
2 -> "Hello"
else -> 5
}
fun main(args: Array<String>) {
val x = silly(2)
if (x is String) println(x.length)
}
Hvis Kotlin kan vite typen til en variabel trenger du ikke caste den for å bruke metoder o.l.
Du kan bruke Intellij IDEA, men uansett er det greit å sjekke ut oppgavene på:
God dokumentasjon på:
https://kotlinlang.org/docs/reference/
(Og begge deler er lett tilgjengelig fra forsiden)
fun Int.square(): Int {
return this * this
}
operator fun Int.times(b: Vec): Vec {
return Vec(this * b.x, this * b.y)
}
Du kan utvide eksisterende klasser med nye metoder (m.m.)
Spesielt nyttig til operator overloading
object Highlander {
val id = 1
}
Deklarasjoner av object er klasser med bare en instans (en "singleton")
sealed class Direction {
object North : Direction()
object South : Direction()
object East : Direction()
object West : Direction()
}
fun Vec.move(dir: Direction) = when (dir) {
Direction.North -> y += 1
Direction.South -> y -= 1
Direction.East -> x += 1
Direction.West -> x -= 1
}
Når vi markerer en klasse med sealed kan den bare arves inne i klassen.
Det gjør det mulig for kompilatoren å se at vi har testet alle muligheter i en "when". (Her kunne man egentlig brukt enum)
// metode-referanse
listOf(1, 2, 3).reduce(Int::times)
// lambda-uttrykk må være i { }
listOf(1, 2, 4).map({x -> x * x})
// men lambda-uttrykk som siste parameter
// trenger ikke stå i paranteser
// og har man bare ett argument kan man bruke
// "it" som navn uten å spesifisere det
listOf(2, 4, 6).filter {it > 3}
Kotlin støtter selvsagt lambda-uttrykk og høyere ordens funksjoner.
Gradle og Maven med Kotlin: