From Java to Kotlin beyond
Alt+Shift+Cmd+K
Fabio Collini
@fabioCollini
linkedin.com/in/fabiocollini
github.com/fabioCollini
medium.com/@fabioCollini
codingjam.it
Android programmazione avanzata
Android Developers Italia
Ego slide
Kotlin can be configured in one click
A Java class can be converted easily
ok, now what?
CLASSES
DATA COLLE
CTIONS
ASYNC
CODE
DELE
GATES
Agenda
COMPANION
OBJECTS
DATA
CLASSES
@Parcelize
data class Person(
val id: Long,
val name: String,
val age: Int
)_: Parcelable
@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
};
}_
@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
@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
@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
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");
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
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
val p1 = p.copy(
address = p.address.copy(
street = p.address.street.copy(
name = "myNewStreet"
)1
)2
)3
Nested copy
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
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
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"
}_
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"
}_
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") }
)
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)
}
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") }
)__
Object $receiver$iv = p;
Object value2$iv = $receiver$iv.getAddress();
Object value3$iv = value2$iv.getStreet();
Object newValue3$iv = Street.copy$default(value3$iv, "myNewStreet", (String) null, 2, (Object) null);
Object newValue2$iv = Address.copy$default(value2$iv, newValue3$iv, (City) null, 2, (Object) null);
Person p2 = Person.copy$default($receiver$iv, 0L, (String) null, 0, newValue2$iv, 7, (Object) null);
val p1 = p.deepCopy(
{ address }, { copy(address = it) },
{ street }, { copy(street = it) },
{ copy(name = "myNewStreet") }
)__
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") }
)__
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"
}_
val p = Person(1, "name", 10)
val (_, name, age) = p
println("$name $age")
data class Pair<out A, out B>(
val first: A,
val second: B
) : Serializable {
override fun toString(): String = "($first, $second)"
}_
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)
fun myMethod(): Pair<Int, Int> {
//...
return 10 to 20
}
val (a, b) = myMethod()
COLLE
CTIONS
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<>();
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)
String s = Stream.of(people)
.map(it -> it.getAddress().getCity())
.filter(it -> it.getRegion().equals("Tuscany"))
.distinct()
.sortBy(City::getName)
.map(it -> it.getName() + " (" + it.getCode() + ")")
.collect(Collectors.joining(", "));
System.out.println(s);
val people = listOf(...)
val s = people
.asSequence()
.map { it.address.city }
.filter { it.region == "Tuscany" }
.map { "${it.name} (${it.code})" }
.first()
println(s)
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 }
COMPANION
OBJECTS
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
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
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
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
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
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
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
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
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
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
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
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")
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
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
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)
}
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
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
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
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)
}
}
}
ASYNC
CODE
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myTextView = findViewById<TextView>(R.id.text)
myTextView.text = "Loading..."
Thread.sleep(2000)
myTextView.text = "Loading something else..."
Thread.sleep(2000)
myTextView.text = "Done"
}_
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myTextView = findViewById<TextView>(R.id.text)
myTextView.text = "Loading..."
Thread.sleep(2000)
myTextView.text = "Loading something else..."
Thread.sleep(2000)
myTextView.text = "Done"
}_
/**
* 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) {
//...
}
suspend
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myTextView = findViewById<TextView>(R.id.text)
async(UI) {
myTextView.text = "Loading..."
delay(2000)
myTextView.text = "Loading something else..."
delay(2000)
myTextView.text = "Done"
}async
}end
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myTextView = findViewById<TextView>(R.id.text)
async(UI) {
myTextView.text = "Loading..."
delay(2000)
myTextView.text = "Loading something else..."
delay(2000)
myTextView.text = "Done"
}async
}end
interface MyService {
@GET("login")
fun login(): Deferred<String>
@GET("data")
fun loadData(@Query(“token") token: String): Deferred<Data>
}A
interface MyService {
@GET("login")
fun login(): Deferred<String>
@GET("data")
fun loadData(@Query(“token") token: String): Deferred<Data>
}A
async(UI) {
try {
val token = service.login().await()
val data = service.loadData(token).await()
showInUi(data)
} catch (e: Exception) {
showError(e)
}__
}___
service.login()
.flatMap { service.loadData(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ data -> showInUi(data) },
{ showError(it) }
)end
RxJavaCoroutines
async(UI) {
try {
val token = service.login().await()
val data = service.loadData(token).await()
showInUi(data)
} catch (e: Exception) {
showError(e)
}__
}___
RxJava
Coroutines
OR
RxJava
Coroutines
AND
RxJava Coroutines->
RxJavaCoroutines ->
rxCompletable, rxMaybe, rxSingle, rxObservable, rxFlowable
CompletableSource.await, MaybeSource.await, MaybeSource.awaitOrDefault,
MaybeSource.openSubscription, SingleSource.await,
ObservableSource.awaitFirst, ObservableSource.awaitFirstOrDefault,
ObservableSource.awaitFirstOrElse, ObservableSource.awaitFirstOrNull,
ObservableSource.awaitLast, ObservableSource.awaitSingle,
ObservableSource.openSubscription, ObservableSource.iterator
github.com/Kotlin/kotlinx.coroutines/tree/master/reactive/kotlinx-coroutines-rx2
async(UI) {
try {
val token = service.login().await()
val data = service.loadData(token).await()
showInUi(data)1
} catch (e: Exception) {
showError(e)
}__
}___
async(UI) {
try {
val token = service.login().await()
val data = service.loadData(token).await()
val otherData = service.loadOtherData(token).await()
showInUi(data, otherData)1
} catch (e: Exception) {
showError(e)
}__
}___
async(UI) {
try {
val token = service.login().await()
val data = service.loadData(token)
val otherData = service.loadOtherData(token)
showInUi(data.await(), otherData.await())1
} catch (e: Exception) {
showError(e)
}__
}___
async(UI) {
try {
val token = service.login().await()
updateProgress()
val data = service.loadData(token)
val otherData = service.loadOtherData(token)
showInUi(data.await(), otherData.await())
} catch (e: Exception) {
showError(e)
}__
}___
async(UI) {
try {
withTimeout(10, SECONDS) {
val token = service.login().await()
updateProgress()
val data = service.loadData(token)
val otherData = service.loadOtherData(token)
showInUi(data.await(), otherData.await())
}B
} catch (e: Exception) {
showError(e)
}__
}___
async(UI) {
try {
withTimeout(10, SECONDS) {
val token = retry(3) {
service.login().await()
}T
updateProgress()
val data = service.loadData(token)
val otherData = service.loadOtherData(token)
showInUi(data.await(), otherData.await())
}B
} catch (e: Exception) {
showError(e)
}__
}___
async(UI) {
try {
withTimeout(10, SECONDS) {
val token = retry(3) {
service.login().await()
}T
updateProgress()
val data = service.loadData(token)
val otherData = service.loadOtherData(token)
showInUi(data.await(), otherData.await())
}B
} catch (e: Exception) {
showError(e)
}__
}___
suspend fun <T> retry(times: Int, block: suspend () -> T): T {
repeat(times - 1) {
try {
return block()
} catch (ignored: Exception) {
}
}
return block()
}
My 2 cents
Suspending methods are easier to
use than RxJava Singles
Observables/Flowables are the best
abstraction for a stream of data
DELE
GATES
class TokenHolder {0
var token = ""
}1
class TokenHolder {0
var token = ""
}1
public class TokenHolder {
private String token = "";
public String getToken() {
return this.token;
}1
public void setToken(String token) {
this.token = token;
}2
}3
class TokenHolder {0
var token = ""
}1
class TokenHolder(private val prefs: SharedPreferences) {0
var token = ""
}1
class TokenHolder(private val prefs: SharedPreferences) {
var token
get() = prefs.getString("token", "")
set(value) = prefs.edit().putString("token", value).apply()
}1
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
class TokenHolder(private val prefs: SharedPreferences) {
var token
get() = prefs.getString("token", "")
set(value) = prefs.edit().putString("token", value).apply()
}1
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
class TokenHolder(prefs: SharedPreferences) {
var token by prefs.string()
}1
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
class TokenHolder(prefs: SharedPreferences) {
var token by prefs.string()
}1
tokenHolder.token += "ABC"
prefs.edit().putString(
"token",
prefs.getString("token", "") + "ABC"
).apply()
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
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
THANKS
FOR YOUR
ATTENTION
QUESTIONS?
Android Developers Italia
androiddevs.it

From java to kotlin beyond alt+shift+cmd+k - Droidcon italy

  • 1.
    From Java toKotlin beyond Alt+Shift+Cmd+K Fabio Collini
  • 2.
  • 3.
    Kotlin can beconfigured in one click
  • 4.
    A Java classcan be converted easily
  • 5.
  • 6.
  • 7.
  • 8.
    @Parcelize data class Person( valid: Long, val name: String, val age: Int )_: Parcelable
  • 9.
    @Parcelize data class Person( valid: 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 classPerson 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 classPerson 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 classPerson 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( valname: 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 Localvariables 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 Nestedcopy 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 Localvariables 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 Localvariables 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") } )__
  • 24.
    Object $receiver$iv =p; Object value2$iv = $receiver$iv.getAddress(); Object value3$iv = value2$iv.getStreet(); Object newValue3$iv = Street.copy$default(value3$iv, "myNewStreet", (String) null, 2, (Object) null); Object newValue2$iv = Address.copy$default(value2$iv, newValue3$iv, (City) null, 2, (Object) null); Person p2 = Person.copy$default($receiver$iv, 0L, (String) null, 0, newValue2$iv, 7, (Object) null); 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<outA, out B>( val first: A, val second: B ) : Serializable { override fun toString(): String = "($first, $second)" }_
  • 29.
    data class Pair<outA, 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)
  • 30.
    fun myMethod(): Pair<Int,Int> { //... return 10 to 20 } val (a, b) = myMethod()
  • 31.
  • 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)
  • 35.
    String s =Stream.of(people) .map(it -> it.getAddress().getCity()) .filter(it -> it.getRegion().equals("Tuscany")) .distinct() .sortBy(City::getName) .map(it -> it.getName() + " (" + it.getCode() + ")") .collect(Collectors.joining(", ")); System.out.println(s);
  • 36.
    val people =listOf(...) val s = people .asSequence() .map { it.address.city } .filter { it.region == "Tuscany" } .map { "${it.name} (${it.code})" } .first() println(s)
  • 37.
    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 }
  • 38.
  • 39.
    private const valMY_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 MyFragmentextends 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 MyFragmentextends 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 valMY_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 valMY_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 valMY_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 valMY_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 valMY_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 valMY_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 valMY_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 valMY_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 valMY_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 valMY_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 valMY_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 valMY_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) } } }
  • 58.
  • 59.
    override fun onCreate(savedInstanceState:Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.text) myTextView.text = "Loading..." Thread.sleep(2000) myTextView.text = "Loading something else..." Thread.sleep(2000) myTextView.text = "Done" }_
  • 60.
    override fun onCreate(savedInstanceState:Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.text) myTextView.text = "Loading..." Thread.sleep(2000) myTextView.text = "Loading something else..." Thread.sleep(2000) myTextView.text = "Done" }_
  • 61.
    /** * Delays coroutinefor a given time without blocking * a thread and resumes it after a specified time. * ... */ suspend fun delay(time: Long, unit: TimeUnit = MILLISECONDS) { //... }
  • 62.
  • 63.
    override fun onCreate(savedInstanceState:Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.text) async(UI) { myTextView.text = "Loading..." delay(2000) myTextView.text = "Loading something else..." delay(2000) myTextView.text = "Done" }async }end
  • 64.
    override fun onCreate(savedInstanceState:Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.text) async(UI) { myTextView.text = "Loading..." delay(2000) myTextView.text = "Loading something else..." delay(2000) myTextView.text = "Done" }async }end
  • 65.
    interface MyService { @GET("login") funlogin(): Deferred<String> @GET("data") fun loadData(@Query(“token") token: String): Deferred<Data> }A
  • 66.
    interface MyService { @GET("login") funlogin(): Deferred<String> @GET("data") fun loadData(@Query(“token") token: String): Deferred<Data> }A async(UI) { try { val token = service.login().await() val data = service.loadData(token).await() showInUi(data) } catch (e: Exception) { showError(e) }__ }___
  • 67.
    service.login() .flatMap { service.loadData(it)} .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { data -> showInUi(data) }, { showError(it) } )end RxJavaCoroutines async(UI) { try { val token = service.login().await() val data = service.loadData(token).await() showInUi(data) } catch (e: Exception) { showError(e) }__ }___
  • 68.
  • 69.
  • 70.
    RxJava Coroutines-> RxJavaCoroutines -> rxCompletable,rxMaybe, rxSingle, rxObservable, rxFlowable CompletableSource.await, MaybeSource.await, MaybeSource.awaitOrDefault, MaybeSource.openSubscription, SingleSource.await, ObservableSource.awaitFirst, ObservableSource.awaitFirstOrDefault, ObservableSource.awaitFirstOrElse, ObservableSource.awaitFirstOrNull, ObservableSource.awaitLast, ObservableSource.awaitSingle, ObservableSource.openSubscription, ObservableSource.iterator github.com/Kotlin/kotlinx.coroutines/tree/master/reactive/kotlinx-coroutines-rx2
  • 71.
    async(UI) { try { valtoken = service.login().await() val data = service.loadData(token).await() showInUi(data)1 } catch (e: Exception) { showError(e) }__ }___
  • 72.
    async(UI) { try { valtoken = service.login().await() val data = service.loadData(token).await() val otherData = service.loadOtherData(token).await() showInUi(data, otherData)1 } catch (e: Exception) { showError(e) }__ }___
  • 73.
    async(UI) { try { valtoken = service.login().await() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await())1 } catch (e: Exception) { showError(e) }__ }___
  • 74.
    async(UI) { try { valtoken = service.login().await() updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) } catch (e: Exception) { showError(e) }__ }___
  • 75.
    async(UI) { try { withTimeout(10,SECONDS) { val token = service.login().await() updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) }B } catch (e: Exception) { showError(e) }__ }___
  • 76.
    async(UI) { try { withTimeout(10,SECONDS) { val token = retry(3) { service.login().await() }T updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) }B } catch (e: Exception) { showError(e) }__ }___
  • 77.
    async(UI) { try { withTimeout(10,SECONDS) { val token = retry(3) { service.login().await() }T updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) }B } catch (e: Exception) { showError(e) }__ }___ suspend fun <T> retry(times: Int, block: suspend () -> T): T { repeat(times - 1) { try { return block() } catch (ignored: Exception) { } } return block() }
  • 78.
    My 2 cents Suspendingmethods are easier to use than RxJava Singles Observables/Flowables are the best abstraction for a stream of data
  • 79.
  • 80.
  • 81.
    class TokenHolder {0 vartoken = "" }1 public class TokenHolder { private String token = ""; public String getToken() { return this.token; }1 public void setToken(String token) { this.token = token; }2 }3
  • 82.
  • 83.
    class TokenHolder(private valprefs: SharedPreferences) {0 var token = "" }1
  • 84.
    class TokenHolder(private valprefs: SharedPreferences) { var token get() = prefs.getString("token", "") set(value) = prefs.edit().putString("token", value).apply() }1
  • 85.
    class TokenHolder(private valprefs: 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 valprefs: SharedPreferences) { var token get() = prefs.getString("token", "") set(value) = prefs.edit().putString("token", value).apply() }1
  • 87.
    class TokenHolder(prefs: SharedPreferences){ var token by prefs.string() }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 isjust 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 AutoValuegithub.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
  • 93.
  • 94.