내일배움캠프 9일차

김재현·2024년 3월 8일

강의 5주차 정리

유용한 기능

  • 숫자 자료형끼리는 .to자료형() 메소드를 활용할 수 있다.

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

  • 위 이미지와 같이 strNum로 변수를 지정하고 자료형 메소드로 지정하거나

    Integer.을 통해 변환을 해줄 수 있다.

  • 혹시 Double 타입의 변수를 Int로 변환해봤는데 에러가 났다. 타입에 맞는 Integer를 써야한다.

객체 자료형간의 변환

  • 객체 자료형간의 변환은 상속관계에서만 가능하다.
  • birds.add(Sparrow(name) 단락을 통해 sparrow라는 객체를 생성하고
 for(bird in birds) {
 	bird.fly()

단락을 통해 호출하는데 전과 다르게 bird fly()는 하나 밖에 없음에도 두개의 메세지가 출력된다.
sparrow는 bird의 클래스를 상속받고 있기때문에 업캐스팅을 통해 각각 가지고 있는 name으로 출력된다.

  • 반대인 다운 캐스팅은 부모 클래스에서 자식 클래스로 정보를 전달한다는 개념인데

    bird의 정보를 sparrow가 전부 가지고 있지 않아 다운 캐스팅은 불가하다.

자료형의 확인

  • 코틀린에서는 is 키워드를 활용해 자료형의 타입을 확인할 수 있다.
if(name is String) {
	println("name은 String 타입입니다")
} else {
    println("name은 String 타입이 아닙니다")
}
  • 메소드는 기본적으로 하나의 데이터를 리턴한다.

  • 두 개 이상의 데이터를 포함하는 데이터클래스를 설계하고 인스턴스를 리턴한다해도 가능하다

  • 각 데이터는 다른 형태여도 가능한것 같다.

  • Pair를 활용한 두 개의 인스턴스 리턴
fun egg() : Pair<String, String>
	var eggs = Pair("달걀", "맥반석")
  • Triple을 활용한 세 개의 인스턴스 리턴
fun egg() : Triple<String, String, Int>
	var eggs = Triple("달걀", "맥반석", 20240308)

Scope Functions

  • 코틀린에서 제공하는 스코프 함수는 객체를 사용할때 임시로 scope를 만들어 편리한 코드 작성을 도와준다.

let

  • let function은 중괄호 블록안에 it으로 자신의 객체를 전달하고 수행된 결과를 반환한다.
// ex)
var strNum = "10"
var result = strNum?.let {
Integer.parseInt(it) // 중괄호 안에서는 it으로 활용함
}
print(result!!+1)

with

  • with function은 중괄호 블록안에 this로 자신의 객체를 전달하고 코드를 수행한다.
  • this는 생략해서 사용할 수 있으므로 null이 아닐때만 사용하는 것이 좋다.
//ex)
val alphabets = "abcd"
with(alphabets) {
    var result = subSequence(0,2) //var result = this.subSequence(0,2)
    println(result)
}

also

  • also는 중괄호 블록안에 it으로 자신의 객체를 전달하고 객체를 반환해준다. (apply와 자주 사용)
//ex)
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} 입니다")
    }
}

apply

  • apply function은 this로 자신의 객체를 전달하고 반환해준다.
  • 주로 객체의 상태를 변화시키고 바로 저장하고 싶을때 사용한다.
//ex)
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} 입니다")
    }
}

run

  • run function은 객체에서 호출하는 경우와 하지않는 경우가 다르다.
// 객체에서 호출하지 않을때
var totalPrice = run {
	var computer = 10000
    var mouse = 5000
    computer+mouse    
}    
println("총 가격은 ${totalPrice}입니다")
// 객체에서 호출할때
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} 입니다")
    }
}
  • 객체에서 호출할 때는 with와 달리 null체크가 가능하여 더욱 안전하다.
  • 그래서 with와 기능이 비슷하지만 안전성이 높아 run을 추천한다.

Scope Function의 정리

  • 수신객체는 it이나 this 같은 것으로 사용가능합니다.
  • 스코프 함수는 명시적으로 수신객체 자체를 람다로 전달하는 방법, 수신객체를 람다의 파라미터 로 전달하는 방법으로 구분할 수 있다.
  • 모든 수신객체를 it으로 활용하면 문제가 발생할 수 있다.★
    • Child Function에서 Shadow가 되어서 참조하지 못할 수 있다.
    • it은 다른 이름으로 변경해서 사용하기도 한다.
ex)
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.name처럼 바꿔줄 수 있다.

확장함수

  • 내가 원하는 메소드가 있지만 내가 설계한 클래스가 아닐때 외부에서 메소드를 관리할 수 있다.

  • 원본 클래스의 일관성은 유지하면서 메소드를 추가하는 장점이 존재한다.

  • 확장함수는 클래스 내부 멤버함수 형태가 아니므로 상속할 수 없고 override 할 수 없다.

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} 입니다")
    }
}
  • 다음과 같이 class밖에 student.getGrade()라는 확장함수를 통해 class에 영향을 끼치지않고 외부에서 메소드 관리를 할 수 있다.

