Foundation
fun main() {
println("Hello, World")
}
- fun 은 함수를 선언하는데 사용된다.
- fun main()는 프로그램이 시작되는 곳
- 함수의 본문은 중괄호 안에 작성 { }
- println(), print() 는 인수를 표준 출력으로 인쇄
Variable
코틀린의 변수는 2가지 종류가 존재
- 읽기 전용 변수 : val
- 변경 가능한 변수 : var
- main() 프로그램 시작 시 함수 외부에서 변수를 선언 가능 → top level variable
- var 변수는 선언 후 다시 할당 가능
- 기본적으로 val 변수를 선언, var 변수는 필요한 경우 선언
String templates
String Templates를 사용해 변수의 내용을 표준 출력으로 사용 가능
Template 표현식을 사용하여 이를 수행 가능
문자열은 큰 따옴표(”)로 묶인 일련의 문자
Template 표현식은 달러 기호로 시작 한다.($)
val customer = 10
println("$customer") // 10
println("${customer + 1}") // 11
템플릿 표현식 변수를 핸들링 하려면 ${variable_name}으로 변수 핸들링 가능
- 또한 코틀린은 type 추론이 가능하다. 위 예제에선 Int로 추론
Basic Types
코틀린의 모든 변수와 데이터 구조에는 data type이 존재
data type은 해당 변수나, 데이터 구조로 수행할 수 있는 작업을 컴파일러에게 알려준다.
위의 예제서 Int 유형을 추론한 것을 type inference 라고 한다.
즉, customer에 정수 값이 할당되고, Kotlin은 customer가 숫자 유형이라고 추론, 결과적으로 컴파일러는 해당 부분에 산술 연산을 수행 할 수 있다고 판단
Category Basic Types
Integers | Byte, Short, Int, Long |
Unsigned Integers | UByte, UShort, UInt, ULong |
Floating-point numbers | Float, Double |
Booleans | Booleans |
Characters | Char |
Strings | String |
Integers
명시적인 유형 지정 없이 변수를 선언하면, 컴파일러는 해당 값을 기준으로 범위에 들어갈 수 있는 유형을 자동으로 유추한다.
기본적으로 Int로 유추한다.
만약 Int의 범위가 초과된다면, Long 유형으로 유추된다.
Long 을 명시적으로 지정하려면 ‘L’ 키워드를 정수 옆에 붙인다.
Floating-Point Numbers
- Float은 단정 밀도를 반영
- Double은 배정 밀도를 반영
- Float 은 32bytes, Si
Variable
- 코틀린은 컴파일러가 타입을 추론해주기 때문에 타입 생략이 가능하다.
- 지연 할당 : 변수를 선언하고, 나중에 값을 할당하는 것
- 지연 할당 시에는 타입 생략이 불가능
- val : value : 상수
- var : variable : 변수
- 변수의 타입이 한번 할당되면, 다른 타입으로 할당이 불가능하다.
- 코틀린은 클래스나, 함수에 속해 있지 않아도 변수를 선언할 수 있다.
- 이를 Top Level 변수라고 한다.
- top level 변수는 private static 으로 선언된다.
- top level 변수는 get/set이 자동으로 할당된다.
Function
//기본적인 함수 선언 스타일
fun sum(a: Int, b: Int): Int {
return a + b
}
//표현식 스타일
fun sum2(a:Int, b:Int) : Int = a +b
//표현식 & 반환타입 생략
fun sum3(a:Int, b:Int) = a + b
//몸통이 있는 함수는 반환 타입을 제거하면 컴파일 오류
fun sum4(a :Int, b:Int) : Int {
return a +b
}
//반환 타입이 없는 함수는 Unit을 반환한다.
fun printSum(a: Int, b: Int) {
println("$a + $b = ${a + b}")
}
//디폴트 파라미터
fun greeting(message: String = "안녕하세요!") {
println(message)
}
- 함수를 선언하면 public static final 으로 선언된다
- 몸통이 있는 함수(”{}”)는 반환 타입을 제거하면 컴파일 오류
- Unit 반환 형과 main은 final이 붙지 않고, static 만 붙는다
Flow Control
fun main() {
//if..else 사용
val job = "Software Developer"
if (job == "Software Developer") {
println("개발자")
}else{
println("개발자아님")
}
//코틀린의 if..else는 표현식
val age : Int = 10
val str = if (age > 10) {
"성인"
}else{
"아이"
}
println(str)
//코틀린은 삼항 연산자가 없다. if..else가 표현식이므로 불필요
val a = 1
val b = 2
val c = if(b > a) b else a
println(c)
}
- if..else 는 표현식 이므로 return이 필요 없다.
- if..else가 표현식 이므로 코틀린은 삼항 연산자가 없다.
- 코틀린에서는 원시 타입을 비교할 때는 == 연산자는 동등성을 비교한다.
- 코틀린에서는 참조 타입을 비교할 때는 == 연산자는 내부적으로 equals()를 호출한다.
- 코틀린에서는 참조 타입의 동일성을 비교 하고 싶으면 === 연산자를 사용한다.
fun main() {
//자바 switch -> 코틀린 when
val day = 2
val result = when(day){
1 -> "월요일"
2-> "화요일"
3 -> "수요일"
4 -> "목요일"
else -> "기타"
}
println(result)
//else를 생략 가능
when (getColor()) {
Color.RED -> println("red")
Color.GREEN -> println("green")
}
//여러개의 조건을 콤마로 구분해 한줄에서 정의 가능
when (getNumber()) {
0, 1 -> println("0 또는 1")
else -> println("0 또는 1이 아님")
}
}
enum class Color{
RED, GREEN
}
fun getColor() = Color.RED
fun getNumber() = 2
- when 문을 Java로 변환하면 switch & break 문으로 변환된다.
- enum 처럼 값의 범위가 정해져 있다면 else 생략 가능, 범위가 정해져 있지 않다면 else 를 생략하면 컴파일 에러 발생
- 여러 개의 조건을 콤마로 구분해 정의 가능
fun main() {
//범위 연산자 .. 를 사용해 for loop
for (i in 0..3) {
println(i)
}
//until을 사용해 반복
//뒤에 온 숫자는 포함하지 않음
//until은 ..< 으로 표현이 가능하다.
//unitl은 자바로 변환되면 for 문을 사용해 반복한다.
//..<은 자바로 변환되면 while 문을 사용해 반복한다.
for (i in 0 until 3) {
println(i)
}
for (i in 0 ..< 3) {
println(i)
}
//step 에 들어온 값 만큼 증가
//step을 사용하면 자바의 while 문으로 변환되어 반복된다.
for (i in 0..6 step 2) {
println(i)
}
//dowTo를 사용해 반복해서 값을 감소
for (i in 3 downTo 1) {
println(i)
}
//전달받은 배열을 반복
val numbers = arrayOf(1,2,3)
for (i in numbers) {
println(i)
}
}
- 범위 연산자 (..)를 사용하여 for loop를 진행시킨다.
- 자바로 변환되면 for 문으로 변환된다.
- until을 사용하면 마지막 반복을 포함하지 않음
- 자바로 변환되면 for 문을 사용해 반복 but 그냥 범위 연산자보다 코드가 길어짐
- until은 ..< 으로 표현 가능, 이때는 자바로 변환되면 while 문으로 변환
- step을 사용하여 증가량 설정 가능
- step을 사용하면 자바의 while 문으로 변환된다.
- downTo를 사용하여 역순으로 반복 가능
- 자바의 for문으로 변환된다.
- 전달받은 배열, 리스트를 반복할 수 있다.
- 자바의 for-each와 동일
- 배열은 자바 기본 for 문으로 변환되고, index 접근을 시도한다.
- ArrayList는 Iterator로 변환되고, while(hasNext())를 통해 접근을 시도한다.
- 매번 돌때마다 Null 체크를 한다.
fun main() {
//자바의 while 문과 동일
//조건을 확인하고 코드 블록을 실행한 후 다시 조건을 확인
var x = 5
while (x > 0) {
println(x)
x--
}
var y = 4
do {
println(y)
}while (y -- > 0)
}
- while문 내부에서 조건을 변경할 경우 자바의 for 문으로 변환된다.
- while문 조건 검사 식 내부 ‘( )’ 에서 조건을 변경할 경우 while로 대체
- do-while 문은 자바의 do-while문으로 그대로 변환된다.
Null 안전성
- 자바를 포함한 많은 프로그래밍 언어에서 가장 많이 발생하는 예외 유형이 NullPointerException, NPE 이다.
- null 을 발명한 토니호어는 실수라고 고백
- 자바에서는 NPE를 줄이기 위해 JDK8 부터 Optional을 지원
- 자바의 Optional은 값을 래핑하기 때문에, 객체 생성에 따른 오버헤드가 발생하고, 컴파일 단계에서 Null 가능성을 검사하지 않는다.
- 코틀린은 Null 가능성을 컴파일러가 미리 감지해서 NPE 가능성을 줄일 수 있다.
fun main() {
// val a : String = null //컴파일 오류
// var b : String = "var"
// b = null //컴파일 오류
// 타입 뒤에 ? 를 붙여 null 가능성을 컴파일러에 알려준다.
// nullable 타입으로 할당 후, 해당 변수에 접근하려면 안전 연산자를 사용해야 한다.
var a : String? = null
println(a?.length)
//기존 자바 스타일 if..else + 표현식 사용하여 null 체크
val b :Int = if(a != null) a.length else 0
println(b)
//엘비스 연산자 : 좌변이 null인경우 우변을 리턴, null이 아니면 좌변을 리턴
val c = a?.length ?: 0
println(c)
}
- 코틀린인 일반 참조형에 null 할당이 불가능하다. 컴파일 에러가 발생한다.
- null 할당을 하려면 참조형 타입에 (?) 을 붙여야 한다.
- nullable 타입으로 할당 후, 해당 변수에 접근하려면 안전 연산자를 사용해야 한다.
- 변수?.메서드
- 변수!!.메서드 : 단언 연산자
- 엘비스 연산자를 사용해 기본값을 할당할 수 있다.
- 기본값을 할당하지 않으면 null 이 반환된다.
fun getNullStr(): String? = null
fun getLengthIfNotNull(str: String?) = str?.length ?: 0
fun main() {
val nullableStr = getNullStr()
val nullableStrLength= nullableStr?.length
println(nullableStrLength)
val length = getLengthIfNotNull(nullableStr)
println(length)
}
- 코틀린에서 직접 throws NullPointerException을 호출하여 NPE를 발생시킬 수 있다.
- NPE의 발생 가능성을 줄여줄 뿐이지, 없어지지는 않는다.
- 엘비스 연산자를 통해 기본 값으로 예외를 throws 할 수 있다.
- !! 단언 연산자를 사용하여 null 값을 강제로 참조하게 되면, NPE를 throws 한다.
- 즉, 단언 연산자는 개발자가 직접 핸들링한다고, 컴파일러에게 알린다.
- 안전 연산자를 사용해야 NPE 에러 발생 빈도가 줄어든다. 출력하면 그냥 null로 출력된다.
- 코틀린과 자바를 혼용할 때, 코틀린에서는 null 체크가 되지만, 코틀린 컴파일러는 자바코드의 null 체크를 하지 못한다. 따라서 안전 연산자와 엘비스 연산자를 잘 사용해야 한다.
Exception Handling
- 코틀린의 모든 예외 클래스는 최상위 예외 클래스인 Throwable을 상속한다.
- Throwable → Error, Exception
- Error → Unchecked errors
- Exception → RuntimeException, Checked exception
- RuntimeException → Unchecked exceptions
- Error : 시스템에 비정상적인 상황이 발생, 예측이 어렵고 기본적으로 복구가 불가능함
- OutOfMemoryError, StackOverflowError
- Exception : 시스템에서 포착 가능하며(try-catch) 복구 가능, 예외 처리 강제
- IOException, FileNotFoundException
- @Transactional에서 해당 예외가 발생하면 기본적으론 롤백이 동작하지 않음
- rollbackFor를 사용해야 한다.
- RuntimeException : 런타임시에 발생하는 예외, 예외 처리를 강제하지 않음
- NullPointerException, ArrayIndexOutOfBoundsException
- 자바에서 체크드 익셉션은 컴파일 에러가 발생하기 때문에 무조건 try-catch로 감싸거나, throws로 예외를 전파해야 한다.
- 코틀린에서는 체크드 익셉션을 강제하지 않는다.
fun main() {
Thread.sleep(1)
}
- 필요한 경우 try-catch를 통해 감쌀 수 있다.
- 코틀린에서도 finally 구문을 지원한다.
- 코틀린에서는 try-catch는 표현식으로 작성이 가능하다.
val a = try {
"1234".toInt()
} catch (e: Exception) {
println("예외 발생")
}
println(a)
throw Exception("예외 발생")
failFast("예외 발생")
fun failFast(message: String): Nothing {
throw IllegalArgumentException(message)
}
- 코틀린은 이렇게 직접 Exception을 발생시킬 수 있다.
- Exception 같이 코드가 정상적으로 수행되지 않는 경우, Unit 타입이 아닌, Nothing Type을 반환한다.
val b :String? = null
val c = b ?: failFast("a is null")
println(c.length)
- 엘비스 연산자와 Nothing 타입을 같이 사용하면 절대로 Null이 나올 수 없기 때문에 컴파일러는 해당 코드는 널 안전 연산자를 사용하지 않아도 된다고 판단한다.
'Back-end' 카테고리의 다른 글
Spring @Async + ThreadPoolTaskExecutor (0) | 2024.01.14 |
---|---|
Spring Security Filter (0) | 2024.01.01 |
JPA Entity 기본 생성자 (0) | 2023.09.29 |
기본 키 할당 전략 (0) | 2023.09.29 |
Wrapper Type vs Primitive Type In JPA (0) | 2023.09.29 |