Kotlin is a first-class language for Android development since Google I/O 2017. And it’s here to stay! Thanks to Android Studio it’s really easy to introduce Kotlin in an existing project, the configuration is trivial and then we can convert Java classes to Kotlin using a Alt+Shift+Cmd+K. But the new syntax is the just beginning, using Kotlin we can improve our code making it more readable and simpler to write. In this talk we’ll see how to use some Kotlin features (for example data classes, collections, coroutines and delegates) to simplify Android development comparing the code with the equivalent “modern” Java code. It’s not fair to compare Kotlin code with plain Java 6 code so the Java examples will use lambdas and some external libraries like RxJava and AutoValue.
9. /**
* Returns the first element matching the given [predicate].
* @throws [NoSuchElementException] if no such element is found.
*/
inline fun <T> Iterable<T>.first(predicate: (T) -> Boolean): T {
for (element in this) {
if (predicate(element)) {
return element
}1
}2
throw NoSuchElementException(
"Collection contains no element matching the predicate.")
}3
10. val list = listOf("Java", "Kotlin")
val s = list.first({_v: String ->
v.length > 4
_})
11. val list = listOf("Java", "Kotlin")
val s = list.first({_v ->
v.length > 4
_})
12. val list = listOf("Java", "Kotlin")
val s = list.first({_
it.length > 4
_})
13. val list = listOf("Java", "Kotlin")
val s = list.first {_
it.length > 4
_}
14. val list = listOf("Java", "Kotlin")
val s = list.first { it.length > 4_}
15. public static <T> T first(Iterable<T> list, Predicate<T> predicate) {_
for (T t : list) {__
if (predicate.test(t)) {___
return t;
}____
}_____
throw new NoSuchElementException(
"Collection contains no element matching the predicate.");
}______
inline fun <T> Iterable<T>.first(predicate: (T) -> Boolean): T {_
for (element in this) {__
if (predicate(element)) {___
return element
}____
}____
throw NoSuchElementException(
"Collection contains no element matching the predicate.")
}_____
16. List<String> list = Arrays.asList("Java", “Kotlin");
String s = first(list, p -> p.length() > 4);
17. List<String> list = Arrays.asList("Java", “Kotlin");
String s = first(list, p -> p.length() > 4);
val list = listOf("Java", "Kotlin")
val s = list.first { it.length > 4 }
18.
19. inline fun <T> Iterable<T>.first(predicate: (T) -> Boolean): T {
for (element in this) {
if (predicate(element)) {
return element
}
}
throw NoSuchElementException(
"Collection contains no element matching the predicate.")
}
public static <T> T first(List<T> list, Predicate<T> predicate) {
for (T t : list) {
if (predicate.test(t)) {
return t;
}
}
throw new NoSuchElementException(
“Collection contains no element matching the predicate.");
}
21. val list = listOf("Java", "Kotlin")
val s = list.first { it.length > 4 }
inline fun <T> Iterable<T>.first(predicate: (T) -> Boolean): T {
for (element in this) {
if (predicate(element)) {
return element
}
}
throw NoSuchElementException(
"Collection contains no element matching the predicate.")
}
22. val list = listOf("Java", "Kotlin")
var ret: String? = null
for (element in list) {
if (element.length > 4) {
ret = element;
break
}
}
if (ret == null)
throw NoSuchElementException(
"Collection contains no element matching the predicate.")
val s = ret
24. class Person(var name:_String)_
public class Person {
private String name;
public Person(String name) {
this.name = name;
}1
public String getName() {
return name;
}2
public void setName(String name) {
this.name = name;
}3
}4
25. public class Person {
private long id;
private String name;
private int age;
public Person(long id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public long getId()_{_return id;_}
public String getName()_{_return name;_}
public int getAge()_{_return age;_}
}4
class Person(
val id: Long,
val name: String,
val age: Int
)_
26. @Parcelize
data class Person(
val id: Long,
val name: String,
val age: Int
)_: Parcelable
public class Person implements Parcelable {
private long id;
private String name;
private int age;
public Person(long id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}1
protected Person(Parcel in) {
id = in.readLong();
name = in.readString();
age = in.readInt();
}2
public long getId()_{_return id;_}
public String getName()_{_return name;_}
public int getAge()_{_return age;_}
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person that = (Person) o;
if (id != that.id) return false;
if (age != that.age) return false;
return name != null ? name.equals(that.name) : that.name == null;
}3
@Override public int hashCode() {
int result = (int) (id ^ (id >>> 32));
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
return result;
}4
@Override public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + ''' +
", age=" + age +
'}';
}5
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(id);
dest.writeString(name);
dest.writeInt(age);
}6
@Override
public int describeContents() {
return 0;
}7
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}8
@Override
public Person[] newArray(int size) {
return new Person[size];
}9
};
}_
27. @AutoValue
public abstract class Person
implements Parcelable {_
public static Person create(
long id, String name, int age) {
return new AutoValue_Person(
id, name, age);
}__
public abstract long id();
public abstract String name();
public abstract int age();
public abstract Person withName(String name);
}_
@Parcelize
data class Person(
val id: Long,
val name: String,
val age: Int
)_: Parcelable
28. val p = Person(1, "name", 10)
val p1 = p.copy(name = "newName", age = 20)_
Person p = Person.create(1, "name", 10);
Person p1 = p.withName("newName");
29. val p = Person(1, "name", 10)
val (_, name, age) = p
println("$name $age")
30. data class Pair<out A, out B>(
val first: A,
val second: B
) : Serializable {
override fun toString(): String = "($first, $second)"
}_
31. data class Pair<out A, out B>(
val first: A,
val second: B
) : Serializable {
override fun toString(): String = "($first, $second)"
}_
val myPair: Pair<Int, Int> = Pair(10, 20)
val otherPair: Pair<Int, Int> = 10 to 20
infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
34. System.out.println(b.toString());
List<Person> people = Arrays.asList(...);
StringBuilder b = new StringBuilder();
for (String s : cities) {
if (b.length() > 0) {
b.append(", ");
}3
b.append(s);
}4
for (Person person : people) {
City city = person.getAddress().getCity();
if (city.getRegion().equals("Tuscany")) {
cities.add(city.getName() + " (" + city.getCode() + ")");
}1
}2
Set<String> cities = new TreeSet<>();
35. val people = listOf(...)
val s = people
.map { it.address.city }
.filter { it.region == "Tuscany" }
.distinct()
.sortedBy { it.name }
.joinToString { "${it.name} (${it.code})" }
println(s)
38. val people = listOf(...)
val s = people
.asSequence()
.map { it.address.city }
.map { "${it.name} (${it.code})" }
.first { it.region == "Tuscany" }
println(s)
39. val youngest = people.minBy { it.age }
val peopleByCity: Map<City, List<Person>> =
people.groupBy { it.address.city }
val all: Boolean = people.all {
it.address.city.name == "Florence"
}1
val any: Boolean = people.any {
it.address.city.name == "Florence"
}2
val (adults: List<Person>, minors: List<Person>) =
people.partition { it.age >= 18 }
40. val readOnlyList: List<Int> = listOf(10, 20, 30)
val secondElement = readOnlyList[1]
val mutableList: MutableList<Int> = mutableListOf(10, 20, 30)
mutableList[1] = 21
val map = mapOf(1 to "ABC", 2 to "DEF")
val abc = map[1]
val mutableMap = mutableMapOf(1 to "ABC", 2 to "DEF")
mutableMap[3] = "XYZ"
val otherList = (mutableList - readOnlyList) + mutableMap.keys
43. /**
* Delays coroutine for a given time without blocking
* a thread and resumes it after a specified time.
* ...
*/
suspend fun delay(time: Long, unit: TimeUnit = MILLISECONDS) {
//...
}
64. class TokenHolder(prefs: SharedPreferences) {
var token by prefs.string()
}1
fun SharedPreferences.string(
defaultValue: String = "",
key: String? = null
): ReadWriteProperty<Any, String> {
return object : ReadWriteProperty<Any, String> {
override fun getValue(thisRef: Any, property: KProperty<*>): String {
return getString(key ?: property.name, defaultValue)
}2
override fun setValue(thisRef: Any, property: KProperty<*>,
value: String) {
edit().putString(key ?: property.name, value).apply()
}3
}4
}5
65. class TokenHolder(prefs: SharedPreferences) {
var token by prefs.string()
}1
tokenHolder.token += "ABC"
prefs.edit().putString(
"token",
prefs.getString("token", "") + "ABC"
).apply()
66. Wrapping up
inline methods to avoid extra classes
functional code is natural in Kotlin
code can be simplified using data
classes, coroutines and delegates