들어가면서
코딩테스트, 스프링 백엔드 개발에서 자바로 프로그래밍을 해왔다.
최근 채용 공고를 보면 `스프링 + 코틀린 (코프링)`이 심심치 않게 보인다. 자바에서 현대적인 언어로 업그레이드 된 언어가 코틀린이라고한다.
따라서 코틀린의 문법을 배우고 더 나아가 간단한 API를 코틀린으로 만들어보는 실습까지 할 계획이다.
다행인 점은 자바를 사용해오던 개발자라면 코틀린 사용이 더 수월할 것이다.
코틀린 파일 생성
인텔리제이에서 Kotlin 파일 또는 클래스를 생성하는 방법은 Java 클래스를 생성하는 방법과 유사하다.
Kotlin은 클래스가 필수적이지 않기 때문에, 단순히 파일로 생성할 수 있다. 나는 "Main"이라는 이름으로 Kotlin 파일을 만들었다.
변수
fun main() {
var i = 10
var name = "주주"
var point = 5.5
var j : Int = 10
var loc : String = "서울시"
}
코틀린은 클래스 없이도 `main 함수만으로` 프로그램을 구성할 수 있다. 이는 코틀린의 스크립팅 언어 특성을 보여준다.
코틀린은 타입을 추론하는 기능을 가지고 있어서 별도로 타입을 지정해주지 않아도 되지만, 명시적으로 타입을 지정할 수도 있다.
코틀린에서는 모든 타입들이 이렇게 `레퍼런스 타입`을 가지고 있다.
자바의 레퍼런스 타입처럼 대문자로 시작하고있다.
코틀린의 타입 시스템 정리
자바에서는 int와 같은 기본 타입(Primitive type)과 Integer 같은 참조 타입(Reference type)을 구분했다. 기본 타입은 메모리 효율성과 성능을 위해 사용되었고, 참조 타입은 객체로 다루어져 컬렉션이나 null을 허용하는 상황에서 사용했다.
반면, 코틀린에서는 모든 타입을 객체로 다루면서도, 내부적으로 JVM 최적화를 통해 기본 타입처럼 동작하도록 했다. 그래서 코틀린의 Int는 자바의 int처럼 보이지만 사실 객체로 동작하며, `Int?`는 `nullable 타입`으로 null을 허용하는 참조 타입처럼 사용할 수 있다.
이렇게 코틀린은 기본 타입과 참조 타입을 통일된 방식으로 다루면서도, 자바의 기본 타입처럼 성능을 유지하는 장점을 제공했다. 타입 시스템을 통일해 일관성을 높이고, nullable과 non-nullable을 명확히 구분해 NullPointerException(NPE)을 줄일 수 있었다.
변경 불가능한 변수
fun main() {
var j : Int = 10
var loc : String = "서울시"
j = 20 // 변수는 재 대입이 가능
val num = 20
// num = 30 // 재 대입이 불가능
}
var와 val의 차이점
- var는 `변경 가능한 변수`를 선언할 때 사용된다.
- 값이 자주 변경될 수 있는 변수에 사용될 수 있다. 예를 들어, 사용자 입력이나 상태 값이 변경될 수 있는 경우가 이에 해당할 수 있다.
- val은 `변경 불가능한 변수`를 선언할 때 사용된다.
- 값이 한 번 할당되면 변경될 필요가 없는 변수에 사용될 수 있다. 예를 들어, 설정 값이나 상수로 사용되는 변수에 적합할 수 있다.
톱 레벨 상수 const
const val num = 20 // main 보다 먼저 컴파일 되는
fun main() {
var i = 10
var name = "주주"
var point = 5.5
var j : Int = 10
var loc : String = "서울시"
j = 20 // 변수는 재 대입이 가능
}
`const val` 은 컴파일 타임 상수로, 반드시 `톱레벨`에 선언되거나 `객체 내`에 선언되어야 한다. 이는 런타임 상수가 아닌 컴파일 타임에 결정된 값을 의미한다.
- `val`은 불변 변수지만, `런타임`에 값이 결정되는 반면,
- `const val`은 `컴파일 타임`에 값이 결정된다.
const val로 선언된 상수는 컴파일 타임 상수이므로, `바이트코드에 직접 포함`되어 런타임에 메모리 할당이나 초기화가 필요하지 않다.
이에 따라 `main 함수 내부에 선언된 변수보다 성능이 더 우수`할 것이다.
형변환
fun main() {
var i = 10
var l = 20L
l = i.toLong() // Int를 Long으로 변환
i = l.toInt() // Long을 Int로 변환
var name = " "
name = i.toString() // Int를 String으로 변환
}
String
fun main() {
// 변수 선언 및 초기화
var name = "ZooPuter"
// 문자열을 대문자로 변환
println(name.uppercase()) // ZOOPUTER
// 문자열을 소문자로 변환
println(name.lowercase()) // zooputer
// 문자열의 첫 번째 문자 접근
println(name[0]) // Z
// 문자열 연결 (String Concatenation)
println("제 이름은 " + name + "입니다.") // 제 이름은 ZooPuter입니다.
// 문자열 템플릿 사용
println("제 이름은 ${name + "님"} 입니다.") // 제 이름은 ZooPuter님 입니다.
}
문자열 연결: `println("제 이름은 " + name + "입니다.")`
- 문자열을 연결할 때 + 연산자를 사용할 수 있다.
- 이 방식은 직관적이지만, 문자열 템플릿을 사용하는 것이 더 권장된다.
문자열 템플릿: `println("제 이름은 ${name + "님"} 입니다.")`
- ${}를 사용하여 문자열에 변수를 삽입할 수 있다.
- 복잡한 표현식도 중괄호 안에 작성할 수 있어 더욱 가독성이 좋다.
max, min
import java.lang.Integer.max
fun main() {
var i = 10
var j = 20
// Math 클래스의 max 메서드를 사용하여 최대값을 구함
print(Math.max(i, j)) // 결과: 20
// Integer 클래스의 max 메서드를 사용하여 최대값을 구함
print(max(i, j)) // 결과: 20
}
코틀린에서는 자바의 Math.max()와 Integer.max()를 활용해 두 숫자 중 최대값을 구할 수 있다. Math.max()는 더 일반적으로 사용되지만, `Integer.max()를 직접 import`해서 사용하는 것도 코드의 `간결성`을 높일 수 있는 방법이다.
random
fun main() {
var randomIntNum = Random.nextInt()
println(randomIntNum)
randomIntNum = Random.nextInt(0, 100)
println(randomIntNum)
var randomDoubleNum = Random.nextDouble(0.0, 1.0)
println(randomDoubleNum)
}
`nextInt()`는 정수를, `nextDouble()`은 실수를 랜덤으로 생성하며, 원하는 범위를 지정할 수 있다. 이를 통해 `게임`, `테스트 데이터 생성` 등 다양한 상황에서 활용할 수 있다.
키보드 입력
fun main() {
val reader = Scanner(System.`in`) // in은 코틀린에서 사용할 수 없는 키워드. 자동으로 ` ` 붙음.
val a = reader.nextInt()
val b = reader.next()
}
알고리즘 문제를 풀 때 주로 사용한다.
조건문
fun main() {
var i = 5
var result = if (j > 10) {
"10 보다 크다."
} else if (i > 5) {
"5 보다 크다."
} else {
"5 이하이다."
}
println(result)
}
코틀린과 자바는 if 문을 거의 비슷하게 사용하지만,
코틀린에서는 if 문이 `표현식`으로 사용되므로 값으로 반환될 수 있다.
즉, result 변수에 바로 할당할 수 있어 자바보다 더 간결한 코드가 된다.
if 에 입력 커서를 두고 `Alt(Option) + Enter` 명령어를 눌러보자. when 절로 치환을 할 수 있다.
fun main() {
var i = 5
var result = when {
i > 10 -> "10 보다 크다."
i > 5 -> "5 보다 크다."
else -> "5 이하이다."
}
println(result)
}
코틀린에서 if 문 대신 `when` 문을 사용할 수 있다. when은 여러 조건을 처리할 때 더 간결하고 읽기 쉬운 방법을 제공한다.
자바에는 이와 비슷하게 `switch` 문이 있지만, when 문과는 다르게 복잡한 조건을 처리하는 데는 적합하지 않다.
fun main() {
/*
[java]
boolean result = i > 10 ? true : false;
*/
var i = 5
val result = if (i > 10) true else false
}
자바의 `삼항연산자`를 코틀린에서 if 문으로 처리할 수 있다.
반복문
fun main() {
val items = listOf(1, 2, 3, 4, 5)
for (item in items) {
println(item)
}
items.forEach { item ->
println(item)
}
// for (int i = 0; i <= 3; i++)
for(i in 0..3) {
println(items[i])
}
}
`for` 와 `foreach` 는 자바와 비슷하게 사용한다.
`while / break / continue` 는 자바와 똑같이 사용하므로 제외했다.
List & Array
fun main() {
val items1 = listOf(1, 2, 3, 4, 5) // 변경 불가
// mutableListOf<Int>(1, 2, 3, 4, 5) : <Int> 는 타입추론이 있기 때문에 생략 가능
val items2 = mutableListOf(1, 2, 3, 4, 5) // 변경 가능
items2.add(6)
items2.remove(3)
items2[0] = 100
// 출력 : 100 2 3 4 5 6
for (item in items2) {
println(item)
}
}
자바에서 List의 value를 반환받으려면: `int a = items2.get(0)`
이를 코틀린에서는 `val a = items2[0]` 같은 방법으로도 가능하다.
자바에서 List의 value를 변경: `items2.set(0, 100)`
이를 코틀린에서는 `items[0] = 100` 같은 방법으로도 가능하다.
예외처리 Try & Catch
fun main() {
val items = listOf(1, 2, 3)
// 출력: Index 4 out of bounds for length 3
try {
val item = items[4]
} catch (e: Exception) {
println(e.message)
}
}
Null Safety
fun main() {
var name: String? = null // Nullable 변수, null을 가질 수 있음
name = "주퓨터"
name = null
var name2: String = "" // Non-nullable 변수, null을 가질 수 없음
// Null Safety 처리 방법 1: null 체크
if(name != null) {
name2 = name
}
// Null Safety 처리 방법 2: 강제 널 안전성 연산자 사용 (주의 필요)
name2 = name!! // name이 null이 아님을 보장 (null일 경우 NPE 발생)
// Null Safety 처리 방법 3: Safe Call과 let 블록
name?.let {
name2 = it // name이 null이 아닐 때만 let 블록 실행
}
}
마치며
2편으로 연결됩니다.
'프로그래밍 언어' 카테고리의 다른 글
코틀린(Kotlin)의 핵심 문법과 예제 (2/2) (1) | 2025.01.12 |
---|