와챠의 우당탕탕 코딩 일기장

[Android/Kotlin]Udemy 강의 정리: Kotlin Basics(2) 본문

코딩 일기장/Android(Kotlin)

[Android/Kotlin]Udemy 강의 정리: Kotlin Basics(2)

minWachya 2022. 6. 5. 19:10
반응형

목차

  1. Object
    1. Object 무명 객체
    2. class / interface생성 시의 Object
    3. companion object
  2. const
  3. inner class
  4. Extension functions
  5. Extensions properties
    1. lambda
    2. trailing lambda
  6. Scope function
    1. let
    2. with
    3. run
    4. apply
    5. also
  7. enum class
  8. sealed

1. Object

object: 싱글톤 패턴

언제 사용?: 언제나 같은 결과 반환을 기대할 때

주의해야할 점: 싱글톤 객체가 어느 위치에서 값을 수정했는지 파악 어려움

// 장바구니
object CartItems {
	// private 제품 리스트, mutableKist: 수정, 추가 가능
	private val mutableProducts = mutableListOf<Product>()
    // public 제품 리스트, List 수정, 추가 불가능
    val product: List<Product> = mutableProducts
    
    // 제품 추가
    fun addProduct(product: Product) { mutableProducts.add(product) }
}

 

1-1. Object 무명 객체

: 앞의 이름이 있는 object와는 달리 매번 새로운 객체를 만듦!!

val cartItems = object {
	val products = mutableListOf(Product("전자기기", "갤럭시폰"))
    
    override fun toString() = products.toString()
}

1-2. class / interface생성 시의 Object

val clickListener = object: ClickListener {
	override fun onClick(view: View) { ~ }
}

1-3. companion object

  • companion object 내부에 선언된 변수와 함수들은 java 의 static 이 아님
  • 단, 아래 케이스들은 static
    • const val 로 선언된 상수
    • @JVMStatic 또는 @JVMField이 붙은 변수 + 함수
class Store private constructor(private val products: List<Product>) {
	companion object {
    	fun create(storeId: String): String { return "~" }
    }
}

val store = Store.create("electronics")

// 아래 2개 모두 같은 기능
Store.create("electronics")					// 1
Store.Companion.create("electronics")		// 2

// But, companion에 이름 붙일 시에는 2번으로는 호출 못 함

 

생성자에 private를 붙여 외부에서 인스턴스 생성 못 하게 함 + create()사용해서 인스턴스 반환해서 싱글톤 패턴 만들수도


2. const

: 상수값

ex) const val KEY = "1234"


3. inner clase

: inner 안 붙이면 외부에서 접근 불가능!


4. Extension functions

fun MutableList<Int>.swap(x: Int, y: Int) {
	val temp = this.x
    this[x] = this[y]
    this[y] = temp
}

val list = mutableListOf(1, 2, 3)
list.swap(0, 2)

MutableList<Int>타입에서 호출할 수 있는 함수를 추가하는 것!

확장할 클래스.함수명() { ~ }<< 일케 쓰면 됨

너무너무 편하고 멋져보인다


5. Extensions properties

val <T> List<T>.lastIndex: Int
    get() = size - 1
    
    
val list = listOf(1, 2, 3, 4)
println("${list.lastIndex}")

아까는 함수를 추가했다면 이번에는 프로퍼티 추가하는 방법

프로퍼티 이름 앞에 리시버 타입 선언하면 됨

 

대상 오브젝트에 추가 하는것이 아니기 때문에 backing field 를 가질 수 없어서(initializer X)

setter, getter를 써야 함

 

+) 프로퍼티: 변수 + getter / setter

+ field: 프로퍼티의 get(), set() 함수가 접근하는 곳

외부 프로퍼티 이용 시에는 프로퍼티의 get(), set() 함수가 호출되는데,

get(), set() 내부에서는 field 를 통해 프로퍼티가 가지고 있는 값에 접근함.

이렇게 뒤에 숨어있는 필드라는 의미라는 뜻으로 backing field 라고도 부름


함수 타입

  • (파라미터) -> 리턴타입
    • ex) (Int) -> Boolean
  • 이름도 쓸 수 있음
    • ex) (input: Int) -> Boolean
  • 제너럴 타입도 사용 가능
    • ex) (T) -> Boolean
  • 여러 파라미터 사용 가능
    • ex) (Int, Int) -> Int
ex) val isEven: (Int) -> Boolean = { it % 2 == 0 }

ex) val sum: (Int, Int) -> Int = { first: Int, second: Int ->
	first + second
}
val resumt = sum(1, 2)

 

Int.() -> Boolean

T.() -> R 형식도 가능

ex) val isEven: Int.() -> Boolean = { this % 2 == 0 }

 

5-1. lambda

: 인자를 중괄호로 표현하는 식

val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
val sum = { x: Int, y: Int -> x + y }

 

5-2. trailing lambda

: 여러 파라미터 중 마지막 파라미터가 함수일 때 trailing lambda 지원

예시의 두번째 파라미터: 인덱스에 따라 초기화 어떻게 할 지 함수타입으로 전달하는 것

fun <T> MutableList(size: Int, init: (index: Int) -> T) : MutableList<T>

