Kotlin 공식 문서를 보며 간단히 번역해 놓고 나름대로 이해한 내용을 덧붙여 정리했다.
- Package definition and imports
가장 상단에는 package specification과 import 가 정의되어야 한다.
source file은 임의로 file system 안에 두면 될 뿐, directory와 package를 꼭 match시킬 필요는 없다.
package my.demo
import kotlin.text
- Program entry point
Kotlin도 Java나 C#과 같이 entry point는 main function이다.
fun main(){
println("Hello world!")
}
다른 form에서의 main fuction은 String type의 인자를 변수로 받을 수 있다.
fun main(args: Array<String>){
println(args.contentToString())
}
- Print to the standard output
Java나 C#과 동일하다.
print method는 인자를 표준 형식으로 출력한다. 즉, 연달아 다른 인자 값으로 호출할 경우 String 값을 붙여 연속된 하나의 String으로 출력한다.
print("Hello ")
print("world!")
//결과: Hello world!
println method는 method 한번 호출에 한 line씩 차지하기 때문에 결국 나뉘어진 두 줄의 문자를 출력한다.
println("Hello world!")
println(42)
//결과: Hello world!
// 42
- Functions
아래의 function은 두 개의 int type의 parameter와 int return type으로 이루어진다.
//즉, sum이라는 함수명과 ()안에 사용할 parameter들의 name과 type을 정의 해주고
//우측에는 return 받을 type을 정의 해준뒤 2번 줄 return에서 return 받을 표현식을 기술하면 된다.
fun sum(a: Int, b: Int): Int{
return a + b
}
// 결과: a가 3, b가 5일 경우 8을 return
function의 body는 표현식이 될 수 있고, 이것으로 return type이 추론 가능하다.
// function의 body에 표현식을 기술해 주면 return type은 parameter들의 type과 표현식으로 추론이 가능하다.
fun sum(a: Int, b: Int) = a + b
//결과: a가 19, b가 23일 경우 42
아래의 function은 의미있는 값을 return하지 못한다.
//Unit Data type을 사용하였기 때문에 의미있는 값을 반환하지 못한다.
fun printSum(a: Int, b: Int): Unit{
println("sum of $a and $b is ${a + b}")
}
//결과: sum of -1 and 8 is 7
- Kotlin의 Data typeJava나 C#과 달리, Kotlin에는 Unit과 Nothing이라는 Data type이 있다.
- Unit: 흔히 return 구문이 없다는 것을 표현하기 위해 사용하며, 완전히 동일하지는 않지만(이를 이해하기 위해서는 generic 이해 필요) Java의 void type과 유사하다.
- 위에서와 같이 보통 fuction 선언부에 : 으로 return type을 명시해주는데 return type을 명시해 주지 않음으로써 딱히 반환할 Data가 없음을 의미한다.
- Nothing: 의미 있는 Data가 없다는 것을 명시적으로 선언해 주기 위하여 사용한다. 그 적절한 예로 예외선언의 throw와 같이 의미있는 Data를 반환해 주지 않는 경우 Nothing Data type을 사용한다.
- fun myFun(arg: Nothing?): Nothing{ throw Exception() } //이 function은 항상 예외를 발생시키기 떄문에 함수를 호출한 곳에 의미 있는 Data를 return 해주지 못한다. //이와 같은 것을 명시적으로 표현하기 위해 Nothing Data type을 사용한다.
- [깡샘의 코틀린 프로그래밍] 정리 5 - Unit과 Nothing
Unit return type은 생략 가능하다.
fun printSum(a: Int, b: Int){
println("sum of $a and $b is ${a + b}")
}
//결과: sum of -1 and 8 is 7
- Variables
읽는 용도의 Read-only 지역 변수의 경우에는 val 키워드를 사용하면 된다. 이는 딱 한번 값이 할당된다.(상수의 개념과 유사하다는 생각)
val a: Int = 1 //즉시 할당
val b = 2 //Int type임을 명시해주지 않아도 추론 가능
val c: Int //초기화를 해주지 않아도 type 명시는 필수
c = 3 //후에 값을 할당
변수가 재할당되어야 할 필요가 있는 경우에는 var 키워드를 사용한다.
var x = 5 //Int type임은 추론 가능하다.
x += 1 //값 재할당되고 있으므로 var 키워드를 사용한다.
top level(function 영역 밖)에 전역변수를 선언하고 모든 하위 function에서 사용 가능하다.
val PI = 3.14 //PI는 한번 할당된 후 다시 재할당되지 않을 것이므로 val 키워드 사용
var x = 0 //x 변수는 아래 function에서 재할당될 것이므로 var 키워드 사용
fun incrementX(){
x += 1 //function 내부에서 변수를 선언해주지 않았지만 전역변수로 선언하여 function 내부에서 사용 가능
}
- Creating classes and instances
class를 정의하려면, class 키워드를 사용해야한다.
class Shape
class의 properties는 클래스의 선언부나 body에 정의할 수 있습니다.
//properties를 선언부와 body에 height와 length로 정의
class Rectangle(var height: Double, var length: Double){
var perimeter = (height + length) * 2
}
class 선언부에 정의된 parameters의 생성자는 자동적으로 이용이 가능하다.
val rectangle = Rectangle(5.0, 2.0)
println("The perimeter is ${rectangle.perimeter}")
//결과: The perimeter is 14.0
class 간 상속은 : 으로 선언할 수 있다. class는 기본적으로 final(상수)이다. class를 상속 가능하도록 하려면 class 앞에 open을 기술하여 정의해야 한다.
//class를 상속 가능하도록 하기 위하여 open으로 정의
open class Shape
class Rectangle(var height: Double, var length: Double): shape(){
var perimeter = (height + length) * 2
}
- Comments
주석은 다른 언어와 동일하게 단문(단일행)의 경우 //
다행이나 긴 주석의 경우 /* */ 로 처리한다.
- String templates
var a = 1
//simple name in template:
val s1 = "a is $a"
a = 2
//arbitrary(임의적) expression in template:
val s2 = "${s1.replace("is", "was")}, but now is $a"
- Conditional expressions
fun maxOf(a: Int, b: Int): Int{
if(a > b){
return a
}else{
return b
}
}
//결과: max of 0 and 42 is 42
Kotlin에서는 if문도 하나의 표현식으로 표현이 가능하다.
fun maxOf(a: Int, b: Int) = if(a > b) a else b
//결과: max of 0 and 42 is 42
- for loop
//Kotlin에서는 배열을 listOf로 표현하는 것 같다.
val items = listOf("apple", "banana", "kiwifruit")
for(item in items){
//for문으로 items의 item들을 출력
println(item)
}
//결과: apple
// banana
// kiwifruit
혹은
val items = listOf("apple", "banana", "kiwifruit")
//index로 items의 위치를 찾아 출력하는 방식인듯 하다.
//indices는 Collection, Array 클래스에 선언된 property로 collection 타입의 index 범위를 반환.
for(index in item.indices){
println("item at $index is ${items[index]}")
}
//결과: item at 0 is apple
// item at 1 is banana
// item at 2 is kiwifruit
- while loop
val items = listOf("apple", "banana", "kiwifruit")
var index = 0
while(index < items.size){
println("item at $index is ${items[index]}")
}
//결과: item at 0 is apple
// item at 1 is banana
// item at 2 is kiwifruit
- when expression
Kotlin에서 when 조건문은 다른 언어에서 switch문과 동일하다.
fun describe(obj: Any): String =
when(obj){
1 -> "One"
"Hello" -> "Greeting"
is Long -> "Long"
!is String -> "Not a string"
else -> "Unknown"
}
//결과: One
// Greeting
// Long
// Not a string
// Unknown
여기서 주의할 점은 when 조건문이 return값을 가지는 용도로 사용되면 반드시 모든 경우의 조건을 정의해야 한다. 그렇지 않을 경우 compile error가 발생한다.
→ null branch를 추가하거나 else branch를 추가하라는 error 메시지가 발생한다. 따라서 모든 조건식(branch)를 정의하거나 else branch를 정의해야 한다.
when문은 표현식이나 범위(range)를 조건식으로 가질 수도 있다.
- Range
val x = 10
val y = 9
//Kotling에서 .. 는 수 사이의 범위를 의미(다른 언어의 부등호 표현과 같은 의미)
if(x in 1..y+1){
println("fits in range")
}
//결과: fits in range
여기서 1..4 와 같은 식이 아닌 4..1 와 같은 식으로 표현할 경우 아무 동작도 하지 않게 된다. 이처럼 감소하는 수를 표현하고 싶으면 downTo 함수를 이용해야 한다.
for(i in 4 downTo 1) print(i)
//결과: 4321
또한 step을 사용할 경우 일정한 간격으로 i를 증가시킬 수도 있다.
for(i in 1..4 step 2) print(i)
//결과: 13
downTo와 step을 함께 사용하여 역순으로 일정한 간격의 수만큼 i를 감소시킬 수도 있다.
for(i in 4 downTo 1 step 2) print(i)
//결과: 42
until 함수는 범위를 결정하는데 사용된다.
for(i in until 10){
println(i)
}
//위 식은 1부터 9까지 즉, 10을 포함하지 않도록 범위를 설정한다.
last함수는 마지막 i의 값을 확인하는데 사용된다.
(1..12 step 2).last == 11
(1..12 step 3).last == 10
(1..12 step 4).last == 9
수가 범위를 넘어서는지 확인하는 코드이다.
val list = listOf("a", "b", "c")
if(-1 !in 0..list.lastIndex){
println("-1 is out of range")
}
if(list.size !in list.indices){
println("list size is out of valid list indices range, too")
}
//결과 : -1 is out of range
// list size is out of valid list indices range, too
범위를 반복하는 용도로도 사용한다.
for(x in 1..5){
print(x)
}
//결과: 12345
- Collections
아래는 collection을 반복하여 사용하는 코드이다.
for(item in items){
println(item)
}
//결과: apple
// banana
// kiwifruit
다음은 collection이 객체를 가지고 있는지 in 을 사용하여 확인하는 코드이다.
when{
"orange" in items -> println("juicy")
"apple" in items -> println("apple is fine too")
}
//결과: apple is fine too
filter나 map collection에 lamda식을 사용할 수 있다.
var fruits = listOf("banana", "avocado", "apple", "kiwifruit")
fruits
//Kotlin에서 it은 다른 언어에서의 this와 동일한 것 같다.
//filter는 list를 iteration하면서 값이 true인 것만 return한다.
.filter { it.startWith("a") }
.sortedBy { it }
//map은 값을 변현한 후 새로운 list를 생성한다.
.map { it.uppercase() }
.forEach { println(it) }
//결과: APPLE
// AVOCADO
- Kotiln Collection 종류
- filter : filter는 list를 iteration하면서 return값이 true인 값만 filtering한다.
- map : map은 값을 변형해서 새로운 list를 생성한다.
- all : all은 모든 원소가 lamda식을 만족하는지 검사할 수 있다.
- any : any는 하나라도 조건을 만족하는지 검사할 수 있다.
- count : count는 lamda식을 만족하는 개수를 return한다.
- find : find는 lamda식을 만족하는 하나의 값을 찾을 수 있다.
- groupBy : groupBy는 조건에 따라 group으로 묶을 수 있다.
- flatMap : flatMap은 lamda식을 모든 객체에 적용하고 얻어지는 여러 list를 하나의 list로 생성한다.
- 코틀린 (Kotlin) filter, map, all, any, count, find, groupBy, flatMap 함수 정리
- Nullable values and null checks
값이 null이 될 가능성이 있는 경우에는 참조는 반드시 nullable로 표시가 되어야 한다. Nullable type name은 끝에 ? 를 가짐으로써 표시된다.
str 값에 integer(정수)를 가지고 있지 않으면 null을 return한다.
fun parseInt(str: String): Int?{
//...
}
nullable 값을 반환하는 함수를 사용할 수도 있다.
fun printProduct(arg1: String, arg2: String){
val x = parseInt(arg1)
val y = parseInt(arg2)
//'x * y' 식을 사용하면 x와 y는 null 값을 가질 수 있기 때문에 error가 발생할 것이다.
if(x != null && y != null){
//x와 y는 null check 이후 wkehdwjrdmfh non-nullable로 형변환 해준다.
println(x * y)
}else{
pringln("'$arg1' or '$arg2' is not a number")
}
}
//결과: 42
// 'a' or '7' is not a number
// 'a' or 'b' is not a number
혹은
//...
if(x == null){
println("Wrong number format in arg1: '$arg1'")
return
}
if(y == null){
println("Wrong number format in arg2: '$arg2'")
return
}
//x와 y는 null check 이후 자동적으로 non-nullable로 형변환된다.
println(x * y)
//결과: 42
// Wrong number format in arg1: 'a'
// Wrong number format in arg2: 'b'
- Type checks and automatic casts
is 연산자는 표현식의 instance의 type이 맞는지 확인합니다. 변경할 수 없는 지역 변수나 property가 특정한 type으로 확인되면 명시적으로 형변환할 필요가 없습니다.
fun getStringLength(obj: Any): Int?{
if(obj is String){
//'obj'는 이 조건문에서 자동으로 'String'으로 형변환된다.
return obj.length
}
//'obj'는 type을 검사하는 분기 외부에서는 여전히 'Any' type이다.
return null
}
혹은
fun getStringLength(obj: Any): Int?{
if(obj !is String) return null
//'obj'는 이 분기에서는 'String'으로 자동으로 형변환된다.
return obj.length
}
혹은
fun getStringLength(obj: Any): Int?{
//'obj'는 '&&'의 오른쪽에서 자동으로 'String'으로 형변환된다.
if(obj is String && obj.length > 0){
return obj.length
}
return null
}
내가 코틀린으로 개발하면서 참고할 목적으로 한번 쭉 공부하면서 정리해 보았는데, 부족한 부분이 많을 수 있다.