kkumot avatar

#31DaysOfKotlin 01~11

kkumot

Published: 06 Mar 2018 › Updated: 06 Mar 2018#31DaysOfKotlin 01~11

#31DaysOfKotlin 01~11

https://twitter.com/AndroidDev 에서 #31DaysOfKotlin 태그로 3월 1일부터 글이 올라오고 있다.

어디에 정리할까하다가 버려둔 여기가 생각났다.


#31DaysOfKotlin


Day 1. Elvis operator

엘비스 연산자를 사용하면 다음과 같이 null 을 처리 할수 있다.

kotlin에서 return 과 throw는 expressions 이므로 엘비스 연산자 우측에 사용할 수 있다.

val name: String = person.name ?: "unknown"
val age = person.age ?: return

Day 2. String templates

'$' 를 사용하면 쉽게 문자열을 만들 수 있다.

val language = "Kotlin"

// "Kotlin has 6 characters"
val text = "$language has ${language.length} characters"

Day 3. Destructuring Declarations

AndroidKTX. 에서는 구조 분해 선언을 사용하여 아래와 같은 구문을 사용할 수 있다.

data 클래스로 선언하는 경우 주 생성자에 대하여 componentN 함수가 자동으로 생성된다.

data 클래스가 아닌 경우 operator 키워드를 사용하여 직접 함수를 구현하여야 한다.

// now with prisms
val (red, green, blue) = color

// destructuring for squares
val (left, top, right, bottom) = rect

// or more pointedly
val (x, y) = point


// 기존에 존재하는 클래스를 구조 분해 선언을 사용하고자 하는 경우
class Person(val name: String, val age: Int)

inline operator fun Person.component1() = this.name
inline operator fun Person.component2()= this.age

Day 4. When Expression

코틀린에는 switch 문이 없다. when을 사용하여 JAVA의 switch 보다 더 다양하게 사용할 수 있다.

class Train(val cargo: Number?) {
    override fun toString(): String {
        return when (cargo) {
            null, 0 -> "empty"
            1 -> "tiny"
            in 2..10 -> "small"
            is Int -> "big inty"
            else -> "$cargo"
        }
    }
}

Day 5. Ranges, Destructuring Declarations

    // iterating in the range 1 to 100
    for(i in 1..100) { println(i) }

    // iterating backwards, in the range 100 to 1
    // 100..1 로 쓰면 안된다.
    for(i in 100 downTo 1) { println(i) }

    // iterating over an array, getting every other element
    // until을 사용하면 '미만'이다.
    // step 단위로 증가한다.
    // 아래는 1이 출력된다. 1부터 2까지 step 2로 증가다.
    val array = arrayOf("a", "b", "x")
    for(i in 1 until array.size step 2 ) { println(i) }

    // iterating over an array with the item index and destructuring
    for((index, element) in array.withIndex()) { println("$index : $element") }

    // iterating over a map
    val map = mapOf(1 to "one", 2 to "two")
    for((key, value) in map) { println("$key : $value") }

Day 6. Properties

Kotlin에서 Property는 getter/setter를 통해 접근한다. 'val' 는 변경 불가능한 변수이므로 setter가 생성되지 않는다.

'field' 를 통해 setter에서 변수의 값을 수정할 수 있다.

class User {
    // properties
    val id: String = "" // immutable. just getter

    var name: String = "" // default getter and setter

    var surname: String = "" // custom getter, default setter
        get() = surname.toUpperCase() // custom getter declaration

    var email: String = "" // default getter, custom setter
        set(value) {
            // "value" = name of the setter parameter
            // "field" = property's backing field; generated
            if(isEmailValid()) field = value
        }
}

Day 7. Data class, Equality

data 클래스를 생성하면 equals(), hashCode(), toString(), copy(), componentN() 함수가 자동으로 생성된다.

코틀린에서 == 연산자는 자바의 equals 함수로 대치된다. 심지어 널체크도 된다.

(cf. 0.0과 -0.0은 서로 다른 값이다. -0.0은 0.0보다 작은 값이다.)

data class User(val name: String, val email: String)

fun main(args: Array<String>) {
    val user1 = User("Kim", "kim@email.com")
    val user2 = User("Kim", "kim@email.com")
    val user3 = null
    println("${user1 == user2}") // true
    println("${user3 == user2}") // false
}

Day 8. Visibility Modifiers

코틀린의 기본 visivility는 public 이다. 또한 코틀린은 자바와 다르게 클래스 선언 없이도 변수와 함수를 파일에 선언할 수 있다.

// example.kt

// public by default
val isVisible = true

// only in the same file
private val isHidden = true

// internal to compilation 'module'
// internal은 생소한 녀석인데, 함께 컴파일 되는 모듈 안에서만 visible이 true이다.
// 그러나 자바에서는 해당 visibility가 없기 때문에, public 으로 컴파일 된다고 한다.
internal val almostVisible = true

