앱/Android

Kotlin 기본 문법

#fdd4ff 2023. 10. 19. 16:10

 

전공 수업 정리 겸, 잘 쓰질 않다보니 계속 까먹는 코틀린 문법 위주로 정리해보았다.

 

String templates

파이썬의 경우 format 형태를 쓰기도 했는데 코틀린은 $ 하나면 된다.

val i = 10 
println("i = $i") // i = 10

 

만약 length와 같은 함수를 써야하는 경우는 ${}를 활용한다. 연산의 경우도 마찬가지

val s = "abc"
println("$s.length is ${s.length}")
// {}없이 $s.length 로 해버리면 .length라고 문자 그대로 출력하게 됨

println("I have ${numberOfShirts + numberOfPants} items of clothing")

 

Variable

코틀린은 변수의 타입을 한번 선언한 이상, 다른 타입으로 바꿀 수 없다.

var book : String = "book"
book = 1 // 오류 발생! val book = "book"으로 해도 결과는 마찬가지

코드 결과

 

또 하나 주의해야할 점은 var / val 의 구별이다.
파이썬이나 C언어만 쓰다가 이쪽 계열(?) 언어를 쓰다보면 헷갈리게 되는데 꼭 알아둬야한다.

var 

- Mutable (Changeable)
- 변수의 값을 바꿀 수 있다 (타입말고)

val

- Immutable (Unchangeable)
- 변수의 값을 바꿀 수 없다
- 보통 val 로 선언하는 것을 추천

var count = 1 
count = 2
// ok

val size = 1 
size = 2
//Error: val cannot be reassigned

 

Ranges

내용 중에 if 문도 있긴 했는데 얘네는 c언어의 if문과 형태가 같아서 넘어갔다.

val numberOfStudents = 50
if (numberOfStudents in 1..100) {
    println(numberOfStudents)
}
// numberOfStudents이 1 부터 100 사이인지 확인하고, 맞으면 출력하기

여기서 1..100 같이 range를 쓸 때 .. 사이에 빈칸이 있으면 안된다. (1. .100 와 같이 빈칸을 두면 안됨)

그렇지 않으면 엄청난 오류메세지를 확인할 수 있다.

for (i in 1..5) print(i)
//12345
for (i in 5 downTo 1) print(i)
//54321
for (i in 3..6 step 2) print(i) 
//3부터 시작해서 2 간격으로 루프를 돈다. 3 -> 5 -> 7인데 6까지 끝
//35
for (i in 'd'..'g') print (i)
//defg

 

when

코틀린에서는 switch 문 대신 when 구절을 사용한다.

when (results) {
    0 -> println("No results")
    in 1..39 -> println("Got results!")
    else -> println("That's a lot of results!")
}
// results의 값에 따라 출력되는 내용이 다르다.
for ((index, element) in pets.withIndex()) {
	println("Item at $index is $element\n")
}
// python의 enumerate 문법과 같이 쓸 수 있다.
// index와 요소의 값을 한꺼번에 추출 가능

"""
Item at 0 is dog

Item at 1 is cat

Item at 2 is canary
"""

val pets = arrayOf("dog", "cat", "canary")
for (index in pets.withIndex()) {
    println("Item at $index is \n")
}
// 만약 (index, element)로 안하고 index 하나만 둔다면

"""
Item at IndexedValue(index=0, value=dog) is 

Item at IndexedValue(index=1, value=cat) is 

Item at IndexedValue(index=2, value=canary) is
"""

 

 

Lists and Array

Lists

val instruments = listOf("trumpet", "piano", "violin")
println(instruments)
// [trumpet, piano, violin]

mutableListOf()로도 쓸 수 있다.

val myList = mutableListOf("trumpet", "piano", "violin")
myList.remove("violin")
// 만약 mutableListOf로 안 했다면 오류가 뜸

근데 val은 변수의 값을 바꿀 수 없다 하지 않았나? 
하지만 list의 경우, 보는 시각이 조금 다르다. list의 내용이 바뀐거지, list가 아예 다른건 아니지 않나
맞는 비유인지는 모르겠지만 건물 리모델링한거랑 건물을 부시고 새로 만든다고 생각하면 되지 않을까?

 

