KOTLIN: A BETTER JAVA FROM JETBRAINS
Luca Franceschini
PhD seminars, 20 September 2018
GOALS
- Concise syntax
- Object-orientation
- Statically typed
- Multiplatform
- Interoperable with Java
- IDE support
JAVA IS FINE
but...
LET'S WRITE A JAVA CLASS
public class Student {
int idNumber;
String name, email;
}
MINIMIZE ACCESSIBILITY
public class Student {
private int idNumber;
private String name, email;
}
WE NEED A CONSTRUCTOR
public class Student {
private int idNumber;
private String name, email;
public Student(int idNumber, String name, String email) {
this.idNumber = idNumber;
this.name = name;
this.email = email;
}
}
NULL CHECKS
public class Student {
private int idNumber;
private String name, email;
public Student(int idNumber, String name, String email) {
this.idNumber = idNumber;
this.name = Objects.requireNonNull(name);
this.email = Objects.requireNonNull(email);
}
}
USE ACCESSOR METHODS
public class Student {
private int idNumber;
private String name, email;
public Student(int idNumber, String name, String email) {
this.idNumber = idNumber;
this.name = Objects.requireNonNull(name);
this.email = Objects.requireNonNull(email);
}
public int idNumber() { return idNumber; }
public String name() { return name; }
public String email() { return email; }
}
MINIMIZE MUTABILITY
public class Student {
private final int idNumber;
private final String name, email;
public Student(int idNumber, String name, String email) {
this.idNumber = idNumber;
this.name = Objects.requireNonNull(name);
this.email = Objects.requireNonNull(email);
}
public int idNumber() { return idNumber; }
public String name() { return name; }
public String email() { return email; }
}
INHERITANCE IS HARD
public final class Student {
private final int idNumber;
private final String name, email;
public Student(int idNumber, String name, String email) {
this.idNumber = idNumber;
this.name = Objects.requireNonNull(name);
this.email = Objects.requireNonNull(email);
}
public int idNumber() { return idNumber; }
public String name() { return name; }
public String email() { return email; }
}
CORRECTLY OVERRIDE EQUALS
public final class Student {
private final int idNumber;
private final String name, email;
public Student(int idNumber, String name, String email) {
this.idNumber = idNumber;
this.name = Objects.requireNonNull(name);
this.email = Objects.requireNonNull(email);
}
public int idNumber() { return idNumber; }
public String name() { return name; }
public String email() { return email; }
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Student)) return false;
Student s = (Student) o;
return idNumber == s.idNumber && name.equals(s.name) && email.equals(s.email);
}
}
... THEN ALSO HASHCODE
public final class Student {
private final int idNumber;
private final String name, email;
public Student(int idNumber, String name, String email) {
this.idNumber = idNumber;
this.name = Objects.requireNonNull(name);
this.email = Objects.requireNonNull(email);
}
public int idNumber() { return idNumber; }
public String name() { return name; }
public String email() { return email; }
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Student)) return false;
Student s = (Student) o;
return idNumber == s.idNumber && name.equals(s.name) && email.equals(s.email);
}
@Override public int hashCode() {
int result = Integer.hashCode(idNumber);
result = 31 * result + name.hashCode();
result = 31 * result + email.hashCode();
return result;
}
}
ALWAYS OVERRIDE TOSTRING
public final class Student {
private final int idNumber;
private final String name, email;
public Student(int idNumber, String name, String email) {
this.idNumber = idNumber;
this.name = Objects.requireNonNull(name);
this.email = Objects.requireNonNull(email);
}
public int idNumber() { return idNumber; }
public String name() { return name; }
public String email() { return email; }
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Student)) return false;
Student s = (Student) o;
return idNumber == s.idNumber && name.equals(s.name) && email.equals(s.email);
}
@Override public int hashCode() {
int result = Integer.hashCode(idNumber);
result = 31 * result + name.hashCode();
result = 31 * result + email.hashCode();
return result;
}
@Override public String toString() {
return String.format("Student{idNumber=%d,name=%s,email=%s}", idNumber, name, email);
}
}
BOILERPLATE!
DATA CLASSES
data class Student(
val idNumber: Int,
val name: String,
val email: String)
- constructor
- null-safety
- immutability
- getters
Automatically gives
-
equals()
-
hashCode()
-
copy()
-
toString()
OBJECT-ORIENTATION
- All types in the same hierarchy
- top is - No primitive types
- like Python - No implicit conversions
- remember JavaScript? - Explicit ones usually not needed either
- type inference does its thing
Any
... and abstract classes, interfaces, enums etc.
STATIC TYPING
AND
CONCISENESS?
Type inference!
val strings = setOf("a", "b", "c", "cc")
val (idNumber, name, email) = student
for ((key, value) in map) { ... }
max(strings, { a, b -> a.length < b.length })
FLOW-SENSITIVE ANALYSIS
A.k.a. "smart casts"
if (x is String && x.length > 0) {
print(x.length)
}
NULL SAFETY
No "billion dollars mistake"
val s: String = null // compilation error
- No null pointer exceptions
- No null checking
- Compile-time null-safety
NULLABLE TYPES
If you really need them
val s: String = null // compilation error
val s2: String? = null // ok
Still some guarantees:
s2.length // error: variable 's2' can be null
HOW TO USE NULLABLES
(without making everything nullable)
- Safe call:
- Elvis operator:
- Not-null assertion:
- Flow-sensitive analysis
student?.email?.length
student?.email ?: "default@email.com"
student!!.email
if (student != null) { ...
WHY NULLS IN THE FIRST PLACE?
Java interoperability
import foo.Bar;
var x: String? = Bar.method()
Getters, setters and boolean methods follow standard naming conventions
MULTIPLATFORM
Kotlin/JVM
Kotlin/JS
Kotlin/JVM
Kotlin/Native
Kotlin/Native
The Java promise: "write once, run everywhere"
MULTIPLATFORM PROJECTS
-
Common modules
- Code not specific to any platform
- Declaration (no implementation) of platform specific code
- Only Kotlin code, only Kotlin libraries
-
Platform modules
- Implementation of declared platform specific code
- Also platform specific libraries
- Also JVM languages if targeting JVM
EXPECT/ACTUAL
(old ideas...)
package foo
expect class Foo {
fun method()
}
package foo
actual class Foo {
actual fun method() {
...
}
}
Common code
Platform code
CONCLUSION
Things I like:
- Static typing + inference
- Null safety
- Syntax
Things I don't:
- Weird default behavior with Java
Other nice things:
- Coroutines
QUESTIONS?
Kotlin
By Luca Franceschini
Kotlin
- 681