Android 앱개발 공부/TIL(Today I Learned)

[Android] TIL 16일차

bunny code 2024. 6. 12. 21:19

유용한 기능


 

* 일반 자료형간의 변환

숫자 자료형끼리는 to자료형() 메소드를 활용

문자열을 숫자로 변경할때에는 별도의 메소드가 필요

정수 형태의 문자열을 정수로 변경할때는 Integer.parseInt(변수명), 실수 형태의 문자열을 실수로 변경할때는 변수명.toDouble()

 

* 일반 자료형 예제

    var num1 = 20
    var num2 = 30.2

    var num3 = num2.toInt()
    var num4 = num1.toDouble()

    var strNum5 = "10"
    var strNum6 = "10.21"

    var num5 = Integer.parseInt(strNum5)
    var num6 = strNum6.toDouble()

    println("num3: $num3")
    println("num4: $num4")
    println("num5: $num5")
    println("num6: $num6")

 

* 출력 결과

30
20.0
10
10.21

 

 

* 객체 자료형 

  • 업 캐스팅:
    • 하위 클래스 객체를 상위 클래스 타입으로 변환
    • 암시적으로 수행 가능
    • 안전하며 as 키워드를 명시적으로 사용할 필요 없음
    • 상위 클래스의 메서드와 프로퍼티만 접근 가능
  • 다운 캐스팅:
    • 상위 클래스 객체를 하위 클래스 타입으로 변환
    • 명시적으로 as 키워드 사용 필요
    • 타입 검사 필요 (is 키워드 사용)
    • 하위 클래스의 메서드와 프로퍼티 접근 가능

 

- 객체 자료형간의 변환 -> 상속관계에서 가능

- 자료형의 타입 확인 -> is 키워드를 활용

 

* 자료형 타입 확인 예제

fun main() {
    var name = "aa"
    if(name is String) {
        println("name은 String 타입입니다")
    } else {
        println("name은 String 타입이 아닙니다")
    }
}

 

* 출력 결과

name은 String 타입입니다

-> name이 어떤 자료형인지를 파악함

 

 

 

* 복수 데이터 값 리턴

- Pair 키워드를 통해 2개의 값을 리턴

- Triple 키워드를 통해 3개의 값을 리턴

 

* 리턴 예제

fun main() {
    var chicken = Chicken()
    var eggs = chicken.getThreeEggs()
    var listEggs = eggs.toList()
    
//    first, second, third로 관리하는 방법
//    var firstEgg = eggs.first
//    var secondEgg = eggs.second
//    var eggTime = eggs.third
    
    // 리스트로 관리하는 방법
    var firstEgg = listEggs[0]
    var secondEgg = listEggs[1]
    var eggTime = listEggs[2]

    println("달걀의 정보는 ${eggs} 입니다.")
    println("리스트 달걀의 정보는 ${listEggs} 입니다.")
    println("첫번째 달걀의 종류는 ${firstEgg} 입니다.")
    println("두번째 달걀의 종류는 ${secondEgg} 입니다.")
    println("달걀은 ${eggTime}에 나왔습니다.")
}

class Chicken {
    fun getTwoEggs(): Pair<String, String> {
        var eggs = Pair("달걀", "맥반석")
        return eggs
    }

    fun getThreeEggs(): Triple<String, String, Int> {
        var eggs = Triple("달걀", "맥반석", 20230101)
        return eggs
    }
}

* 출력 결과

달걀의 정보는 (달걀, 맥반석, 20230101) 입니다.
리스트 달걀의 정보는 [달걀, 맥반석, 20230101] 입니다.
첫번째 달걀의 종류는 달걀 입니다.
두번째 달걀의 종류는 맥반석 입니다.
달걀은 20230101에 나왔습니다.

-> Pair, Triple 둘 다 메소드의 반환 자료형 작성 부분에 작성, 객체 형태로 출력해줌

 

 

 

 

* Scope Functions

자기 자신의 객체를 전달해서 효율적인 처리를 있게 (중요)

 

* let function의 활용

  • 중괄호 블록안에 it으로 자신의 객체를 전달하고 수행된 결과를 반환

