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. @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
};
}_
10. @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();
}1
11. @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);
}1
12. @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);
}1
@Parcelize
data class Person(
val id: Long,
val name: String,
val age: Int
)_: Parcelable
13. 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");
14. data class City(
val name: String,
val code: String,
val region: String
)endCity
data class Street(
val name: String,end
val number: String
)endStreet
data class Address(
val street: Street,
val city: City
)endAddress
data class Person(
val id: Long,
val name: String,
val age: Int,
val address: Address
)endPerson
15. val p = Person(1, "name", 10,
Address(
Street("myStreet", "1/F"),
City("Florence", "FI", "Tuscany")
)_
)__
val p1 = p.copy(
address = p.address.copy(
street = p.address.street.copy(
name = "myNewStreet"
)1
)2
)3
16. val p1 = p.copy(
address = p.address.copy(
street = p.address.street.copy(
name = "myNewStreet"
)1
)2
)3
Nested copy
17. val address = p.address
val street = address.street
val newStreet = street.copy(name = "myNewStreet")
val newAddress = address.copy(street = newStreet)
val p1 = p.copy(address = newAddress)
Nested copy
val p1 = p.copy(
address = p.address.copy(
street = p.address.street.copy(
name = "myNewStreet"
)1
)2
)3
18. val address = p.address
val street = address.street
val newStreet = street.copy(name = "myNewStreet")
val newAddress = address.copy(street = newStreet)
val p1 = p.copy(address = newAddress)
Local variablesNested copy
val p1 = p.copy(
address = p.address.copy(
street = p.address.street.copy(
name = "myNewStreet"
)1
)2
)3
19. Nested copy Local variables
val p1 = p.copy(
address = p.address.copy(
street = p.address.street.copy(
name = "myNewStreet"
)1
)2
)3
val address = p.address
val street = address.street
val newStreet = street.copy(name = "myNewStreet")
val newAddress = address.copy(street = newStreet)
val p1 = p.copy(address = newAddress)
val personStreetName: Lens<Person, String> =
personAddress compose
addressStreet compose streetName
val p1 = personStreetName.modify(p) {
"newStreetName"
}_
20. Arrow - Lenses
Nested copy Local variables
val p1 = p.copy(
address = p.address.copy(
street = p.address.street.copy(
name = "myNewStreet"
)1
)2
)3
val address = p.address
val street = address.street
val newStreet = street.copy(name = "myNewStreet")
val newAddress = address.copy(street = newStreet)
val p1 = p.copy(address = newAddress)
val personStreetName: Lens<Person, String> =
personAddress compose
addressStreet compose streetName
val p1 = personStreetName.modify(p) {
"newStreetName"
}_
21. Nested copy Local variables
Arrow - Lenses
val p1 = p.copy(
address = p.address.copy(
street = p.address.street.copy(
name = "myNewStreet"
)1
)2
)3
val address = p.address
val street = address.street
val newStreet = street.copy(name = "myNewStreet")
val newAddress = address.copy(street = newStreet)
val p1 = p.copy(address = newAddress)
val personStreetName: Lens<Person, String> =
personAddress compose
addressStreet compose streetName
val p1 = personStreetName.modify(p) {
"newStreetName"
}_
val p1 = p.deepCopy(
{ address }, { copy(address = it) },
{ street }, { copy(street = it) },
{ copy(name = "myNewStreet") }
)
22. inline fun <C1, C2, C3> C1.deepCopy(
field1: C1.() -> C2, f1: C1.(C2) -> C1,
field2: C2.() -> C3, f2: C2.(C3) -> C2,
f: C3.(C2) -> C3): C1 {
val value2 = field1()
val value3 = value2.field2()
val newValue3 = value3.f(value2)
val newValue2 = value2.f2(newValue3)
return f1(newValue2)
}1
inline fun <C1, C2, C3, C4> C1.deepCopy(
field1: C1.() -> C2, f1: C1.(C2) -> C1,
field2: C2.() -> C3, f2: C2.(C3) -> C2,
field3: C3.() -> C4, f3: C3.(C4) -> C3,
f: C4.(C3) -> C4): C1 {
val value2 = field1()
val value3 = value2.field2()
val value4 = value3.field3()
val newValue4 = value4.f(value3)
val newValue3 = value3.f3(newValue4)
val newValue2 = value2.f2(newValue3)
return f1(newValue2)
}
inline fun <C1, C2> C1.deepCopy(
field1: C1.() -> C2, f1: C1.(C2) -> C1,
f: C2.(C1) -> C2): C1 {
val value2 = field1()
val newValue2 = value2.f(this)
return f1(newValue2)
}
23. Nested copy Local variables
Arrow - Lenses
val p1 = p.copy(
address = p.address.copy(
street = p.address.street.copy(
name = "myNewStreet"
)1
)2
)3
val address = p.address
val street = address.street
val newStreet = street.copy(name = "myNewStreet")
val newAddress = address.copy(street = newStreet)
val p1 = p.copy(address = newAddress)
val personStreetName: Lens<Person, String> =
personAddress compose
addressStreet compose streetName
val p1 = personStreetName.modify(p) {
"newStreetName"
}_
val p1 = p.deepCopy(
{ address }, { copy(address = it) },
{ street }, { copy(street = it) },
{ copy(name = "myNewStreet") }
)__
25. Address address = p.getAddress();
Street street = address.getStreet();
Street newStreet = street.copy("myNewStreet", street.getNumber());
Address newAddress = address.copy(newStreet, address.getCity());
Person p2 = p.copy(p.getId(), p.getName(), p.getAge(), newAddress);
val p1 = p.deepCopy(
{ address }, { copy(address = it) },
{ street }, { copy(street = it) },
{ copy(name = "myNewStreet") }
)__
26. val p1 = p.deepCopy(
{ address }, { copy(address = it) },
{ street }, { copy(street = it) },
{ copy(name = "myNewStreet") }
)__
inline method
Nested copy Local variables
Arrow - Lenses
val p1 = p.copy(
address = p.address.copy(
street = p.address.street.copy(
name = "myNewStreet"
)1
)2
)3
val address = p.address
val street = address.street
val newStreet = street.copy(name = "myNewStreet")
val newAddress = address.copy(street = newStreet)
val p1 = p.copy(address = newAddress)
val personStreetName: Lens<Person, String> =
personAddress compose
addressStreet compose streetName
val p1 = personStreetName.modify(p) {
"newStreetName"
}_
27. val p = Person(1, "name", 10)
val (_, name, age) = p
println("$name $age")
28. data class Pair<out A, out B>(
val first: A,
val second: B
) : Serializable {
override fun toString(): String = "($first, $second)"
}_
29. 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)
32. for (Person person : people) {
City city = person.getAddress().getCity();
if (city.getRegion().equals("Tuscany")) {
cities.add(city.getName() + " (" + city.getCode() + ")");
}1
}2
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
Set<String> cities = new TreeSet<>();
33. 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)
39. private const val MY_PARAM = "my_param"
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val myStringParam = param
//...
}5
val param
get() = arguments!!.getString(MY_PARAM)
companion object {
fun newInstance(param: String): MyFragment {
return MyFragment().apply {
arguments = Bundle().apply {
putString(MY_PARAM, param)
}6
}7
}8
}9
}0
40. public class MyFragment extends Fragment {
public static final Companion Companion = new Companion();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String myStringParam = getParam();
//...
}9
public String getParam() {
return getArguments().getString("my_param");
}8
public static final class Companion {
public final MyFragment newInstance(String param) {
MyFragment fragment = new MyFragment();
Bundle bundle = new Bundle();
bundle.putString("my_param", param);
fragment.setArguments(bundle);
return fragment;
}2
}3
}4
private const val MY_PARAM = "my_param"
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val myStringParam = param
//...
}5
val param
get() = arguments!!.getString(MY_PARAM)
companion object {
fun newInstance(param: String): MyFragment {
return MyFragment().apply {
arguments = Bundle().apply {
putString(MY_PARAM, param)
}6
}7
}8
}9
}0
41. public class MyFragment extends Fragment {
public static final Companion Companion = new Companion();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String myStringParam = getParam();
//...
}9
public String getParam() {
return getArguments().getString("my_param");
}8
public static final class Companion {
public final MyFragment newInstance(String param) {
MyFragment fragment = new MyFragment();
Bundle bundle = new Bundle();
bundle.putString("my_param", param);
fragment.setArguments(bundle);
return fragment;
}2
}3
}4
private const val MY_PARAM = "my_param"
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val myStringParam = param
//...
}5
val param
get() = arguments!!.getString(MY_PARAM)
companion object {
fun newInstance(param: String): MyFragment {
return MyFragment().apply {
arguments = Bundle().apply {
putString(MY_PARAM, param)
}6
}7
}8
}9
}0
42. private const val MY_PARAM = "my_param"
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val myStringParam = param
//...
}5
val param
get() = arguments!!.getString(MY_PARAM)
companion object {
@JvmStatic
fun newInstance(param: String): MyFragment {
return MyFragment().apply {
arguments = Bundle().apply {
putString(MY_PARAM, param)
}6
}7
}8
}9
}0
public class MyFragment extends Fragment {
public static final Companion Companion = new Companion();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String myStringParam = getParam();
//...
}9
public String getParam() {
return getArguments().getString("my_param");
}8
public static MyFragment newInstance(String param) {
return Companion.newInstance(param);
}1
public static final class Companion {
public final MyFragment newInstance(String param) {
MyFragment fragment = new MyFragment();
Bundle bundle = new Bundle();
bundle.putString("my_param", param);
fragment.setArguments(bundle);
return fragment;
}2
}3
}4
43. private const val MY_PARAM = "my_param"
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val myStringParam = param
//...
}5
val param
get() = arguments!!.getString(MY_PARAM)
companion object {
fun newInstance(param: String): MyFragment {
return MyFragment().apply {
arguments = Bundle().apply {
putString(MY_PARAM, param)
}6
}7
}8
}9
}0
44. private const val MY_PARAM = "my_param"
open class FragmentCreator<T>(
val factory: () -> Fragment
) {
}1
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val myStringParam = param
//...
}5
val param
get() = arguments!!.getString(MY_PARAM)
companion object : FragmentCreator<String>(::MyFragment) {2
fun newInstance(param: String): MyFragment {
return MyFragment().apply {
arguments = Bundle().apply {
putString(MY_PARAM, param)
}6
}7
}8
}9
}0
45. private const val MY_PARAM = "my_param"
open class FragmentCreator<T>(
val factory: () -> Fragment
) {
fun newInstance(param: T) : Fragment {
return factory().apply {
arguments = bundleOf(MY_PARAM to param)
}7
}8
}1
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val myStringParam = param
//...
}5
val param
get() = arguments!!.getString(MY_PARAM)
companion object : FragmentCreator<String>(::MyFragment)
}0
46. private const val MY_PARAM = "my_param"
open class FragmentCreator<T>(
val factory: () -> Fragment
) {
fun newInstance(param: T) : Fragment {
return factory().apply {
arguments = bundleOf(MY_PARAM to param)
}7
}8
}1
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val myStringParam = param
//...
}5
val param
get() = arguments!!.getString(MY_PARAM)
companion object : FragmentCreator<String>(::MyFragment)
}0
47. private const val MY_PARAM = "my_param"
open class FragmentCreator<T>(
val factory: () -> Fragment
) {
fun newInstance(param: T) : Fragment {
return factory().apply {
arguments = bundleOf(MY_PARAM to param)
}7
}8
val Fragment.param
get() = arguments!!.get(MY_PARAM) as T
}1
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val myStringParam = param
//...
}5
companion object : FragmentCreator<String>(::MyFragment)
}0
48. private const val MY_PARAM = "my_param"
open class FragmentCreator<T>(
val factory: () -> Fragment
) {
fun newInstance(param: T) : Fragment {
return factory().apply {
arguments = bundleOf(MY_PARAM to param)
}7
}8
val Fragment.param
get() = arguments!!.get(MY_PARAM) as T
}1
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val myStringParam = param
//...
}5
companion object : FragmentCreator<String>(::MyFragment)
}0
49. private const val MY_PARAM = "my_param"
open class FragmentCreator<T>(
val factory: () -> Fragment
) {
fun newInstance(param: T) : Fragment {
return factory().apply {
arguments = bundleOf(MY_PARAM to param)
}7
}8
val Fragment.param
get() = arguments!!.get(MY_PARAM) as T
fun param(fragment: Fragment): T {
return fragment.arguments!!.get(MY_PARAM) as T
}
}1
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val myStringParam = param
//...
}5
companion object : FragmentCreator<String>(::MyFragment)
}0
50. class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val myStringParam = param
//...
}5
companion object : FragmentCreator<String>(::MyFragment)
}0
val fragment = MyFragment.newInstance("ABC")
51. private const val MY_PARAM = "my_param"
open class FragmentCreator<T>(
val factory: () -> Fragment
) {
fun newInstance(param: T) : Fragment {
return factory().apply {
arguments = bundleOf(MY_PARAM to param)
}7
}8
val Fragment.param: T
get() = arguments!!.get(MY_PARAM) as T
fun param(fragment: Fragment): T {
return fragment.arguments!!.get(MY_PARAM) as T
}
}1
52. fun bundleOf(vararg pairs: Pair<String, Any?>) = Bundle(pairs.size).apply {
for ((key, value) in pairs) {
when (value) {
null -> putString(key, null) // Any nullable type will suffice.
// Scalars
is Boolean -> putBoolean(key, value)
is Byte -> putByte(key, value)
is Char -> putChar(key, value)
is Double -> putDouble(key, value)
is Float -> putFloat(key, value)
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Short -> putShort(key, value)
// References
is Bundle -> putBundle(key, value)
is CharSequence -> putCharSequence(key, value)
is Parcelable -> putParcelable(key, value)
// Scalar arrays
is BooleanArray -> putBooleanArray(key, value)
is ByteArray -> putByteArray(key, value)
is CharArray -> putCharArray(key, value)
is DoubleArray -> putDoubleArray(key, value)
is FloatArray -> putFloatArray(key, value)
is IntArray -> putIntArray(key, value)
is LongArray -> putLongArray(key, value)
is ShortArray -> putShortArray(key, value)
// Reference arrays
is Array<*> -> {
val componentType = value::class.java.componentType
@Suppress("UNCHECKED_CAST") // Checked by reflection.
when {
Parcelable::class.java.isAssignableFrom(componentType) -> {
putParcelableArray(key, value as Array<Parcelable>)
}
String::class.java.isAssignableFrom(componentType) -> {
putStringArray(key, value as Array<String>)
}
CharSequence::class.java.isAssignableFrom(componentType) -> {
putCharSequenceArray(key, value as Array<CharSequence>)
}
Serializable::class.java.isAssignableFrom(componentType) -> {
putSerializable(key, value)
}
else -> {
val valueType = componentType.canonicalName
throw IllegalArgumentException(
"Illegal value array type $valueType for key "$key"")
}5
}6
}7
// Last resort. Also we must check this after Array<*> as all arrays are serializable.
is Serializable -> putSerializable(key, value)
else -> {
if (Build.VERSION.SDK_INT >= 18 && value is Binder) {
putBinder(key, value)
} else if (Build.VERSION.SDK_INT >= 21 && value is Size) {
putSize(key, value)
} else if (Build.VERSION.SDK_INT >= 21 && value is SizeF) {
putSizeF(key, value)
} else {
val valueType = value.javaClass.canonicalName
throw IllegalArgumentException("Illegal value type $valueType for key "$key"")
}1
}2
}3
}4
}5
53. fun bundleOf(vararg pairs: Pair<String, Any?>) = Bundle(pairs.size).apply {
for ((key, value) in pairs) {
when (value) {
null -> putString(key, null) // Any nullable type will suffice.
// Scalars
is Boolean -> putBoolean(key, value)
is Byte -> putByte(key, value)
is Char -> putChar(key, value)
is Double -> putDouble(key, value)
is Float -> putFloat(key, value)
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Short -> putShort(key, value)
// References
is Bundle -> putBundle(key, value)
is CharSequence -> putCharSequence(key, value)
is Parcelable -> putParcelable(key, value)
// Scalar arrays
is BooleanArray -> putBooleanArray(key, value)
is ByteArray -> putByteArray(key, value)
is CharArray -> putCharArray(key, value)
is DoubleArray -> putDoubleArray(key, value)
is FloatArray -> putFloatArray(key, value)
is IntArray -> putIntArray(key, value)
is LongArray -> putLongArray(key, value)
54. }
Serializable::class.java.isAssignableFrom(componentType) -> {
putSerializable(key, value)
}
else -> {
val valueType = componentType.canonicalName
throw IllegalArgumentException(
"Illegal value array type $valueType for key "$key"")
}5
}6
}7
// Last resort. Also we must check this after Array<*> as all arrays are serializable.
is Serializable -> putSerializable(key, value)
else -> {
if (Build.VERSION.SDK_INT >= 18 && value is Binder) {
putBinder(key, value)
} else if (Build.VERSION.SDK_INT >= 21 && value is Size) {
putSize(key, value)
} else if (Build.VERSION.SDK_INT >= 21 && value is SizeF) {
putSizeF(key, value)
} else {
val valueType = value.javaClass.canonicalName
throw IllegalArgumentException("Illegal value type $valueType for key "$key"")
}1
}2
}3
}4
}5
55. private const val MY_PARAM = "my_param"
open class FragmentCreator<T>(
val factory: () -> Fragment
) {
fun newInstance(param: T) : Fragment {
return factory().apply {
arguments = bundleOf(MY_PARAM to param)
}7
}8
val Fragment.param: T
get() = arguments!!.get(MY_PARAM) as T
fun param(fragment: Fragment): T {
return fragment.arguments!!.get(MY_PARAM) as T
}2
}1
56. private const val MY_PARAM = "my_param"
open class FragmentCreator<T>(
val factory: () -> Fragment
) {
val Fragment.param: T
get() = arguments!!.get(MY_PARAM) as T
fun param(fragment: Fragment): T {
return fragment.arguments!!.get(MY_PARAM) as T
}2
}1
fun <T : Parcelable> FragmentCreator<T>.newInstance(param: T): Fragment {
return factory().apply {4
arguments = Bundle().apply {5
putParcelable(MY_PARAM, param)
}6
}7
}8
57. private const val MY_PARAM = "my_param"
open class FragmentCreator<T>(
val factory: () -> Fragment
) {
val Fragment.param: T
get() = arguments!!.get(MY_PARAM) as T
fun param(fragment: Fragment): T {
return fragment.arguments!!.get(MY_PARAM) as T
}2
}1
fun <T : Parcelable> FragmentCreator<T>.newInstance(param: T): Fragment {
return factory().apply {4
arguments = Bundle().apply {5
putParcelable(MY_PARAM, param)
}6
}7
}8
fun FragmentCreator<String>.newInstance(param: String): Fragment {
return factory().apply {
arguments = Bundle().apply {
putString(MY_PARAM, param)
}
}
}
61. /**
* 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) {
//...
}
84. class TokenHolder(private val prefs: SharedPreferences) {
var token
get() = prefs.getString("token", "")
set(value) = prefs.edit().putString("token", value).apply()
}1
85. class TokenHolder(private val prefs: SharedPreferences) {
var token
get() = prefs.getString("token", "")
set(value) = prefs.edit().putString("token", value).apply()
}1
public class TokenHolder {
private final SharedPreferences prefs;
public TokenHolder(SharedPreferences prefs) {
this.prefs = prefs;
}1
public final String getToken() {
return this.prefs.getString("token", "");
}2
public final void setToken(String value) {
this.prefs.edit().putString("token", value).apply();
}3
}4
86. class TokenHolder(private val prefs: SharedPreferences) {
var token
get() = prefs.getString("token", "")
set(value) = prefs.edit().putString("token", value).apply()
}1
88. 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
class TokenHolder(prefs: SharedPreferences) {
var token by prefs.string()
}1
89. class TokenHolder(prefs: SharedPreferences) {
var token by prefs.string()
}1
public class TokenHolder {
static final KProperty delegatedProperty = //..
private final ReadWriteProperty token$delegate;
public TokenHolder(SharedPreferences prefs) {
this.token$delegate = PrefsKt.string(prefs, null, null);
}1
public final String getToken() {
return (String)token$delegate.getValue(this, delegatedProperty);
}2
public final void setToken(String var1) {
token$delegate.setValue(this, delegatedProperty, var1);
}3
}4
90. class TokenHolder(prefs: SharedPreferences) {
var token by prefs.string()
}1
tokenHolder.token += "ABC"
prefs.edit().putString(
"token",
prefs.getString("token", "") + "ABC"
).apply()
91. Wrapping up
Alt+Shift+Cmd+K is just the beginning
functional code is natural in Kotlin
code can be simplified using
companion objects, data classes,
coroutines and delegates
92. Links Demo Project
github.com/fabioCollini/ArchitectureComponentsDemo
Libraries
AutoValue github.com/google/auto/blob/master/value/userguide/index.md
Lightweight Stream github.com/aNNiMON/Lightweight-Stream-API
Arrow arrow-kt.io/
Guides and posts
Guide to kotlinx.coroutines by example
github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md
Kotlin coroutines, a deeper look
medium.com/@elizarov/kotlin-coroutines-a-deeper-look-180536305c3f
Kotlin delegates in Android development part 1
hackernoon.com/kotlin-delegates-in-android-development-part-1-50346cf4aed7
Kotlin delegates in Android development part 2
proandroiddev.com/kotlin-delegates-in-android-development-part-2-2c15c11ff438