#31DaysOfKotlin 01~11

in kr •  7 years ago  (edited)

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", "[email protected]")
    val user2 = User("Kim", "[email protected]")
    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
    }
}

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Hey dude great post. Keep it up. I upvoted your post.
You can follow me on https://steemit.com/@manashjyoti

와우 이게 코틀린이군요. 자바 유저인 저에겐 참 신기한 문법들입니다. ㅎㅎㅎ 익숙해지면 편하겠네요.

코틀린에 관심이 있으시면, "Kotlin in Action" 책 추천드립니다. 자바와 비교하며 설명하고 있어서 좋아요~!