* with function의 활용

  • 중괄호 블록안에 this로 자신의 객체를 전달하고 코드를 수행
  • this는 생략해서 사용할 수 있으므로 반드시 null이 아닐때만 사용하는게 좋음

* also function의 활용

  • 중괄호 블록안에 it으로 자신의 객체를 전달하고 객체를 반환해줘요
  • apply와 함께 자주 사용

* apply function의 활용

  • 중괄호 블록안에 this로 자신의 객체를 전달하고 객체를 반환해줘요
  • 주로 객체의 상태를 변화시키고 바로 저장하고 싶을때 사용

* run function의 활용

  • 객체에서 호출하는 경우, 호출하지 않는 경우 두 가지 존재

 

* let function 예제

    var strNum = "10"

    var result = strNum?.let {
        // 중괄호 안에서는 it으로 활용함
        // 문자열 "10"대신 it이 들어간 것
        Integer.parseInt(it)
    }

    println(result!!+1)

 

* 출력 결과

11

 

 

* with function 예제

    var alphabets = "abcd"

    with(alphabets) {
//      var result = this.subSequence(0,2)
        var result = subSequence(0,2)
        println(result)
    }

 

 

* 출력 결과

ab

 

* subSequence 함수 구조

-> startIndex: 부분 문자열의 시작 위치입니다 (포함), endIndex: 부분 문자열의 끝 위치입니다 (포함하지 않음)로 구성

-> 지금 코드에선 0부터 1까지 즉, ab 출력

 

 

* also 함수 구조

fun main() {
    var student = Student("참새", 10)

    var result = student?.also {
        it.age = 50
    }
    result?.displayInfo()
    student.displayInfo()
}

class Student(name: String, age: Int) {
    var name: String
    var age: Int

    init {
        this.name = name
        this.age = age
    }

    fun displayInfo() {
        println("이름은 ${name} 입니다")
        println("나이는 ${age} 입니다")
    }
}

* 출력 결과

이름은 참새 입니다
나이는 50 입니다
이름은 참새 입니다
나이는 50 입니다

 

 

* apply 예제

package com.example.myapplication

fun main() {
    var student = Student("참새", 10)

    var result = student?.apply {
        student.age = 50
    }
    result?.displayInfo()
    student.displayInfo()
}

class Student(name: String, age: Int) {
    var name: String
    var age: Int

    init {
        this.name = name
        this.age = age
    }

    fun displayInfo() {
        println("이름은 ${name} 입니다")
        println("나이는 ${age} 입니다")
    }
}

 

* 출력 결과

이름은 참새 입니다
나이는 50 입니다
이름은 참새 입니다
나이는 50 입니다

 

 

* run function 객체에서 호출하지 않는 경우

package com.example.myapplication

fun main() {
    var totalPrice = run {
        var computer = 10000
        var mouse = 5000

        computer+mouse
    }
    println("총 가격은 ${totalPrice}입니다")
}

 

* 출력 결과

총 가격은 15000입니다

 

 

* run function 객체에서 호출하는 경우

package com.example.myapplication

fun main() {
    var student = Student("참새", 10)
    student?.run {
        displayInfo()
    }
}

class Student(name: String, age: Int) {
    var name: String
    var age: Int

    init {
        this.name = name
        this.age = age
    }

    fun displayInfo() {
        println("이름은 ${name} 입니다")
        println("나이는 ${age} 입니다")
    }
}

 

* 출력 결과

이름은 참새 입니다
나이는 10 입니다

-> with 달리 null체크를 수행할  있으므로 더욱 안전하게 사용가능

 

 

* Scope Functions 정리

 

1. 수신객체와 람다함수간의 긴밀한 관계가 존재

  • 명시적으로 수신객체 자체를 람다로 전달하는 방법, 수신객체를 람다의 파라미터로 전달하는 방법 두 가지로 구분

 

2. 수신객체, 람다와의 관계 

  • T는 수신객체를 의미
  • block: 내부는 람다함수의 소스코드
  • 수신객체는 it으로 사용할 수 있음 -> 모든 수신객체를 it으로 활용하면 문제가 발생할 수 있어서 다른 표기명을 쓰기도 함

