자료형 변환 시, 숫자 자료형끼리는 to자료형() 메소드를 사용하면 되는데, 만약 문자열을 숫자로 변경할 때는 별도의 메소드가 필요하다.
👉 숫자 형태의 문자열을 숫자로 변환할 때는 Integer.parseInt(문자열) 사용
각 캐스팅에 대해 예시를 통해 조금 더 자세히 정리해보자.
open class Drinks{
var name :String = "음료수"
open fun Drink(){
println("${name}을 마십니다.")
}
}
class Cola : Drinks(){
var type :String = "콜라"
override fun Drink(){
println("${name}인 ${type}을 마십니다.")
}
fun Taste(){
println("${type}향이 납니다.")
}
}
Up-Casting은 하위클래스가 상위클래스화 되는 것으로 위의 예시에서의 Cola클래스인 자식클래스가 Drinks 클래스인 부모클래스화 되는 것을 말한다.
var A:Drinks = Cola()
Down-Casting은 상위클래스가 하위클래스화 되는 것으로 위의 예시에서의 Drinks 클래스인 부모클래스가 Cola 클래스인 자식클래스화 되는 것을 말한다.
var B:Cola = Drinks()
Cola 인스턴스가 가져야 할 Taste 함수를 Drinks로 만든 인스턴스는 가지고 있지 않기 때문에 타입 에러 발생 ❗
❗ 주의 ❗
부모클래스에서 자식클래스로 강제 변환은 에러 발생
is 키워드를 사용해서 자료형의 타입을 확인할 수 있다. A is B 의 형태로 확인
if(name is String) {
println("이름의 자료형은 String 이다. ")
메소드는 기본적으로 하나의 데이터를 리턴한다. 만약 두 개 이상의 데이터를 포함하는 데이터 클래스를 설계하고 인스턴스를 리턴하면 가능하지만 매번 불필요한 클래스를 만드는 것은 비효율적 !!
복수 데이터를 리턴하는 방법으로는 Pair과 Triple 키워드를 사용하면 된다.
fun main() {
var chicken = Chicken()
var eggs = chicken.getEggs()
var listEggs = eggs.toList()
var firstEgg = listEggs[0]
var secondEgg = listEggs[1]
var eggTime = listEggs[2]
println("달걀의 종류는 ${eggs} 입니다.")
println("리스트 달걀의 종류는 ${listEggs} 입니다.")
println("첫번째 달걀의 종류는 ${firstEgg} 입니다.")
println("두번째 달걀의 종류는 ${secondEgg} 입니다.")
}
class Chicken {
fun getEggs(): Pair<String, String> {
var eggs = Pair("달걀", "맥반석")
return eggs
}
}
fun getThreeEggs(): Triple<String, String, Int> {
var eggs = Triple("달걀", "맥반석", 20230101)
return eggs
}
}
Scope Function 기능을 이용해서 자기 자신의 객체를 전달해서 효율적인 처리가 가능하고 이는 객체를 사용할 때 임시로 Scope를 만들어서 코드 작성을 편리하게 만들어준다.
👉 left function
중괄호 블록 안에
it으로 자신의 객체를 전달하고 새행된 결과를 반복한다.
var strNum = "10"
// result 변수에 strNum이 null이 아니라면 자신의 객체(strNum)을 정수형으로 변환
var result = strNum?.let{
Integer.parseInt(it)
}
println(result!!+1)
👉 with function
중괄호 블록 안에
this로 자신의 객체를 전달하고 코드 수행
(단, this는 생략 사용이 가능하기 때문에 null이 아닐 경우에만 사용할 것 !!)
var alphabets = "abc"
with(alphabets) {
// var result = this.subSequence(0, 2) this는 생략 가능
var result = subSequence(0, 2)
println(result)
}
👉 also function
중괄호 블록 안에
it으로 자신의 객체를 전달 후 객체 반환,apply와 함께 자주 사용됨.
var student = Student("참새", 10)
var result = student?.also {
it.age = 50
}
👉 apply function
중괄호 블록 안에
this로 자신의 객체를 전달하고 객체 반환, 주로 객체 상태를 변화시키고 바로 저장할 때 사용
var student = Student("참새", 10)
var result = student?.also {
student.age = 50
}
👉 run function
객체에서 호출하지 않는 경우 / 객체에서 호출하는 경우
var totalPrice = run {
var computer = 100
var mouse = 50
computer + mouse
}
println("총 가격 : ${totalPrice}")
fun main() {
var student = Student("참새", 10)
student?.run {
displayInfo()
}
}
뭔가 it을 사용하기도 하고 this를 사용하기도 하고.. 그냥 it으로 통합해서 사용하면 안되나?? 절대 안됩니다.
모든 수신객체를 it으로 사용하게 되면 Child Function에서 Shadow가 되어 있어 제대로 참조가 불가능할 수 있는 문제가 발생할 수 있기 때문입니다.
| Scope에서 접근방식 this | Scope에서 접근방식 it | |
|---|---|---|
| 블록 수행 결과를 반환 | run, with | let |
| 객체 자신을 반환 | apply | also |
확장함수를 이용해서 기존 클래스에 쉽게 메소드를 추가할 수 있는데, 이는 원하는 메소드가 존재하지만 내가 설계한 클래스가 아닐떄, 외부에서 메소드를 관리한다.
확장함수으 내부 구현시 this 키워드를 사용해서 확장 대상이 될 클래스가 가지고 있는 public 인스턴스에 접근하는 객체를 이용해준다. 확장함수를 만들 때 이 객체에 .을 붙여서 새로운 함수를 만들어주면 된다.
여러가지의 로직들이 완료여부에 관계없이 실행되는 방식으로 예를 들어 밥솥으로 밥을 하면서 국을 끓여 식사준비를 동시에 하는 행동 등, 순차적으로 이루어지는게 아닌 동시에 여러가지 행동을 하는 것을 말한다.
- 동기 : 한 가지씩 작업 처리
- 비동기 : 다양한 일을 한꺼번에 수행
프로그램은 하나의 메인 쓰레드인 실행흐름이 존재하고, 로직을 동시에 실행할 수 있도록 도와주고, thread 키워드로 쓰레드를 생성할 수 있다.
프로세스 안에서 더 작은 작업의 단위를 쓰레드라고 부르고, 생성되서 수행할 때 각 독립된 메모리 영역인 stack 을 가진다.
fun mani() {
thread(start = true) {
for(i in 1..10) {
println("현재 숫자 : ${i}")
runBlocking {
launch {
delay(1000)
}
}
}
}
}
hread(start = true) {
for(i in 500..60) {
println("현재 숫자 : ${i}")
runBlocking {
launch {
delay(1000)
}
}
}
}
}
}
위의 예시처럼 Thread1, Thread2가 있을 때 순서가 보장되지 않고 출력된다.
아까 정리했던 비동기, 코루틴을 이용하면 최적화된 비동기 함수를 사용해 안정적인 동시성과 비동기 프로그래밍을 확보할 수 있다.
launch와 async 빌더를 가장 많이 사용한다. launch는 결과값이 없는 코루틴 빌더로 Job 객체로 코루틴을 관리, async는 결과값이 있는 코루틴으로 Deffered 타입으로 값을 리턴해준다.
쓰레드와 코루틴은 둘 다 동시성 프로그래밍을 위한 기술로 동시성 프로그래밍을 위한 기술이라는 공통점을 가지고 있다.
그렇다면, 차이점은 무엇일까?
쓰레드 👾
코루틴 🤖
쓰레드나 코루틴은 각자의 방법으로 동시성을 보정하나, 코루틴은 하나의 Thread를 더 잘게 쪼개서 사용하는 기술로 CPU 자원이 절약된다.