2019-10-06
구글에서 kotlin 강의를 검색하면 Inflearn의 무료강좌가 공식문서 마냥 맨 위에 뜬다. kotlin의 공식문서를 볼까 했지만 일단 보면서 내용 정리 및 기록을 해본다.
package my.demo // 패키지 정의는 파일 최상단에. directory와 패키지를 일치시키지 않아도 됨
import java.util.* // import는 자바와 같다고 한다
// ..
fun sum(a: Int, b: Int): Int {
return a + b
}
fun sum(a: Int, b: Int) = a + b
// Expression으로 선언 가능. 이 경우 리턴 타입을 생략 가능하며 타입을 추론해줌
fun printKotlin(): Unit {
println("hello Kotlin")
}
// Unit 은 자바의 void와 같은 역할. 생략가능한데 이 경우 묵시적으로 Unit을 리턴함이 정의됨. JS같네.
val
: 읽기 전용 변수. 자바의 final과 비슷하다함
val a: Int = 1 // 즉시 할당
val b = 2 // Int 타입 추론
val c: Int // 컴파일 오류. 초기화 필요
c = 3 // 컴파일 오류. 읽기전용
var
: mutable 변수
var x = 5
x += 1
JS와 동일한데 다른점은 block comment의 중첩이 가능
/* block comment
/* nested block comment 가능*/
허헛
*/
String Interpolation
var a = 1
val s1 = "a is $a"
a = 2
val s2 = "${s1.replace("is", "was")}, but now is $a"
JS와 유사하지만 단순 변수 대입할땐 중괄호가 필요없네.
fun maxOf(a: Int, b: Int): Int {
if (a > b) {
return a
} else {
return b
}
}
// 조건식으로 사용 가능. 간결.
fun maxOf(a: Int, b: Int) = if (a > b) a else b
근데 가독성 측면에서 block 표기를 선호하는 경우도 있기 때문에 이런식의 간결화는 호불호 내지는 장단점이 있다고 본다.
타입스크립트, flow에서 접해본 개념.
fun parseInt(str: String): Int? {
// 정수가 아닌 경우 null 리턴
}
nullable 타입의 변수에 접근할 때는 반드시 null 체크를 해야 함 컴파일 오류로 이어짐
fun printProduct(arg1: String, arg2: String) {
val x: Int? = parseInt(arg1)
val y: Int? = parseInt(arg2)
if (x != null && y !== null) {
println(x * y)
} else {
println("either '$arg1' or '$arg2' is not a number")
그렇다고 한다. if문으로 처리하는게 뭔가좀 번거로워 보이는데.. 간편한 문법을 지원하지 않으려나?
타입 체크만 해도 자동으로 타입 변환 됨
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// obj 가 자동으로 String 타입으로 변환됨
return obj.length
}
return null
}
when
expressionJS는 아직 제안단계(Stage 1) pattern matching
fun describe(obj: Any): String =
when (obj) {
1 -> "One"
"Hello" -> "Greeting"
is Long -> "Long"
!is String -> "Not a string"
else -> "Unknown"
}
val x = 3
if (x in 1..10) {
println("fits in range")
}
for (x in 1..5) {
print(x)
}
컬렉션도 in
으로 loop 가능
val items = listOf("apple", "banana", "kiwi")
for (item in items) {
println(item)
}
in
으로 컬렉션에 포함되는지 체크 가능
val items = setOf("apple", "banana", "kiwi")
when {
"orange" in items -> println("juicy")
"apple" in items -> println("apple is fine too")
}
람다식을 이용해서 컬렉션에 filter, map 등 연산 가능
val fruits = listOf("banana", "avocado", "apple", "kiwi")
fruits
.filter { it.startsWith("a") }
.sortedBy { it }
.map { it.toUpperCase() }
.forEach { println(it) }
(람다식이 뭔가 했더니 JS에서 화살표함수와 유사한 형태의 문법이고.. OOP 언어인 자바에서 함수형 지원을 위해 도입한 것으로 보인다. 마치 JS가 객체지향 패러다임을 지원하려고 prototype으로 흉내를 내듯.. 여기 적은 예제는 자바에 람다식과 함께 도입된 stream API를 함께 사용했다. parameter가 it 하나뿐이면 it ->
을 생략할 수 있다. 참고)
자바와 비슷하지만..
Kotlin | Java | bit |
---|---|---|
Double | double | 64 |
Float | float | 32 |
Long | long | 64 |
Int | int | 32 |
Short | short | 16 |
- | char | 16 |
Byte | byte | 8 |
kotlin은 8진수를 지원하지 않음
언더스코어로 자리수 끊어 가독성을 높일 수 있다.
val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_2344L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
// JVM primitive
val a: Int = 100
print(a === a) // Prints 'true'
// Boxed
val boxedA: Int? = a
val anotherBoxedA: Int? = a
println("==: ${boxedA == anotherBoxedA}") // Prints 'true'
println("===: ${boxedA === anotherBoxedA}") // Prints 'false'
참고: ===
는 양변이 같은 객체인지 비교 가능
생각해보면 당연하다. 한 int 변수에 숫자가 아닌 null이 들어올 수 있으려면 int라는 타입만으로는 표현이 불가하다. nullable하려면 변수가 다른 타입이 되어야 하니 객체가 되는거다.
작은 타입은 큰 타입의 하위 타입이 아님. 암시적으로는 숫자 타입 변환이 안됨. 명시적으로 변환해줘야 함
val a: Int = 1 // A boxed Int
// val b: Long = a // 오류
val b: Long = a.toLong()
// println(a == b) // 오류: Operator '==' cannot be applied to 'Int' and 'Long'
val i: Int = b.toInt() // OK
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
Char는 숫자로 취급되지 않음
fun check(c: Char) {
if (c == 1) {/*...*/} // Error
}
fun check(c: Char) {
if (c == `a`) {/*...*/} // OK
}
print('0'.toInt()) // print 48 (코드값)
size 등 유용한 멤버 함수 포함
var array: Array<String> = arrayOf("코틀린", "강좌")
println(array.get(0)) // 0번째 인덱스 꺼냄
println(array[0]) // 오버로딩 돼있어서 이렇게도 사용가능
println(array.size)
JS같다!
두가지 방법
val b = Array(5, {i -> i.toString() }) // 범위와 람다식 이용
val a = arrayOf("0", "1", "2", "3", "4")
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = 7
println(x.get(0)) // print 7
println(x[0]) // print 7
println(x.size) // print 3
Array<Double>
배열에 제네릭을 사용하면 primitive type이 아니라 reference type이 사용된다..고 함. 메모리 문제인지, 말그대로 원시값/참조값 차이가 생기는게 문제인건지 모르겠지만 이런 현상을 없애기 위해 사용한다고 한다. 따라서 nullable하지 않은 내용이라면 IntArray 등의 클래스를 사용해 배열을 선언하는 것이 권장된다.
흠. JS에도 typed array가 있긴 하지만 같은개념인진 모르겠다. (typed array를 모름)
var x: String = "Kotlin"
println(x.get(0))
println(x[0])
println(x.length)
for (c in x) {
println(c) // 한글자씩 가져오기 가능
}
escaped string (“Kotlin”)
raw string ("""Kotlin""")
val s = "Hello, world!\n"
val s = """
"'이것은 코틀린의
raw String
입니다.'"
"""
JS의 백틱이랑 비슷하지만.. 백틱은 이스케이프 문자가 먹힌다.
불행인지 다행인지 JS에서 보던 것들을 코틀린에서도 볼 수 있다. 쪼개져있던 언어들이 한 형태로 수렴중인 느낌. 이럴수록 나같은 주니어는 하던 언어를 더 깊이 파고 다른 언어를 접하는게 효율적이겠단 생각이 깊어진다.