* it을 다른 표기명으로 사용하는 방법 예시

// Scope Function을 중첩으로 사용할 경우 누가 누구의 범위인지 알수 없다!
// Implicit parameter 'it' of enclosing lambda is shadowed 경고 발생!

data class Person(
	var name: String = "",
	var age: Int? = null,
	var child: Person? = null
)

// 잘못된 예시
Person().also {
	it.name = "한석봉"
	it.age = 40
  val child = Person().also {
	  it.name = "홍길동" // 누구의 it인지 모른다!
    it.age = 10 // 누구의 it인지 모른다!
  }
  it.child = child
}

// 수정한 예시
Person().also {
	it.name = "한석봉"
	it.age = 40
  val child = Person().also { c ->
	  c.name = "홍길동"
    c.age = 10
  }
  it.child = child
}

-> it이 여러곳에서 쓰일 경우 명칭을 달리함, 수정한 예시에서 보면 it 대신 c를 사용

 

 

* 정리

  Scope에서 접근방식 this Scope에서 접근방식 it
블록 수행 결과를 반환 run, with let
객체 자신을 반환 apply also

 

 

 

 

확장함수


 

* 확장함수란? 기존 클래스에 쉽게 메소드를 추가, 원하는 메소드가 있지만 내가 설계한 클래스가 아닐때 외부에서 메소드를 관리

 

* 주의사항

  • 확장함수는 public 멤버에만 접근할 수 있고 private, protected는 접근할 수 없음
  • 상속 불가능, 오버라이딩 불가능

* 확장함수 예제

package com.example.myapplication

fun main() {
    fun Student.getGrade() = println("학생의 등급은 ${this.grade} 입니다")
    var student = Student("참새", 10, "A+")
    student.displayInfo()
    student.getGrade()
}

class Student(name: String, age: Int, grade: String) {
    var name: String
    var age: Int
    var grade: String

    init {
        this.name = name
        this.age = age
        this.grade = grade
    }

    fun displayInfo() {
        println("이름은 ${name} 입니다")
        println("나이는 ${age} 입니다")
    }
}

 

* 출력 결과

이름은 참새 입니다
나이는 10 입니다
학생의 등급은 A+ 입니다

 

-> Student.getGrade()를 사용하여 Student 클래스에 getGrade() 메소드를 추가함

 

 

 

* 비동기 프로그래밍

 

* 동기 프로그래밍

  • 요청 보내고 결과값을 받을 때까지 작업을 멈춤
  • 가지씩 작업을 처리

* 비동기 프로그래밍

  • 요청 보내고 결과값을 받을 때까지 멈추지 않고 또 다른 일을 수행
  • 다양한 일을 번에 수행

 

쓰레드


 

* 쓰레드

  • 프로그램은 하나의 메인 쓰레드(실행흐름)이 존재, 메인 쓰레드는 메인함수 fun main()를 의미
  • 메인 쓰레드만 사용할 때는 로직을 실행해서 동시처리가 불가능 했지만 자식 쓰레드를 생성하면 동시에 로직 실행 가능
  • thread 키워드를 활용

 

프로세스와 쓰레드의 차이는?

 

* 프로세스(Process)

  • 프로그램이 메모리에 올라가서 실행될때 이를 프로세스 1개라 칭함
  • 보통 프로그램을 더블클릭하면 프로세스가 생김

 

* 쓰레드(Thread)

  • 프로세스보다 더 작은 작업 단위
  • 쓰레드를 생성하면 스택메모리의 일정 영역을 차지

 

 

* 쓰레드

package com.example.myapplication

import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.concurrent.thread

fun main() {
    thread(start = true) {
        for(i in 1..10) {
            println("Thread1: 현재 숫자는 ${i}")
            runBlocking {
                launch {
                    delay(1000)
                }
            }
        }
    }

    thread(start = true) {
        for(i in 50..60) {
            println("Thread2: 현재 숫자는 ${i}")
            runBlocking {
                launch {
                    delay(1000)
                }
            }
        }
    }
}

 

* 출력 결과

