CodeKata
deprecated된 내용
toUpperCase -> uppercase
toLowerCase -> lowercase
caplitalize -> replaceFirstChar
과제 피드백
과제 피드백을 받을 때마다 감동이다..
모르는건 질문할 수 있겠지만 프로젝트 전체를 봐주신다는 건 힘든 일이다.
내 쓰레기같은 코드를 하나씩 봐주신다는 것이 너무 영광이다.
1. isEmpty vs isBlank
isEmpty
문자열의 길이가 0인지 확인한다. 문자열이 아무 문자도 포함하고 있지 않은 경우에 true를 반환한다. 하지만! 공백 문자열(””)은 포함하지 않는다.
isBlank
문자열이 비어 있거나 공백 문자만 포함하고 있는지 확인한다. 그러므로 문자열이 공백 문자로만 이루어져 있는 경우에도 true를 반환한다.
비교해보자면 isEmpty는 문자열의 길이를 확인하고 isBlank는 공백 문자를 포함한 상태를 확인한다.
..어쩐지 과제 해설을 보니까 trim()을 사용했다. " "는 생각하지 못했다.
이렇게 큰 차이가 있는지 모르고 사용했었다.
2. @SuppressLint("SetTextl18n")
어디서 사용했냐면 로그인 시에 HomeActivity로 넘어갈 때 아이디 값을 가져가는 경우인데
text 값을 하드코딩으로 넣어주었다.
그랬더니 노란색으로 전부떠서 확인해보니 해당 줄이나 메서드에 대한 특정 경고를 무시할 수 있는 annotation을 찾았다.
바로 @SuppressLint("SetTextI18n")이다.
TextView에 setText() 메서드를 사용할 때 지역화 문자열(I18n)을 사용하는 것을 권장하는데 이를 무시하고자 사용하는 것이다.
그러면 하드코딩은 안하면 된다.
string으로 값을 넣어주거나 직접 데이터를 넣으면 된다.
사실 이렇게까지는 몰랐는데 art + enter 누르니까 저런 annotation이 나왔고 검색해보니 권장은 안하지만 괜찮다라는 식이여서 사용한 것이다..
이제부터라도 알았으니 저런 annotation은 사용은 안하는 걸로 해야겠다!
3. visibility modifier
선언에 대한 접근 권한을 제어
public, internal, protected, private가 있다는 건 kotlin in action 책을 통해 배웠?어서 알고는 있다.
솔직히 생략하고 사용하면 기본적으로 public으로 된다는 건 기억난다.
일단 피드백에서 해주신 내용을 보면 내가 만든 randomNum 함수를 private로 해줘야한다고 해주셨다.
왜일까?
내가 알고있는 선에서 먼저 말하자면
일단 내가 만든 randomNum은 생성한 클래스 내부에서만 사용을 할 것이기 때문이다.
찾은 내용을 바탕으로 설명하면
기본적으로 public으로 선언되니까 어느 곳이든 접근이 가능한데 그럴 필요가 없으면 private으로 바꿔주는게 맞다.
해당 함수를 클래스 안에서만 사용할꺼면 private로 선언해서 클래스 내부에서만 사용되도록 가시성을 좁히는 것이 좋다.
다른 함수를 만들 때도 사용 목적, 구조에 따라 신경써서 만들어줘야한다.
Kotlin 문법
배운 내용
1. 산술 연산자
Kotlin에서는 특정 연산자에 대해 클래스에 대한 사용자 정의 동작을 정의할 수 있다.
operator overloading이라고 한다.
- operator 키워드: 사용자 정의 연산자를 정의할 때 사용됩니다. 이 키워드를 사용하여 특정 함수를 연산자로 사용할 수 있도록 지정합니다.
- plus() 함수: "+" 연산자를 사용할 때 호출되는 함수를 정의할 때 사용됩니다.
- equals() 함수: "==" 연산자를 사용할 때 호출되는 함수를 정의할 때 사용됩니다.
- compareTo() 함수: Comparable 인터페이스와 함께 사용되며, 두 객체를 비교할 때 호출되는 함수를 정의할 때 사용됩니다.
class Point(val x: Int, val y: Int) {
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
fun main() {
val point1 = Point(3, 4)
val point2 = Point(1, 2)
val result = point1 + point2 // "+" 연산자를 사용하여 plus() 함수 호출
println(result) // 출력: Point(x=4, y=6)
}
class Point(val x: Int, val y: Int) {
// plus 연산자의 사용자 정의
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
// equals 연산자의 사용자 정의
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Point
if (x != other.x) return false
if (y != other.y) return false
return true
}
// compareTo 함수의 사용자 정의
operator fun compareTo(other: Point): Int {
return compareValuesBy(this, other, { it.x }, { it.y })
}
}
fun main() {
val point1 = Point(3, 4)
val point2 = Point(1, 2)
val result = point1 + point2 // plus 연산자 사용
println(result) // 출력: Point(x=4, y=6)
println(point1 == point2) // equals 연산자 사용
// compareTo 함수 사용
println(point1.compareTo(point2)) // 출력: 1 (point1이 크다는 것을 의미)
}
식 | 함수 이름 |
a * b | times |
a / b | div |
a % b | rem(1.1이전 mod) |
a + b | plus |
a - b | minus |
Coroutines
코루틴 공부를 시작했다. 한번에 빡 할 생각보단 천천히 공부해 나갈 것이다.
코루틴에 대한 개념이 아예 없다.
delay나 sleep를 사용한게 끝인데 심지어 코루틴도 아니다..
오늘 배운내용으로 thread에 대해 먼저 배웠다.
1. Thread 함수와 사용자 스레드/데몬 스레드를 배웠다.
import kotlin.concurrent.thread
fun main() {
println("메인 스레드 시작")
thread(isDaemon = false) {
println("새로운 스레드 시작")
Thread.sleep(2000L)
println("새로운 스레드 종료")
}
Thread.sleep(1000L)
println("메인 스레드 종료")
}
2. Thread의 문제점
- Thread 클래스를 상속한 클래스를 인스턴스화해 실행할 때마다 매번 새로운 스레드가 생성된다. 스레드는 생성 비용이 비싸기 때문에 매번 새로운 스레드를 생성하는 것은 성능적으로 좋지 않다.
- 프로그램의 복잡성이 증가하며, 실수로 인해 오류나 메모리 누수를 발생시킬 가능성이 증가한다.
3. Executor 프레임웍
- Executor 프레임웍은 스레드를 생성하고 관리하는 데 스레드 풀(Thread Pool)이란 개념을 사용한다.
- Executor 프레임웍은 작업 처리를 위해 스레드풀을 미리 생성해 놓고 작업을 요청 받으면 쉬고 있는 스레드에 작업을 분해한다.이를 통해 스레드풀에 속한 스레드의 생성과 관리 및 작업 분배에 대한 책임을 Executor 프레임웍이 담당하므로 개발자느 ㄴ더 이상 스레드를 직접 다루거나 관리하지 않아도 된다.
- 각 스레드가 작업을 끝내더라도 스레드를 종료하지 않고 다음 작업이 들어오면 재사용한다.
- 스레드 풀은 스레드의 집합이며, 여기에 스레드풀을 관리하고 사용자로부터 요청받은 작업을 각 스레드에 할당하는 시스템을 더한 것이 Executor 프레임웍이다.
사용자가 사용할 수 있는 함수는 크게 2가지이다.
- 스레드풀을 생성하고 생성된 스레드풀을 관리하는 객체를 반환받는 함수 >newFixedThreadPool
- 스레드풀을 관리하는 객체에 작업을 제출하는 함수
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
fun main() {
var startTime = System.currentTimeMillis()
val executorService: ExecutorService = Executors.newFixedThreadPool(2)
executorService.submit {
println("${getElapsedTime(startTime)} 작업1 시작")
Thread.sleep(1000L)
println("${getElapsedTime(startTime)} 작업2 종료")
}
executorService.submit {
println("${getElapsedTime(startTime)} 작업2 시작")
Thread.sleep(1000L)
println("${getElapsedTime(startTime)} 작업2 종료")
}
executorService.shutdown()
}
fun getElapsedTime(startTime: Long): String =
"지난 시간: ${System.currentTimeMillis() - startTime}ms"
출력
지난 시간: 4ms 작업2 시작
지난 시간: 4ms 작업1 시작
지난 시간: 1030ms 작업2 종료
지난 시간: 1030ms 작업2 종료
4.launch를 사용해 코루틴 추가로 실행하기
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
fun main() = runBlocking<Unit> {
println("${Thread.currentThread().name} hello")
launch {
println("${Thread.currentThread().name} hello")
}
launch {
println("${Thread.currentThread().name} hello")
}
}
main @coroutine#1 hello
main @coroutine#2 hello
main @coroutine#3 hello
5. 그래서 뭐가 코루틴인데? (코루틴 이름 지정 완료)
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
fun main() = runBlocking<Unit>(context = CoroutineName("Main")) {
println("${Thread.currentThread().name} hello")
launch(context = CoroutineName("노동1")) {
println("${Thread.currentThread().name} hello")
}
launch(context = CoroutineName("노동2")) {
println("${Thread.currentThread().name} hello")
}
}
출력
main @Main#1 hello
main @노동1#2 hello
main @노동2#3 hello
코루틴 공부는 '코틀린 코루틴의 정석' 책으로 진행하고 있다.
'Kotlin > TIL' 카테고리의 다른 글
TIL (04.02) (1) | 2024.04.02 |
---|---|
TIL (04.01) (0) | 2024.04.01 |
TIL (03.28) (0) | 2024.03.28 |
TIL (03.27) (1) | 2024.03.27 |
TIL (03.26) (0) | 2024.03.26 |