Array

- size는 고정이다.
- Array의 요소들의 타입이 다 달라도 상관없다.
- 연산자 + 사용 가능

val mix = arrayOf("hats", 2)
val numbers = intArrayOf(1,2,3) 
val numbers2 = intArrayOf(4,5,6) 

val combined = numbers2 + numbers 
println(Arrays.toString(combined))
// [4, 5, 6, 1, 2, 3]

 

 

Null safety

- 코틀린은 변수는 기본적으로 null일 수 없다.
- safe call operator인 ?를 통해 null을 쓸 수 있게 한다.
- !! 를 사용하여 해당 변수가 null이 아니라는걸 보증(?) 한다.

var numberOfBooks: Int = null
// error: null can not be a value of a non-null type Int

var numberOfBooks: Int? = null
var numberOfBooks = 6
numberOfBooks = numberOfBooks?.dec()
// null이 아니면 값을 1 감소 시키자.

val len = s!!.length
// s가 null이 아니야! 그니까 length를 쓸 수 있어!
// s가 null이면 NullPointerException 발생
numberOfBooks = numberOfBooks?.dec() ?: 0
// numberOfBooks가 null이 아니면 1 감소하고
// null이면 0으로 할당해주자

여기서 조금 귀여웠던 내용..

?: 를 Elvis operator라고 부르기도 하는데 그 이유가  :) 를 웃는 표정으로 보듯
?: 를 옆으로 보면 Elvis 헤어스타일을 한 것같이 보인단다.

 

Lambda, Higher-order functions

Lambda 

얘는 그냥 사진으로 보여주는 게 빠를 듯하다.

 

Higher-order functions

- 고차 함수라고도 한다.
- 함수를 파라미터로 받거나, 함수를 반환하면 고차원 함수.

fun encodeMsg(msg: String, encode: (String) -> String): String {
	return encode(msg)
}

encode라는 함수를 파라미터로 받고, 해당 함수를 반환한다.

결과는 HELLO로 출력이 된다.

encode는 함수 타입 (String) -> String을 요구하기 때문에 (String) -> String 형태인 enc1을 선언하여 넣는것이다.

 

:: 연산자를 사용하여 선언된 함수를 다른 함수에 인수로 전달할 수도 있다고 한다. 근데 이렇게 많이 쓰나?? 

fun enc2(input:String): String = input.reversed()
encodeMessage("abc", ::enc2)
// :: 를 쓰지않으면 오류 뜬다.

자료를 번역해보면 :: 연산자는 코틀린에게 함수 참조를 인수로 전달하고 함수를 호출하려고 하지 않는다는 것을 알려준다고 한다.
그니까 함수를 인수로 전달만 할 뿐이지, 얘를 불러서 실행시킨다던지 그 느낌이 아니라는 걸 알려주는 것 같다.. (자신은 없음 ㅠㅠ)

혹은 람다 형식으로 넣어줄 수 있다.

encodeMessage("acronym", { input -> input.toUpperCase() })

// () 밖에서 해도 됨
encodeMsg("acronym") { input -> input.toUpperCase() }

 

 

List Filter, Lazy, Eager

Filter의 경우 별 다른 설명없이 예제 코드만 쓰고 넘어간다. 그냥 조건을 넣어서 필터링 한다 그 뿐이기 때문에..

val ints = listOf(1, 2, 3) 
ints.filter { it > 0 }

ints.filter{n:Int->n>0}
// 혹은
ints.filter{n->n>0}

val books = listOf("nature", "biology", "birds") 
println(books.filter { it[0] == 'b' })
//[biology, birds]


val instruments = listOf("viola", "cello", "violin") 
val eager = instruments.filter { it [0] == 'v' } 
println("eager: " + eager)
//eager: [viola, violin]

- lazy : 런타임에 필요한 경우에만 발생한다. (값이 클 경우 유용)
- eager : 결과가 사용되었는지 여부에 관계없이 발생한다.