val list = MutableList(5) { index -> index }
// index로 list를 초기화 한 것 == [0, 1, 2, 3, 4]

6. Scope function

함수타입을 인자로 받는 고차함수

모두 제레닉 사용

함수명 앞에 T. 붙어있는 확장 함수도 있음

 

inline: 함수 호출의 부하 없이 호출

 

6-1: let

null 체크

// 1. let
inline fun <T, R> T.let(block: (T) -> R): R
// null아닌 경우에만 함수호출하는 용도로 사용
// ex
fun processNonNullString(str: String) {}
val str: String? = "hello"		// String? 타입
processNonNullString(str)	// error!! null일수고 있기 때문
val length = str.let {  // str이 null이 아닐 때 let으로 전달
	processNonNullString(it)	// 가능
    it.length	// 반환!
}

 

6-2. with

(앞에서 받은 객체와) 추가로 함께 작업할 거 있을 때

// 2. with
inline fun <T, R> with(receiver: T, block: T.() -> R): R
// receiver객체의 참조를 블록으로 전달받아 추가로 처리할 작업이 있는 경우 주로 사용
// ex
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
	println($this)	// numbers
    println($size)	// numbser.size
}
// let처럼 ?<와 함께 쓸 수 없음 << Null여부 알 수 없음!!

 

6-3. run

그냥 작업할 때

// 3. 확장함수 run
inline fun <T, R> T.run(block: T.() -> R): R
// T.()를 보니... 블록의 참조인 this 사용 가능
// ex
val products = mapOf(
	"패션" to "겨울 패딩",
    "전자기기" to "핸드폰",
)
// Key로 조회했을 때 Null일 수 있어서 Null 여부 확인
// let: it으로 참조
products["패션"]?.let{ println("상품명 $it") }
// run: this로 참조 + this의 프로퍼티 접근 시 this 생략 가능
products["패션"]?.let{ println("상품명 $this") }


// 4. 확장함수 아닌 run
inline fun <R> run(block: () -> R): R
// !!수신객체 없이!! 블록 안에서 특정 코드를 실행시켜야 하는 경우에 사용
// ex
products["패션"]?.run {
	println("상품명: $this")
} ?: run {	// 수신객체 없이~~ 값이 없는 경우에 실행됨(it, this 사용 불가 당연함)
	println("등록된 상품이 없습니다.")
}

 

6-4. apply

생성 시점에 추가로 더 작업할 거 있을 때

// 5. apply
inline fun <T> T.apply(block: T.() -> Unit): T
// T.() << 객체 참조 this로 할 수 있겠다
// 블록의 반환타입이 Unit + 수신객체의 타입이 apply의 반환 타입이 됨
// 객체 생성 시점에 추가로 처리해야하는 작업이 있는 경우 사용
// ex
val adam = Person("Adam").apply {
	age = 32
    city = "London"
}

 

6-5. also

생성 이후에 작업할 거 있을 때

// 6. also
inline fun <T> T.also(block: (T) -> Unit): T
// 블록 안의 파라미터 타입이 수신객체 T와 동일, it 참조!
// 블록 반환 타입 Unit + 수신객체 타입인 T가 also의 반홤 타입
// 객체 생성 이후 추가로 처리해야 할 작업이 생기는 경우 사용
// ex
val numbers = mutableListOf("one", "two", "three")
numbers
	.also ( println("추가되기 전 상태: #it")
    .add("four")

7. enum class

같은 타입의 여러 상수 정의

enum class Color {
	RED,
    GREEN,
    BLUE
}


// enum + when: else 없이도 모든 조건 평가 가능
val colorCode = when (color) {
	Color.RED -> "red"
    Color.GREEN -> "green"
    Color.BLUE -> "blue"
}
// 생성자, 프로퍼티 선언 가능
enum class Color(val rgb: Int) {
	RED(0xFF0000),
    GREEN(0x00FF00),
    BLUE(0x0000ff)
 }
 
 // 함수도 선언 가능
enum class ProtocolState {
	WAITING { override fun signal() = TALKING },
    TALKING { override fun signal() = WAITING };
    
    abstract fun signal(): ProtocolState
}

8. sealed interface / class

: 클래스 간 계층 구조 생성 가능

class도 될 수 있고 object도 될 수 있음

같은 패키지 내에서만 subclass 선언 가능

sealed<<이거는 AndroidWeekly에서도 본 적이 있다.

그때도 이게 뭐지..하고 지나갔던 기억이,,다시 봐야지,,

이것도 상태 체크하는 거 같은데,,,!!!


코틀린 공부를 완전히 각잡고 책 봐가면서 제대로 해 본 적은,,, 없는데

그래도 쫌쫌따리 모르는 거 검색하고 공식문서 몇 개 보고 한 게 도움이 좀 된 거 같다.

그래도 코틀린 문법에 더 익숙해져야 할 필요성을 많이 느꼈다!!

 

글고 Scope function은 전에도 블로그에 정리한 적이 있지만 완벽한 이해는 아니었는데

이번에 또 공부하다보니까 이해가 더 잘 된 거 같다.

 

싱글톤 패턴이나 Scope function 사용해서 이전 플젝들을 조금씩 수정하고 싶어졌다ㅎ

 

 

반응형
Comments