- 코틀린 시작하기
03-1 코틀린 언어 소개
코틀린은 젯프레인스에서 오픈소스 그룹을 만들어 개발한 프로그래밍 언어입니다.
자바를 대체할 목적으로 만들어져 JVM에서 실행할 수 있으나 둘은 다른 언어입니다.
자바는 확장자로 .java를 사용하지만 코틀린은 .kt를 사용합니다.
하지만 코틀린 컴파일러(kotlinc)가 .kt 파일을 컴파일하면 자바 바이트 코드가 만들어집니다. 그리고 이를 JVM이 실행합니다.
코틀린 개발이 자바 개발에 비해 갖는 이점
표현력과 간결함: 훨씬 간결한 구문으로 프로그램 작성이 가능합니다.
안전한 코드: 코틀린은 널 안정성을 지원하는 언어로 변수를 널허용(nullable)과 널불허용(not null)로 구분해 선언합니다.
상호 운용성: 자바와 100% 호환합니다. 자바 클래스나 라이브러리를 얼마든지 활용할 수 있습니다. 자바로 개발한 앱을 유지보수하기위해 추가하는 코드만 코틀린으로 작성할 수도 있습니다.
구조화 동시성: 코루틴이라는 기법을 이용하면 비동기 프로그래밍을 간소화 할 수 있습니다. 네트워크 연동이나 DB 갱신과 같은 작업을 할때 이용하면 프로그램을 조금 더 간단하고 효율적으로 작성할 수 있습니다.
코틀린 파일 구성
var data = 10
fun formatDate(date: Date) String {
val sdformat = SimpleDateFormat("yyyy-mm-dd")
return sdformat.format(date)
}
class User {
var name = "hello"
fun sayHello(){
println("name : $name")
}
}
package 구문은 컴파일했을때 만들어지는 클래스파일의 위치를 나타내며 맨 처음 한 줄로 선언합니다.
그런데 package구문은 kt 파일의 위치와 상관없는 별도의 이름으로도 선언할 수 있습니다.
User.kt 파일이 com/example/test3에 있더라도 package ch3으로 선언하면 ch3폴더에 컴파일된 클래스파일을 생성합니다.
import구문은 여러 줄 작성할 수 있으며 변수, 함수, 클래스를 선언할 수 있습니다. 변수와 함수는 클래스 안, 밖에도 선언할 수 있습니다.
그리고 어떤 파일에 선언한 멤버(변수, 함수, 클래스)를 다른 코틀린 파일에서 참조할 때 두 파일을 같은 package로 선언했다면 import없이 사용할 수 있습니다.
자바와 다르게 코틀린 파일명과 그 파일에 선언한 클래스명은 아무런 상관이 없습니다.
코틀린의 변수 함수를 클래스로 묶지않고 선언한다면 변수, 함수, 클래스에 각각 접근하면 됩니다.
(자바는 모든 것을 클래스로 묶어야 합니다.)
03-2 변수와 함수
변수 선언하기
val, var 키워드로 선언합니다.
val: value의 줄임말로 초깃값이 할당되면 바꿀 수 없는 변수를 선언할 때 사용합니다.
var: variable의 줄임말로 초깃값이 할당된 후에도 값을 바꿀 수 있는 변수를 선언할 때 사용합니다.
타입 지정과 타입 추론
변수명 뒤에는 콜론(:)을 추가해 타입을 명시할 수 있으며, 대입하는 값에 따라 타입을 유추(타입 추론)할 수 있을 때는 생략할 수 있습니다.
val data1: Int = 10
val data2 = 10
data2는 대입한 값이 10이므로 타입을 명시하지 않아도 Int 타입이 됩니다.
초깃값 할당
최상위에 선언한 변수나 클래스의 멤버 변수는 선언과 동시에 초깃값을 할당해야 하며, 함수 내부에 선언하는 경우는 할당하지 않아도 됩니다.
val data1: Int
val data2 = 10
fun someFun(){
val data3: Int
println("data3 : $data3") // 오류
data3 = 10
println("Data3 : $data3") // 성공
}
class User{
val data4: Int // 오류
val data5: Int = 10 // 성공
}
초기화 미루기
변수를 선언할 때 초깃값을 할당할 수 없는 경우가 있습니다.
이때는 값을 이후에 할당할 것이라고 컴파일러에게 알려주어야 합니다.
lateinit나 lazy키워드를 이용합니다.
lateinit: 이후에 초깃값을 할당할 것임을 명시적으로 선언합니다.
하지만 모든 유형의 변수 선언에 사용할 수는 없으며 다음 2가지 규칙을 따라야 합니다.
1. var 키워드로 선언한 변수에만 사용할 수 있습니다.
2. Int, Long, Short, Double, Float, Boolean, Byte 타입에는 사용할 수 없습니다.
lazy: 변수 뒤에 by lzay{}형식으로 선언하며, 소스에서 변수가 최초로 이용되는 순간 중괄호로 묶은 부분이 자동으로 실행되어 그 결괏값이 변수의 초깃값으로 할당됩니다.
val data4: Int by lazy{
println("in lazy.....")
10
}
fun main(){
println("in main.....")
println(data4 + 10)
println(data4 + 10)
}
위 코드를 실행하면
in main.....
in lazy.....
20
20
이 출력됩니다.
데이터 타입
코틀린의 모든 변수는 객체 입니다. 정수를 다루는 타입이 Int인데 이것은 기초 데이터 타입(primitive type:int)가 아니라 클래스입니다.
(따라서 선언 및 초기화시 null 대입 가능, 객체의 메서드도 호출 가능)
val a1: Byte = 0b00001011
val a2: Int = 123
val a3: Short = 123
val a4: Long = 10L
val a5: Double = 10.0
val a6: Float = 10.0f
val a7: Boolean = true
기초 데이터를 객체로 표현하는 타입들 입니다.
Char, String - 문자와 문자열
Char는 문자를 표현하는 타입입니다. 작은 따옴표(')로 감싸서 사용하며 Number타입으로 표현할 수 없습니다.
String은 문자열을 표현하는 타입입니다. 큰따옴표(")나 삼중 따옴표(""")로 감싸서 표현합니다.
줄바꿈이나 들여쓰기 등을 그대로 유지하려면 역슬래시(\)를 사용합니다.
(안드로이드 스튜디오에서 삼중 따옴표를 사용하면 닫는 따옴표 뒤에 .trimIndent()함수가 자동으로 추가됩니다. 문자열 앞에 공백을 없에줍니다.)
문자열 템플릿: String 타입의 데이터에 변숫값이나 어떤 연산식의 결괏값을 포함해야 할 때 $ 기호를 이용합니다.
fun main(){
fun sum(no: Int): Int{
var sum = 0
for(i in 1..no){
sum += i
}
return sum
}
val name: String = "kkang"
println("name: $name, sum: ${sum(10)}, plus: ${10+20}")
}
Any - 모든 타입 가능
코틀린에서 최상위 클래스 입니다. 모든 코틀린의 클래스는 Any의 하위 클래스라 모든 타입의 데이터를 할당할 수 있습니다.
Unit - 반환문이 없는 함수
데이터의 형식이 아닌 특수한 상황을 표현하려는 목적으로 사용합니다.
Unit타입으로 선언한 변수에는 Unit 객체만 대입할 수 있습니다.
주로 함수의 반환 타입으로 사용됩니다. 함수에서 반환문이 없음을 명시적으로 나타낼 때 Unit타입을 사용합니다.
Noting - null이나 예외를 반환하는 함수
Unit과 마찬가지로 의미 있는 데이터가 아니라 특수한 상황을 표현합니다. Nothing으로 선언한 변수는 null만 대입할 수있습니다.
주로 함수의 반환 타입에 사용합니다. 어떤 함수의 반환 타입이 Nothing이면 반환은 있지만 의미 있는 값은 아니라는 의미 입니다.
항상 null만 반환하는 함수나 예외를 던지는 함수의 반환타입을 Nothing으로 선언합니다.
널 허용과 불허용
코틀린의 모든타입은 객체이므로 변수에 null을 대입할 수 있습니다.
null은 값이 할당되지 않은 상황을 의미합니다. 변수를 선언할 때 널 허용인지 널 불혀용인지 명확하게 구분해서 선언해야 합니다.
이런 구분은 물음표(?)로 표시합니다. 타입 뒤에 물음표를 추가하면 널 허용으로 선언 합니다.
함수 선언
fun이라는 키워드를 이용합니다.
fun 함수명(매개변수명: 타입) 반환타입 {...}
반환타입을 생략하면 자동으로 Unit타입이 적용됩니다.
함수의 매개변수에는 var이나 val 키워드를 사용할 수 없습니다. val이 자동으로 적용되며 함수 안에서 매개변숫값을 변경할 수 없습니다.
또, 함수의 매개변수에는 기본값을 선언할 수 있습니다. 이렇게하면 호출할 때 인자를 전달하지 않아도 되며 선언문에 명시한 기본값이 적용됩니다.
fun main(){
fun some(data1: Int, data2: Int = 10): Int{
return data1 * data2
}
println(some(10)) // 실행결과 100
println(some(10, 20)) // 실행결과 200
}
함수의 매개변수가 여러개면 호출할 때 전달한 인자를 순서대로 할당합니다. 그런데 매개변수명을 지정하면 매개변숫값의 순서를 바꿔도 됩니다.
fun some(data1: Int, data2: Int): Int{
return data1 * data2
}
println(some(10, 20))
some(data2 = 20, data1 = 10)
매개변수명을 지정해 호출하는 것을 명명된 매개변수라고하며, 이렇게 하면 순서에 맞춰 호출하지 않아도 됩니다.
컬렉션 타입
여러 개의 데이터를 표현하는 방법이며 Array, List, Set, Map이 있습니다.
Array-배열표현
생성자의 첫 번째 매개변수는 배열의 크기이며, 두번째 매개변수는 초깃값을 지정하는 함수입니다. 배열의 타입은 제네릭(선언하는 곳이 아니라 이용하는 곳에서 타입을 지정하는 기법)으로 표현합니다.
<init>(size: Int, init: (Int) -> T) // Array클래스의 생성자
val data1: Array<Int> = Array(3, {0})
0으로 초기화한 데이터를 3개 나열한 정수형 배열을 선언 한 코드입니다.
배열의 데이터에 접근할 때는 대괄호([])를 이용해도 되고 set()이나 get()함수를 이용할 수도 있습니다.
기초타입의 배열
배열의 데이터가 기초 타입이라면 Array를 사용하지 않고 각 기초타입의 배열을 나타내는 클래스를 이용할 수도 있습니다.
(BooleanArray, ByteArray, CharArray .. 등)
val data1: IntArray = IntArray(3, {0})
val data2: BooleanArray = BooleanArray(3, {false})
또한 arrayOf()함수를 이용하면 배열을 선언할 때 값을 할당할 수도 있습니다.
val data1 = arrayOf<Int>(10, 20, 30)
arrayOf()함수도 기초타입을 대상으로 하는 booleanArrayOf(), byteArrayOf() ... 함수를 제공합니다.
List, Set, Map
Collection 인터페이스를 타입으로 표현한 클래스에며, 통틀어 컬렉션 타입 클래스라고 합니다.
List: 순서가 있는 데이터 집합으로 데이터의 중복을 허용
Set: 순서가 없으며 데이터의 중복을 불허용
Map: 키와 값으로 이루어진 데이터 집합으로 순서가 없으며 키의 중복을 불허용
초기에 데이터를 대입하면 더 이상 변경할 수 없는 불변 클래스와 이후에도 데이터를 추가하거나 변경할 수 있는 가변 클래스로 나뉩니다.
| 구분 | 타입 | 함수 | 특징 |
| List | List | listOf() | 불변 |
| MutableList | mutableListOf() | 가변 | |
| Set | Set | setOf() | 불변 |
| MutableSet | mutableSetOf() | 가변 | |
| Map | Map | mapOf() | 불변 |
| MutableMap | mutableMapOf() | 가변 |
fun main(){
var list = listOf<Int>(10, 20, 30)
println(
"""
list size : ${list.size}
list data : ${list[0]), $(list.get(1)}, ${list.get(2)}
"""
)
}
fun main(){
var mutableList = mutableListOf<Int>(10, 20, 30)
mutableList.add(3, 40)
mutableList.set(0, 50)
println(
"""
list size : ${mutableList.size}
list data : ${mutableList[0]}, ${mutableList.get(1)},
${mutableList.get(2)}, ${mutableList.get(3)}
"""
)
}
Map객체는 키와 값으로 이루어진 데이터의 집합으로 키와 값은 Pair객체를 이용할 수도 있고 '키 to 값'형태로 이용할 수도 있습니다.
fun main(){
var map = mapOf<String, String>(Pair("one", "hello"), "two" to "world")
prnitln(
"""
map size : ${map.size}
map data : ${map.get("one")}, ${map.get("two")}
"""
)
}
Pair("one", "hello")처럼 키값을 Pair 객채로 표현해 대입하거나 "two" to "world"처럼 대입한 코드입니다.
03-3 조건문과 반복문
조건문 if~else와 표현식
if문에 명시한 조건을 만족하면 if 부분을 실행하고, 그렇지 않으면 else부분을 실행합니다.
else if를 이용해 조건을 여러개로 나열할 수도 있습니다.
대부분의 프로그래밍 언어에서 제공하는 조건문과 차이가 없으나, 코틀린에서 if~else는 표현식으로도 사용할 수 있습니다.
표현식이란 결괏값을 반환하는 계산식을 말합니다. 즉 if~else문을 단순히 조건에 맞는 영역을 실행하는 용도로 사용해도 되지만, 결괏값을 반환하는 표현식으로도 사용할 수 있습니다.
fun main(){
var data = 10
val result = if(data>0){
println("data > 0")
true
}else{
println("data <= 0")
false
}
println(result)
}
단 if~else문을 표현식으로 사용하려면 항상 else구문이 있어야 하며 if~else혹은 if~else if~else 형태로 사용해야 합니다.
반환하는 결괏값은 각 영역의 마지막 줄에 해당합니다.
조건문 when
fun main(){
var data = 10
when (data) {
10 -> println("data is 10")
20 -> println("data is 20")
else -> {
println("data is not valid data")
}
}
}
소괄호 안에 넣은 데이터가 조건이 되고 이 값이 때라 화살표 오른쪽의 구문을 실행합니다.
정수가 아닌 다른 타입의 데이터를 지정할 수도 있습니다. ("hello", "hello" -> println(""))
fun main(){
var data: Any = 10
when (data){
is String -> println("data is String") // data가 문자열 타입이면
20, 30 -> println("data is 20 or 30") // data가 20 또는 30이면
in 1..10 -> println("data is 1..10") // data가 1~10의 값이면
else -> println("data is not valid")
}
}
(in: 범위 지정 연산자)
when문을 이용하면서 데이터를 명시하지 않고 조건만 명시할 수도 있습니다.
fun main(){
var data = 10
when{
data <= 0 -> println("data is <= 0")
data > 100 -> println("data is > 100")
else -> println("data is valid")
}
}
if 문처럼 표현식으로도 사용할 수있습니다. else역시 생략할 수 없습니다.
fun main(){
var data = 10
var result = when{
data <= 0 -> data is <= 0
data > 100 -> data is > 100
else -> data is valid
}
println(result)
}
반복문 for 와 while
for문은 제어 변숫값을 증감하면서 특정 조건이 참일 때까지 구문을 반복해서 실행합니다. 주로 범위연산자인 in을 사용합니다.
for (i in 1..10){...} - 1부터 10까지 1씩 증가
for (i in 1 until 10) {...} - 1부터 9까지 1씩 증가(10은 미포함)
for (i in 2..10 step 2) {...} - 2부터 10까지 2씩 증가
for (i in 10 downTo 1) {...} - 10부터 1까지 1씩 감소
증감 조건을 숫자로 명시하지 않고 컬렉션 타입의 데이터 개수만큼 반복하게 할 수도 있습니다.
fun main(){
var data = arrayOf<Int>(10, 20, 30)
for(i in data.indices){
print(data[i])
if(i !== data.size - 1) print(",")
}
// 실행결과: 10, 20, 30
}
indicies는 컬렉션 타입의 인덱스값을 의미하므로 i 값이 0, 1, 2를 대입합니다.
인덱스와 함께 실제 데이터를 가져오려면 withIndex()함수를 이용합니다.
fun mian(){
var data = arrayOf<Int>(10, 20, 30)
for ((index, value) in data.withIndex()){
print(value)
if(index !== data.size - 1) print(",")
}
// 실행결과 : 10, 20, 30
}
'Java > Kotlin' 카테고리의 다른 글
| Kotlin Programming Study - 07 (0) | 2025.12.16 |
|---|---|
| Kotlin Programming Study - 06 (0) | 2025.12.15 |
| Kotlin Programming Study - 04 (0) | 2025.12.10 |
| Kotlin Programming Study - 02 (0) | 2025.12.09 |
| Kotlin Programming Study - 01 (0) | 2025.12.05 |