Kotlin 기본 문법

함수

fun max(a: Int, b: Int): Int {
  return if (a > b) a else b
}
  • 함수는 fun 키워드로 선언한다.

  • 파라미터 이름 뒤에 타입을 쓴다.
  • 최상위 수준에 정의할 수 있다.
  • 블록이 본문인 함수

문(statement) vs. 식(expression)

코틀린의 if는 문장이 아닌 식으로서 결과를 만들 수 있다. 또한, 식은 다른 식의 하위 요소로 계산에 참여할 수 있지만, 문은 자신을 둘러싸는 가장 안쪽 블록의 최상위 요소로 존재하고 아무런 값을 만들어내지 않는다.

fun max(a: Int, b: Int): Int = if (a > b) a else b

// 반환 타입 생략
fun max(a: Int, b: Int) = if (a > b) a else b
  • 식이 본문인 함수
    • 반환 타입을 적지 않아도 컴파일러가 본문을 해석하면서 결과 타입을 함수 반환 타입으로 정한다.
    • 타입 추론(type inference): 컴파일러가 타입을 분석해 구성 요소 타입을 정해주는 기능

변수

val question = "How many Harry Potter movies are there?"
val answer = 7
val anotherAnswer: int = 7
val year: Int
year = 2021
  • 키워드로 변수 선언 시작
  • val 은 변경 불가능한 참조를 저장하는 변수로 초기화한 후에 재대입이 불가능하다
  • var 은 값을 변경할 수 있으며 변수 타입은 바뀌지 않는다.₩
  • 타입 지정을 생략할 수 있다.
  • 타입 지정을 할 경우, 변수 이름 뒤에 명시한다.
  • 타입을 지정하지 않으면 컴파일러가 초기화식을 분석해서 변수 타입으로 지정한다.
  • 초기화를 하지 않고 변수를 선언할 때는 타입 추론이 어려우므로 변수 타입을 명시해야 한다.
val message: String
if (canPerformOperation()) {
  message = "Success"
} else {
  message = "Failure"
}
  • val 변수는 블록을 실행할 때 한 번만 초기화 되어야 한다.
  • 컴파일러가 확인할 수 있다면 조건에 따라 여러 값으로 초기화할 수 있다.
val languages = arrayListOf("java")
languages.add("kotlin")
  • val 참조 자체는 불변이지만, 참조가 가리키는 객체의 내부 값은 변경될 수 있다.
var x = 0

fun incrementX() {
  x += 1
}
  • 변수를 최상위 수준에 정의할 수 있다.

문자열 템플릿

fun main(args: Array<String>) {
  val name = if (args.size > 0) args[0] else "World"
  println("Hello $name")
}

fun main2(args: Array<String>) {
  if (args.size > 0) {
    println("Hello ${args[0]}")
  }
}

fun main3(args: Array<String>) {
  println("Hello ${if (args.size > 0) args[0] else "World"}")
}
  • $를 변수 앞에 추가해서 문자열 안에 변수를 사용할 수 있다.

  • 변수 이름이 아닌 식도 중괄호({})로 싸서 문자열 템플릿 안에 넣을 수 있다.

  • 중괄호로 둘러싼 식 안에는 큰 따옴표(““)를 쓸 수 있다.

클래스

class Person{
  val name: String,
  var isMarried: Boolean
}

val person = Person("Harry", true)
println(person.name)				// "Harry"
println(person.isMarried)		// true
person.isMarried = false
  • 필드와 접근자 메서드를 프로퍼티가 대신한다.
  • val로 선언한 프로퍼티는 읽기 전용
  • 읽기 전용 프로퍼티는 getter만 선언
  • var로 선언한 프로퍼티는 변경 가능
  • 변경 가능한 프로퍼티는 getter, setter 모두 선언
  • 프로퍼티를 직접 사용해도 자동으로 getter를 호출한다.
  • 프로퍼티를 변경할 때도 직접 사용해서 변경한다.
class Rectangle(var height: Int, var width: Int) {
  val isSquare: Boolean
    get() {
      return height == width
    }
  	// get() = height == width 도 가능
}

fun main() {
  r = Rectangle(12, 12)
  println(r.isSquare)
}
  • 값을 필드에 저장하지 않고 프로퍼티 게터를 선언하여 접근할 때마다 확인할 수 있다.

enum & when

enum class Color (
	val r: Int, val g: Int, val b: Int    // 상수 프로퍼티 정의
) {
  // 상수를 생성할 때 프로퍼티 값 지정
  RED(255, 0, 0), ORANGE(255, 165, 0), YELLOW(255, 255, 0), 
  GREEN(0, 255, 0), BLUE(0, 0, 255), INDIGO(75, 0, 130), VIOLET(238, 130, 238);
  
  fun rgb() = (r * 256 + g) * 256 + b
}

fun main() {
  println(Color.BLUE.rgb())  // 255
}
  • enum은 soft keyword로 다른 곳에서는 이름에 사용할 수 있다.

  • enum 클래스 안에 프로퍼티나, 메소드 정의가 가능하다.
  • 클래스 안에 메서드를 정의할 때는 상수와 메서드 사이에 ; 을 사용해야 한다.
fun getMnemonic(color: Color) = 
	when (color) {
    Color.RED -> "Richard"
    Color.ORANGE -> "Of"
    Color.YELLOW -> "York"
    Color.GREEN -> "Gave"
    Color.BLUE -> "Battle"
    Color.INDIGO -> "In"
    Color.VIOLET -> "Vain"
  }

fun main() {
  println(getMnemonic(Color.GREEN))   // "Gave"
}
  • if처럼 when도 값을 만들어내는 식이다.
  • 분기 끝에 break를 붙이지 않아도 된다.
fun getWarmth(color: Color) =	when (color) {
    Color.RED, Color.ORANGE, Color.YELLO -> "warm"
    Color.GREEN -> "neutral"
    Color.BLUE, Color.INDOGO, Color.VIOLET -> "cold"
  }

fun main() {
  println(getWarmth(Color..BLUE))   // "cold"
}
  • 하나의 분기에서 여러 값을 사용할 때는 ,를 이용하여 구분한다.
import Color.*

fun mix(c1: Color, c2: Color) =
	when(setOf(c1, c2)) {
    setOf(RED, YELLOW) -> ORANGE
    setOf(YELLOW, BLUE) -> GREEN
    setOf(BLUE, VIOLET) -> INDIGO
    else -> throw Exception("Dirty color")
  }

fun main() {
  println(mix(BLUE, YELLOW))  // "GREEN"
}
  • when의 분기 조건에서 임의의 객체를 허용한다.
import Color.*

fun mixOptimized(c1: Color, c2: Color) =
  when {
    (c1 == RED && c2 == YELLOW) || (c1 == YELLOW && c2 == RED) -> ORANGE
    (c1 == YELLOW && c2 == BLUE) || (c1 == BLUE && c2 == YELLOW) -> GREEN
    (c1 == BLUE && c2 == VIOLET) || (c1 == VIOLET && c2 == BLUE) -> INDIGO
    else -> throw Exception("Dirty Color")
  }


fun main() {
    println(mixOptimized(BLUE, YELLOW))
}
  • 인자가 없는 when 식을 사용하여 불필요한 객체 사용을 막을 수 있다.
  • when에 인자가 없으려면 분기의 조건이 boolean 결과를 계산하는 식이어야 한다.