비동기 프로그래밍

  • 순서대로 하나의 작업씩 수행하는 행위를 동기적 프로그래밍이라고 한다.

  • 동기적 프로그래밍은 앞선 작업이 끝나지 않는다면 뒷작업은 수행할 수 없게 된다.

  • 비동기 프로그래밍은 요청을 보내고 결과값을 받을 때까지 또 다른 일을 수행할 수 있다.

쓰레드

  • 먼저 프로세스는 프로그램이 메모리에 올라가서 실행되는 것을 이야기한다.

  • 쓰레드(Thread)는 프로세스보다 더 작은 단위로 프로세스 안에서 더 작은 작업의 단위를 쓰레드라고 한다.

  • 쓰레드는 수행될때 각 독립된 메모리 영역인 STACK을 가지고 쓰레드 한 개 생성하면 스택메모리의 일정 영역을 차지한다.

  • 예를 들어 쓰레드는 경마 프로그램에서 말들이 동시에 출발해서 경쟁하는 것 같은때 필요하다.

ex)
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)
                }
            }
        }
    }
}

  • 해당 코드를 실행하면 다음과 같이 랜덤으로 두개의 쓰레드가 시작된다.

코루틴

  • 코루틴은 최적화된 비동기 함수를 사용하고 쓰레드보다 더욱 가볍게 사용할 수 있다.

  • 코루틴은 빌더와 함께 사용하는데 일반적으로 launch와 async 빌더를 많이 사용한다.

  • launch는 결과값이 없는 코루틴 빌더를 의미하고 async는 결과값이 있는 코루틴이다.

  • 하나의 쓰레드 안에 여러개의 코루틴을 정해서 쓸때 사용하는 개념으로는 다음과 같이 있다.

    • Dispatchers.Main: UI와 상호작용하기 위한 메인쓰레드
    • Dispatchers.IO: 네트워크나 디스크 I/O작업에 최적화되어있는 쓰레드
    • Dispatchers.Default: 기본적으로 CPU최적화되어있는 쓰레드
    • 안드로이드에서는 특히 Dispatcher간의 변환을 해야하는 작업을 고려해야한다.

쓰레드와 코루틴

  • 쓰레드와 코루틴은 둘 다 동시성 프로그래밍을 위한 기술이다

  • 쓰레드와 코루틴의 차이는 다음과 같다.

쓰레드

  • 쓰레드 작업 하나의 단위 : Thread(각 Thread가 독립적인 Stack 메모리 영역을 가진다.)
  • 운영체제 커널에 의한 Context Switching 을 통해 동시성을 보장한다.
  • 블로킹(Blocking)
    • Thread A가 Thread B의 결과를 기다리고 있을때 Thread A는 블로킹 상태라고 할 수 있다.
    • A는 B의 결과가 나올 때까지 해당 자원을 사용하지 못한다.
    • 어떤 쓰레드를 먼저 실행해야할지 결정하는 행위를 스케쥴링이라고 한다.
    • Thread A,B와 관계없이 Thread C에서는 작업을 진행중이다.
    • 이러한 행위를 통해 동시성을 보장한다.

코루틴

  • 코루틴 작업 하나의 단위 : Coroutine Object(여러 작업 각각에 Object를 할당.)
  • Coroutine Object도 엄연한 객체이기 때문에 JVM Heap에 적재한다.(코틀린 기준)
  • Programmer Switching (No-Context Switching)을 통해 동시성을 보장한다.
    (소스코드를 통해 Switching 시점을 마음대로 정한다.)
  • Suspend(Non-Blocking)
    • Object 1이 Object 2의 결과를 기다릴 때 Object 1의 상태는 Suspend로 바뀐다.
    • 그래도 Object 1을 수행하던 Thread는 그대로 유효하다.
    • 그래서 Object 2도 Object 1과 동일한 Thread에서 실행된다.

정리

  • 쓰레드와 코루틴은 각자의 방법으로 동시성을 보장하는 기술이다.

  • 코루틴은 하나의 쓰레드를 더욱 잘게 쪼개서 사용하는 기술이다.

  • 코루틴은 쓰레드보다 CPU 자원을 절약하기에 Light-Weight Thread 라고 한다.

강의 마무리.
.
.
.
.
.

개인과제

  • 개인과제 해설을 받고 AbstractOperation을 만들어 추상화하고 코드를 수정해보았다.

    각 코드를 수정하고

    Calulator를 수정했는데 클래스가 사용되지않고 바로 AddOperation으로 넘어가진다.

    메인은 변수를 추가해서 연산자마다 각자 class로 넘어가게 설정해주었고

작동도 잘 되지만...

Calculator class가 사용되지않는게 찜찜하다..ㅠ

다시 공부해보고 수정해봐야할듯

하루의 마무리

오늘은 집중이 잘 안되서 강의를 보면서 계속 내용 정리를 해보았습니다. 강의는 완독했지만 계속

들어보면서 구조와 제가 모르는 단어 연관시켜서 정리해야겠고 이번주도 잘 마무리된 것 같습니다.

아프지말고 항상 화이팅!

내일은 결혼식가서 많이 먹어야징😊

0개의 댓글