@marcinmoskala
@marcinmoskala
@marcinmoskala
@marcinmoskala
@marcinmoskala
https://www.pinterest.de/explore/alter-computer/
@marcinmoskala
interface Player {
fun playGame()
}
class RpgGamePlayer(val enemy: String) : Player {
override fun playGame() {
println("Killing $enemy")
}
}
class WitcherPlayer(enemy: String) : Player {
val player = RpgGamePlayer(enemy)
override fun playGame() {
player.playGame()
}
}
RpgGamePlayer("monsters").playGame() // Prints: Killing monsters
WitcherPlayer("monsters").playGame() // Prints: Killing monsters
WitcherPlayer class is delegating methods defined in Player interface to an instance of type RpgGamePlayer
@marcinmoskala
interface Player {
fun playGame()
}
class RpgGamePlayer(val enemy: String) : Player {
override fun playGame() {
println("Killing $enemy")
}
}
class WitcherPlayer(enemy: String) : RpgGamePlayer(enemy)
RpgGamePlayer("monsters").playGame() // Prints: Killing monsters
WitcherPlayer("monsters").playGame() // Prints: Killing monsters
@marcinmoskala
@marcinmoskala
public class CountingHashSet extends HashSet {
private int addCount = 0;
@Override
public boolean add(Object o) {
addCount += 1;
return super.add(o);
}
@Override
public boolean addAll(Collection collection) {
addCount += collection.size();
return super.addAll(collection);
}
public int getAddCount() {
return addCount;
}
}
// Usage
CountingHashMap s = new CountingHashMap();
s.addAll(Arrays.asList(new String[]{"A", "B", "C"}));
System.out.println(s.getAddCount()); // Prints: 6
@marcinmoskala
When classes is final, private or not designed for inheritance.
When there is no "is-a" relationship
When we need only some functionalities from class
@marcinmoskala
interface Player {
fun playGame()
}
class RpgGamePlayer(val enemy: String) : Player {
override fun playGame() {
println("Killing $enemy")
}
}
class WitcherPlayer(enemy: String) : Player by RpgGamePlayer(enemy)
RpgGamePlayer("monsters").playGame() // Prints: Killing monsters
WitcherPlayer("monsters").playGame() // Prints: Killing monsters
Using by keyword, we are informing compiler to delegate all methods defined in Player interface from WitcherPlayer to RpgGamePlayer
@marcinmoskala
class WitcherPlayer() : Player by RpgGamePlayer("monsters")
class WitcherPlayer(enemy: String) : Player by RpgGamePlayer(enemy)
class WitcherPlayer(player: Player) : Player by player
class WitcherPlayer(val player: Player = RpgGamePlayer("monsters")) : Player by player
val player = RpgGamePlayer(10)
class WitcherPlayer() : Player by player
@marcinmoskala
InputStream fis = new FileInputStream("/someFile.gz");
InputStream bis = new BufferedInputStream(fis);
InputStream gis = new ZipInputStream(bis);
ObjectInputStream ois = new ObjectInputStream(gis);
SomeObject someObject = (SomeObject) ois.readObject();
class PlayHard(player: Player) : Player by player {
override fun playGame() {
println("Arrgggh! ")
player.playGame()
}
}
// Usage
val player = WitcherPlayer("monsters")
player.playGame() // Prints: Killing monsters
val hardcore = PlayHard(player)
hardcore.playGame() // Prints: Arrgggh! Killing monsters
@marcinmoskala
class User(val name: String, val surname: String)
var user: User by UserDelegate()
// Usage
println(user.name)
user = User(“Marcin”,”Moskala”)
Property user is delegated to object of type UserDelegate.
@marcinmoskala
@marcinmoskala
public class MainActivity extends Activity {
@BindView(R.id.title) TextView title;
@BindView(R.id.subtitle) TextView subtitle;
@BindView(R.id.footer) TextView footer;
// ...
}
public class MainPresenter implements Presenter {
@Inject NetworkApi networkApi;
@Inject UserService userService;
// ...
}
@Entity
@Table(name = "EMPLOYEE")
public class Employee {
@Id @GeneratedValue
@Column(name = "id")
private int id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "salary")
private int salary;
}
private UserRepository userRepo;
public UserRepository getUserRepo() {
if(userRepo == null) {
userRepo = new UserRepository();
}
return userRepo;
}
private List<String> observableList;
public void setObservableList(List<String> observableList) {
this.observableList = observableList;
onObservableListChanged()
}
@marcinmoskala
Field
Property
// Java
public String description;
// Kotlin
var description: String? = null
@marcinmoskala
Property
Property
// Java
private String description;
String getDescription() {
return description;
}
void setDescription(String description) {
this.description = description;
}
// Kotlin
var description: String? = null
@marcinmoskala
Property
Property
// Java
private String description;
String getDescription() {
return Strings.trim(description);
}
void setDescription(String description) {
if(Strings.isNotNullOrEmpty(description))
this.description = description;
}
// Kotlin
var description: String? = null
get() = field?.trim()
set(value) {
if (value != null && value.isNotEmpty())
field = value
}
@marcinmoskala
Property
Property
// Java
String getDescription() {
return "Super presentation";
}
// Kotlin
val description: String? = null
get() = "Super presentation"
@marcinmoskala
class User(val name: String, val surname: String)
var user: User by UserDelegate()
// Usage
println(user.name)
user = User(“Marcin”,”Moskala”)
Property user is delegated to object of type UserDelegate.
var p$delegate = UserDelegate()
var user: User
get() = p$delegate.getValue(this, this::user)
set(value) {
p$delegate.setValue(this, this::user, value)
}
@marcinmoskala
data class User(val name: String, val surname: String)
class UserDelegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): User
= User("Marcin", "Moskala")
operator fun setValue(thisRef: Any?, property: KProperty<*>, user: User) {
println("I am not setting new user")
}
}
fun main(args: Array<String>) {
var user: User by UserDelegate()
println(user) // Prints: User(name=Marcin, surname=Moskala)
user = User("Michal", "Michalski") // Prints: I am not setting new user
}
@marcinmoskala
val someProperty by object {
operator fun getValue(thisRef: Any?, property: KProperty<*>) = "Something"
}
println(someProperty) // Prints: Something
@marcinmoskala
var user$delegate = UserDelegate()
var user: User
get() = user$delegate.getValue(this, this::user)
set(value) {
user$delegate.setValue(this, this::user, value)
}
public interface ReadOnlyProperty<in R, out T> {
public operator fun getValue(thisRef: R, property: KProperty<*>): T
}
public interface ReadWriteProperty<in R, T> {
public operator fun getValue(thisRef: R, property: KProperty<*>): T
public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}
Specify allowed location
Specify allowed
type
@marcinmoskala
var a by SomeDelegate() // Top-level property with delegate
fun someTopLevelFun() {
var b by SomeDelegate() // Local variable (inside top-level function) with delegate
}
class SomeClass() {
var c by SomeDelegate() // Member property with delegate
fun someMethod() {
val d by SomeDelegate() // Local variable (inside method) with delegate
}
}
@marcinmoskala
var name: String by Delegates.notNull()
private class NotNullVar<T: Any>() : ReadWriteProperty<Any?, T> {
private var value: T? = null
public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw IllegalStateException(
"Property ${property.name} should be initialized before get."
)
}
public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
}
name = "Marcin"
println(name) // Prints: Marcin
name = "Maja"
println(name) // Prints: Maja
println(name) // Error:
// Property name should be
// initialized before get.
@marcinmoskala
var user: User by Delegates.notNull()
lateinit var user: User
Can be used everywhere
Can be used to all types
More efficient
Cannot be used at top level
Cannot be used to primitive (Int, Long, Double etc.)
@marcinmoskala
val userRepo by lazy { UserRepository() }
private var _userRepo: UserRepository? = null
private val userRepoLock = Any()
val userRepo: UserRepository
get() {
synchronized(userRepoLock) {
if (_userRepo == null) {
_userRepo = UserRepository()
}
return _userRepo!!
}
}
val userRepo by lazy(LazyThreadSafetyMode.NONE) { UserRepository() }
@marcinmoskala
class MainActivity : Activity() {
lateinit var questionLabelView: TextView
lateinit var answerLabelView: EditText
lateinit var confirmButtonView: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
questionLabelView = findViewById(R.id.main_question_label) as TextView
answerLabelView = findViewById(R.id.main_answer_label) as EditText
confirmButtonView = findViewById(R.id.main_button_confirm) as Button
}
}
@marcinmoskala
class MainActivity : Activity() {
var questionLabelView: TextView by lazy { findViewById(R.id.main_question_label) }
var answerLabelView: TextView by lazy { findViewById(R.id.main_answer_label) }
var confirmButtonView: Button by lazy { findViewById(R.id.main_button_confirm) }
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
}
}
@marcinmoskala
class MainActivity : Activity() {
var questionLabelView: TextView by bindView(R.id.main_question_label)
var answerLabelView: TextView by bindView(R.id.main_answer_label)
var confirmButtonView: Button by bindView(R.id.main_button_confirm)
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
}
}
fun <T: View> Activity.bindView(viewId: Int) = lazy { findViewById<T>(viewId) }
@marcinmoskala
class SettingsActivity : Activity() {
private val titleString by bindString(R.string.title)
private val blueColor by bindColor(R.color.blue)
private val doctor by extra<Doctor>(DOCTOR_KEY)
private val title by extraString(TITLE_KEY)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.settings_activity)
}
}
fun <T> Activity.bindString(@IdRes id: Int): Lazy<T> = lazy { getString(id) }
fun <T> Activity.bindColour(@IdRes id: Int): Lazy<T> = lazy { getColour(id) }
fun <T : Parcelable> Activity.extra(key: String) = lazy { intent.extras.getParcelable<T>(key) }
fun Activity.extraString(key: String) = lazy { intent.extras.getString(key) }
@marcinmoskala
class MainActivity : Activity(), MainView {
private val doctor by extra<Doctor>(DOCTOR_KEY)
private val title by extraString(TITLE_KEY)
private val titleView by bindView<TextView>(R.string.title_view)
private val presenter by lazy { MainPresenter(this, doctor) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.settings_activity)
titleView.text = title
}
}
@marcinmoskala
var name: String by Delegates.observable("Empty"){ property, oldValue, newValue ->
println("$oldValue -> $newValue")
}
// Usage
name = "Martin" // Prints: Empty -> Martin
name = "Igor" // Prints: Martin -> Igor
name = "Igor" // Prints: Igor -> Igor
@marcinmoskala
var list: List<LocalDate> by observable(list) { _, old, new ->
if(new != old) notifyDataSetChanged()
}
private var drawerOpen by Delegates.observable(false) { _, _, opened ->
if (opened) drawerLayout.openDrawer(GravityCompat.START)
else drawerLayout.closeDrawer(GravityCompat.START)
}
var presenters: List<Presenter> by observable(list) { _, old, new ->
(old - new).forEach { it.onDestroy() }
(new - old).forEach { it.onCreate() }
}
@marcinmoskala
var list: List<String> by Delegates.vetoable(emptyList()) { prop, old, new ->
if(new.size < 3) return@vetoable false
println(new)
true
}
// Usage
listVetoable = listOf("A", "B", "C") // Update A, B, C
println(listVetoable) // Prints: [A, B, C]
listVetoable = listOf("A") // Nothing happens
println(listVetoable) // Prints: [A, B, C]
listVetoable = listOf("A", "B", "C", "D", "E") // Prints: [A, B, C, D, E]
var name: String by Delegates.vetoable("") { prop, old, new ->
if (isValid(new)) {
showNewName(new)
true
} else {
showNameError()
false
}
}
@marcinmoskala
class User(map: Map<String, Any>) {
val name: String by map
val kotlinProgrammer: Boolean by map
}
// Usage
val map: Map<String, Any> = mapOf(
"name" to "Marcin",
"kotlinProgrammer" to true
)
val user = User(map)
println(user.name) // Prints: Marcin
println(user.kotlinProgrammer) // Prints: true
operator fun <V, V1: V> Map<String, V>.getValue(
thisRef: Any?,
property: KProperty<*>
): V1 {
val key = property.name
val value = get(key)
if (value == null && !containsKey(key)) {
throw NoSuchElementException("Key ${property.name} is missing in the map.")
} else {
return value as V1
}
}
@marcinmoskala
class User(val map: MutableMap<String, Any>) {
var name: String by map
}
// Usage
val map = mutableMapOf("name" to "Marcin")
val user = User(map)
println(user.name) // Prints: Marcin
user.map.put("name", "Igor")
println(user.name) // Prints: Igor
user.name = "Michal"
println(user.name) // Prints: Michal
@marcinmoskala
fun <T> mutableLazy(initializer: () -> T): ReadWriteProperty<Any?, T>
= MutableLazy<T>(initializer)
private class MutableLazy<T>(val initializer: () -> T) : ReadWriteProperty<Any?, T> {
private var value: T? = null
private var initialized = false
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
synchronized(this) {
if (!initialized) {
value = initializer()
initialized = true
}
return value as T
}
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
synchronized(this) {
this.value = value
initialized = true
}
}
}
@marcinmoskala
var gameMode : GameMode by mutableLazy {
getDefaultGameMode()
}
var mapConfiguration : MapConfiguration by mutableLazy {
getSavedMapConfiguration()
}
var screenResolution : ScreenResolution by mutableLazy {
getOptimalScreenResolutionForDevice()
}
public class MainActivity: BaseActivity() {
@get:Arg var userData: String by argExtra()
}
@marcinmoskala
Source: spindrift-racing.com/jules-verne/drupal/en/log-book/icebergs-en
interface LoginView {
fun showProgress(show: Boolean)
fun getEmail(): String
fun setEmail(text: String)
fun getPassword(): String
fun setPassword(text: String)
fun setEmailError(id: Int?)
fun setPasswordError(id: Int?)
fun requestEmailFocus()
}
@marcinmoskala
override fun getEmail(): String = emailView.text.toString()
override fun setEmail(text: String) {
emailView.text = text
}
override fun getPassword(): String = passwordView.text.toString()
override fun setPassword(text: String) {
passwordView.text = text
}
override fun setEmailError(id: Int?) {
emailView.setErrorId(id)
}
override fun setPasswordError(id: Int?) {
passwordView.setErrorId(id)
}
override fun requestEmailFocus() {
emailView.requestFocus()
}
override fun requestPasswordFocus() {
passwordView.requestFocus()
}
override fun showProgress(show: Boolean) {
progressView.visibility = if (show) View.VISIBLE else View.GONE
loginFormView.visibility = if (show) View.GONE else View.VISIBLE
}
@marcinmoskala
override var progressVisible by bindToLoading(R.id.progressView, R.id.loginFormView)
override var email by bindToText(R.id.emailView)
override var password by bindToText(R.id.passwordView)
override var emailErrorId by bindToErrorId(R.id.emailView)
override var passwordErrorId by bindToErrorId(R.id.passwordView)
override val emailRequestFocus by bindToRequestFocus(R.id.emailView)
override val passwordRequestFocus by bindToRequestFocus(R.id.passwordView)
https://github.com/MarcinMoskala/KotlinAndroidViewBindings
interface LoginView {
var progressVisible: Boolean
var email: String
var password: String
var emailErrorId: Int?
var passwordErrorId: Int?
val emailRequestFocus: () -> Unit
val passwordRequestFocus: () -> Unit
}
interface LoginView {
fun showProgress(show: Boolean)
fun getEmail(): String
fun setEmail(text: String)
fun getPassword(): String
fun setPassword(text: String)
fun setEmailError(id: Int?)
fun setPasswordError(id: Int?)
fun requestEmailFocus()
fun requestPasswordFocus()
}
https://github.com/MarcinMoskala/KotlinAndroidViewBindings
fun Activity.bindToEditText(@IdRes editTextId: Int): ReadWriteProperty<Any?, String>
= bindToEditText { findViewById(editTextId) as EditText }
private fun bindToEditText(viewProvider: () -> EditText): ReadWriteProperty<Any?, String>
= EditTextViewTextBinding(lazy(viewProvider))
private class EditTextViewTextBinding(
lazyViewProvider: Lazy<EditText>
) : ReadWriteProperty<Any?, String> {
val view by lazyViewProvider
override fun getValue(thisRef: Any?, property: KProperty<*>): String {
return view.text.toString()
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
view.setText(value)
}
}
val CAN_EAT_PIE_KEY = "canEatPieKey"
val editor = preferences.edit();
editor.putBoolean(CAN_EAT_PIE_KEY, true);
editor.commit();
if(preferences.getBoolean(CAN_EAT_PIE_KEY, false)) {
// Code
}
val Context.preferences: SharedPreferences
get() = PreferenceManager.getDefaultSharedPreferences(this)
preferences.canEatPie = true
if(preferences.canEatPie) {
// Code
}
@marcinmoskala
https://github.com/MarcinMoskala/KotlinPreference
var SharedPreferences.canEatPie: Boolean by bindToPreferenceField(true, "CanEatPieKey")
var SharedPreferences.experience: Float? by bindToPreferenceFieldNullable()
var SharedPreferences.allPieInTheWorld: Long by bindToPreferenceField(0, "AllPieKey")
var SharedPreferences.savedGame: Game? by bindToPreferenceFieldNullable()
// Usage
stopDietButton.setOnClickListener {
preferences.canEatPie = true
}
startDietButton.setOnClickListener {
preferences.canEatPie = false
}
eatPieButton.setOnClickListener {
if(preferences.canEatPie) {
toast("Heah, pie :D")
} else {
toast("I cannot? :(")
}
}
@marcinmoskala
https://github.com/MarcinMoskala/PreferenceHolder
object Pref: PreferenceHolder() {
var canEatPie: Boolean by bindToPreferenceField(true, "CanEatPieKey")
var longList: Map<Int, Long> by bindToPreferenceField(mapOf(0 to 12L, 10 to 143L))
var savedGame: Game? by bindToPreferenceFieldNullable("GameKey")
}
// Usage
stopDietButton.setOnClickListener {
Pref.canEatPie = true
}
startDietButton.setOnClickListener {
Pref.canEatPie = false
}
eatPieButton.setOnClickListener {
if(Pref.canEatPie) {
toast("Heah, pie :D")
} else {
toast("I cannot? :(")
}
}
@marcinmoskala
@marcinmoskala
override var email by bindToText(R.id.emailView)
private var _userRepo: UserRepository? = null
private val userRepoLock = Any()
val userRepo: UserRepository
get() {
synchronized(userRepoLock) {
if (_userRepo == null) {
_userRepo = UserRepository()
}
return _userRepo!!
}
}
val userRepo by lazy { UserRepository() }
val emailView by lazy { findViewById(R.is.email_view) }
override fun getEmail(): String
= emailView.text.toString()
override fun setEmail(text: String) {
emailView.text = text
}
vs
vs
@marcinmoskala
override var email: String by bindToText(R.id.emailView)
email = "marcinmoskala@gmail.com"
if(email.matches(EMAIL_REGEX)) { /* ... */ }
interface MainView {
var email: String
}
val userRepo: LazyProperty<UserRepository>
= LazyProperty { UserRepository() }
userRepo.getValue().getUser()
val userRepo: UserRepository by lazy { UserRepository() }
userRepo.getUser()
override var email: BindToText = BindToText(R.id.emailView)
email.setValue("marcinmoskala@gmail.com")
if(email.getValue().matches(EMAIL_REGEX)) { /* ... */ }
interface MainView {
var email: BindToText
}
vs
vs
@marcinmoskala
http://kotlinacademy.org/
@marcinmoskala
Marcin Moskała
marcinmoskala.com
blog.kotlin-academy.com
github.com/MarcinMoskala
@marcinmoskala
Senior Android Developer in Gamekit, writing articles, open-source, freelancing and consulting.
marcinmoskala@gmail.com