Thread1: 현재 숫자는 1
Thread2: 현재 숫자는 50
Thread2: 현재 숫자는 51
Thread1: 현재 숫자는 2
Thread1: 현재 숫자는 3
Thread2: 현재 숫자는 52
Thread1: 현재 숫자는 4
Thread2: 현재 숫자는 53
Thread1: 현재 숫자는 5
Thread2: 현재 숫자는 54
Thread2: 현재 숫자는 55
Thread1: 현재 숫자는 6
Thread2: 현재 숫자는 56
Thread1: 현재 숫자는 7
Thread1: 현재 숫자는 8
Thread2: 현재 숫자는 57
Thread1: 현재 숫자는 9
Thread2: 현재 숫자는 58
Thread1: 현재 숫자는 10
Thread2: 현재 숫자는 59
Thread2: 현재 숫자는 60

-> delay(1000)을 넣어서 1초마다 값이 동시에 출력 됨

-> 출력 순서는 랜덤, 이유는 CPU에 먼저 도착한 값이 먼저 출력되기 때문에 계속 달라짐(경쟁하는 것)

 

 

 

코루틴


 

코루틴이란? 쓰레드보다 더 경량화된 동시처리 기능 제공

  • 안정적인 동시성, 비동기 프로그래밍을 가능하게 함
  • 코루틴이 쓰레드보다 더욱 가볍게 사용
  • lanuch는 Job객체로 코루틴을 관리(Job객체는 다양한 함수를 가지고 있음), 결괏값이 없는 코루틴 
  • join: 현재의 코루틴이 종료되기를 기다려요
  • cancel: 현재의 코루틴을 즉시 종료
  • async는 결과값이 있는 코루틴이다

 

코루틴은 스코프로 범위를 지정할 수 있음

  • GlobalScope: 앱이 실행된 이후에 계속 수행되어야 할 때
  • CoroutineScope: 필요할때만 생성하고 사용 후에 정리가 필요 (job.cancel을 사용)

 

* 코루틴 예제

import kotlinx.coroutines.*

fun main() = runBlocking { // this: CoroutineScope
    launch { // launch a new coroutine and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World!") // print after delay
    }
    println("Hello") // main coroutine continues while a previous one is delayed
}

 

* 출력 결과

Hello
World!

-> runBlocking : 코루틴 빌더로 fun main()의 코루틴이 아닌 영역과 runBlocking() 중괄호 내의 코루틴 코드를 연결

-> launch : 코루틴 빌더로 나머지 코드와 동시에 새로운 코루틴을 시작하며 계속해서 독립적으로 작동

-> 이 코드에서 Hello가 먼저 출력된 것도 launch로 인해 World!가 나중에 출력되기 때문

-> delay : 일정 기간 동안 딜레이를 거는 것 

 

 

* 쓰레드와 코루틴의 차이

  • 쓰레드나 코루틴은 각자의 방법으로 동시성을 보장하는 기술
  • 코루틴은 Thread를 대체하는 기술이 아니라 하나의 Thread를 더욱 잘개 쪼개서 사용하는 기술
  • 코루틴은 쓰레드보다 CPU 자원을 절약하기 때문에 Light-Weight Thread라고 칭함

 

 

 

참고자료


 

https://gngsn.tistory.com/207

 

Kotlin, 코루틴 제대로 이해하기 - (1)

kotlin의 Coroutine을 이해하는 것이 해당 포스팅의 목표입니다. 🔗 Kotlin 시리즈 모아보기 사실, 순서대로라면 Class에 대한 내용을 다뤄야하는데, 추석이 끝나고 마음이 급해져서 코루틴이라도 파보

gngsn.tistory.com

 

- 코틀린 공식 사이트 : 코루틴 기본 개념 이해

https://kotlinlang.org/docs/coroutines-basics.html#your-first-coroutine

 

Coroutines basics | Kotlin

 

kotlinlang.org

 

 

 

 

'Android 앱개발 공부 > TIL(Today I Learned)' 카테고리의 다른 글

[Android] TIL 18일차  (0) 2024.06.14
[Android] TIL 17일차  (2) 2024.06.13
[Android] TIL 15일차  (0) 2024.06.11
[Android] TIL 14일차  (2) 2024.06.10
[Android] TIL 13일차  (0) 2024.06.07