class Foo {
    // public by default
    val isVisible = true
    
    // visible to my subclasses
    protected val isInheritable = true
    
    // only in the same class
    private val isHidden = true
}

Day 9. Default Arguments

코틀린에서 기본 인수를 사용하면 코드를 좀 더 깔끔하고 읽기 쉽게 만들 수 있다. 쓸데없이 수 많은 오버로드 메서드를 만들지 않아도 된다.

// parameters with default values
class BulletPointSpan(
        private val bulletRadius: Float = DEFAULT_BULLET_RADIUS,
        private val gapWidth: Int = DEFAULT_GAP_WIDTH,
        private val color: Int = Color.BLACK
) { /* ... */ }

// useing only default values
val bulletPointSpan = BulletPointSpan()

// 첫 번째 파라메터는 resource에서 얻은 값을 사용하고, gapWidth와 color값은 기본 값을 사용
val bulletPointSpan2 = BulletPointSpan(resource.getDimension(R.dimen.radius))

// 이름 있는 인수를 사용하여 첫 번째와 두 번째 인수는 기본 값을 사용하고 컬러는 레드를 사용
val bulletPointSpan3 = BulletPointSpan(color = Color.RED)

Day 10. Sealed Classes (3/3)

Sealed 클래스 첫 번째 시간.

Sealed 클래스를 사용하면 쉽게 에러 데이터를 다룰 수 있다. LiveData와 함께 사용하면 하나의 LiveData로 성공과 실패를 모두 나타낼 수 있다. 2개의 변수를 사용하는 것 보다 낫다.

sealed class NetworkResult
data class Success(val result: String) : NetworkResult()
data class Failure(val error: Error) : NetworkResult()

// one observer for success and failure
viewModel.data.observe(this, 
        Observer<NetworkResult> { data ->
    data ?: return@Observer // skip nulls
    when (data) {
        is Success -> showResult(data.result) // smart cast to Success
        is Failure -> showError(data.error) // smart cast to Failure
    }

})

  • Sealed 클래스 두 번째 시간.

RecyclerView adapter에 sealed classes를 사용할 수 있습니다. ViewHolders에 딱 맞는데 각각의 홀더에 명시적으로 매칭되는 모든 타입을 깔끔하게 나타낼 수 있습니다. Sealed 클래스를 사용하면 모든 타입에 매칭되지 않으면 컴파일 에러가 발생합니다.

// use Sealed classes as ViewHolders in a RecyclerViewAdapter
override fun onBindViewHolder(holder: SealedAdapterViewHolder, position: Int) {
    return when (holder) { // compiler enforces handling all types
        is HeaderHolder -> holder.displayHeader(items[position]) // smart cast here
        is DetailHolder -> holder.displayDetails(items[position]) // smart cast here
    }
}

  • Sealed 클래스 마지막 시간.

RecyclerView Item에 많은 콜백 함수가 있는 경우(상세 클릭, 공유 액션, 삭제 액션), sealed classes를 사용할 수 있습니다.

하나의 콜백에서 모든 액션을 처리 할 수 있습니다.

sealed class DetailItemClickEvent
data class DetailBodyClick(val section: Int): DetailItemClickEvent()
data class ShareClick(val platform: String): DetailItemClickEvent()
data class DeleteClick(val confirmed: Boolean): DetailItemClickEvent()

class MyHandler : DetailItemClickInterface {
    override fun onDetailClicked(item: DetailItemClickEvent) {
        return when (item) { // compiler enforces handling all types
            is DetailBodyClick -> expandBody(item.section)
            is ShareClick -> shareOn(item.platform)
            is DeleteClick -> if (item.confirmed) doDelete() else confirmDelete()
        }
    }
}

Day 11. Lazy

Lazy 를 사용하면 property 초기화를 사용하는 시점까지 미룰 수 있다. 이후 사용되어 초기화 된 값은 저장되어 다음 호출 시 사용된다.

val preference: String by lazy { sharedPreferences.getString(PREFERENCE_KEY) }

Day 12. Late initialized properties and variables

안드로이드에서는 onCreate 또는 다른 콜백에서 객체를 초기화 합니다. 코틀린에서 non-null 변수는 반드시 초기화 되어야 합니다. lateinit을 사용하면, 이 상황을 해결할 수 있습니다.

class MyActivity : AppCompatActivity() {
    lateinit var recyclerView: RecyclerView // non-null, but not initialized
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        //..
        recyclerView = findViewById(R.id.recycler_view) // initialized here
    }
}

Leave #31DaysOfKotlin 01~11 to:

Written by

Read more #kr posts


Best Posts From kkumot

We have not curated any of kkumot's posts yet. But you can encourage our curation team to review posts by visiting them regularly and by referring other readers. Because we give priority to frequently read content.

More Posts From kkumot