숫자 자료형끼리는 .to자료형() 메소드를 활용할 수 있다.
문자열을 숫자로 변경할때에는 별도의 메소드가 필요하다.

위 이미지와 같이 strNum로 변수를 지정하고 자료형 메소드로 지정하거나
Integer.을 통해 변환을 해줄 수 있다.

객체 자료형간의 변환

- birds.add(Sparrow(name) 단락을 통해 sparrow라는 객체를 생성하고
for(bird in birds) { bird.fly()단락을 통해 호출하는데 전과 다르게 bird fly()는 하나 밖에 없음에도 두개의 메세지가 출력된다.
sparrow는 bird의 클래스를 상속받고 있기때문에 업캐스팅을 통해 각각 가지고 있는 name으로 출력된다.
반대인 다운 캐스팅은 부모 클래스에서 자식 클래스로 정보를 전달한다는 개념인데
bird의 정보를 sparrow가 전부 가지고 있지 않아 다운 캐스팅은 불가하다.
자료형의 확인
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)
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는 결과값이 있는 코루틴이다.
하나의 쓰레드 안에 여러개의 코루틴을 정해서 쓸때 사용하는 개념으로는 다음과 같이 있다.
쓰레드와 코루틴은 둘 다 동시성 프로그래밍을 위한 기술이다
쓰레드와 코루틴의 차이는 다음과 같다.
쓰레드
- 쓰레드 작업 하나의 단위 : 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 라고 한다.
강의 마무리.
.
.
.
.
.




작동도 잘 되지만...
Calculator class가 사용되지않는게 찜찜하다..ㅠ
다시 공부해보고 수정해봐야할듯
오늘은 집중이 잘 안되서 강의를 보면서 계속 내용 정리를 해보았습니다. 강의는 완독했지만 계속
들어보면서 구조와 제가 모르는 단어 연관시켜서 정리해야겠고 이번주도 잘 마무리된 것 같습니다.
아프지말고 항상 화이팅!
내일은 결혼식가서 많이 먹어야징😊