kotlin in action 2장 - 기초

2022. 1. 24. 18:24kotlin & spring

2장에서는 변수,함수,클래스, 프로퍼티, 제어구조, 캐스팅, 예외처리에 대해 다룬다.

 

2.1 기본요소: 함수와 변수

2.1.1 Hello world!

  • 코틀린에서는 함수를 선언할때 fun 키워드 사용
  • 파라미터 이름 뒤에 파라미터 타입을 쓴다.
    fun main(args: Array<String>) {
      println("Hello, world!")
    }
  • 함수를 최상위 레벨에 선언할수 있다. 자바와 다르게 꼭 클래스 안에 넣을 필요는 없다
  • 배열도 일반적인 클래스와 마찬가지다. 자바와 달리 배열을 위한 문법이 따로 없다
  • System.out.println -> println 간결하게 쓸수있다.
  • 코드 끝에 세미콜론 생략해도 된다.

2.1.2 함수

  • 함수의 리턴타입은 파라미터 목록을 닫는 괄호 뒤에 추가한다.
    fun max(a: Int, b: Int): Int {
      return if (a > b) a else b
    }​
  • 위 예제에서 if 문은 반환값이 있는 expression 으로 동작한다. 이는 다른 언어와 차별점이다 (자바에서는 if 문은 반환값이 없는 statement 이다)
  • 위 예제를 더 줄일 수 있다. 책에서는 "식이 본문인 함수"라고 부르는데 공식적으로는 "단일 표현식 함수"라고 부르는게 맞다. 영어로는 "single expression function" 이라고 부른다.
    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
  • 코틀린 컴파일러가 a, b 변수의 타입을 기반으로 리턴타입을 추론해주기때문에 리턴타입을 생략할수 있다.
  • 위와 같이 리턴타입 생략하는건 단일 표현식 함수에서만 가능하다.

2.1.3 변수

  • 코틀린에서는 변수에 대한 타입선언을 생략하는 경우가 흔하다.
  • 변수 선언할때 사용하는 val 키워드와 var 키워드 
    • val: immutable
    • var: mutable
  • 코틀린 철학상 왠만하면 변수를 val(불변) 으로 선언하는걸 권장한다.
  • val 변수가 참조하는 레퍼런스는 불변일지라도 레퍼런스 대상 객체의 내부 값은 바뀔수 있다.

2.1.4 문자열 템플릿

  • 자바의 문자열 덧셈을 대체할수 있는 기능으로 간결하게 변수의 값을 String 에 삽입할수 있다.
  • 존재하지 않는 변수를 템플릿에 사용하려고 하면 컴파일 오류가 발생한다.
  • backslash 로 $ 표현을 escape 할수있다
  • 문자열과 충돌이 일어나는 경우 ${변수명} 으로 해결할 수 있다.

 

2.2 클래스와 프로퍼티

  • 코틀린에서는 프로퍼티 선언을 생성자 내부에 할수 있다.
  • class Person(val name: String)
  • 위 한줄이면 생성자선언과, 멤버변수 대입까지 다 처리된다.

2.2.1 프로퍼티

  • 자바에서는 보통 멤버변수를 private 로 하고 getter setter 를 제공하지만, 코틀린에서는 이를 언어 기본 기능으로 제공한다.
    • val 멤버변수는 getter 와 private field 를 만들어낸다.
      • name 이라는 멤버변수는 getName 으로 접근하거나, 그냥 .name 해서 접근도 된다.
    • var 멤버변수에는 setter 도 만들어진다
      • setter 는 a.isMarried = true 와 같은 문법으로 사용할 수 있다.

2.2.2 커스텀 접근자

    • 아래와 같이 사용할수 있다
class Rectangle(val height: Int, val width: Int) {
  val isSquare: Boolean
    get() {
      return height == width
    }
}
  • 그냥 함수를 정의하는것과 성능상 차이는 없다.
  • 다만 표현 방식의 차이일 뿐이고, rectangle.isSquare 와 같은 형태로 사용할수 있기 때문에 가독성의 차이가 난다.

 

2.2.3 코틀린 소스코드 구조: 디렉터리와 패키지

  • 자바와 비슷
  • 같은 패키지에 속해있다면 import 없이 사용 가능
  • 다른패키지라면 import
  • wildcard(*) 로 모든 선언 import 할수 있음
  • 자바와 다르게 한개의 파일에 여러 최상위 클래스 선언해도 괜찮음

2.3 선택 표현과 처리:  enum, when

2.3.1 enum 클래스 정의

  • enum 클래스 선언
enum class Color {
  RED, ORANGE, YELLOW
}
  • enum 에도 생성자와 프로퍼티, 메소드 선언 할수 있음
enum class Color(val r: Int, val g: Int, val b: Int) {
  RED(255, 0, 0), ORANGE(255, 165, 0)
  
  fun rgb() = (r * 256 + g) * 256 + b
}

 

2.3.2 when 으로 enum 다루기

fun getWarmth(color: Color) = when (color) {
  Color.RED, Color.ORANGE -> "warm"
  Color.Green -> "neutral"
  else -> "haha"
}
  • enum -> 대응하는 문자열
  • 여러개인 경우 콤마로 구분
  • else 로 나머지 값들에 대응하는 처리

2.3.3 when 과 임의의 객체 함께 사용

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

set 을 equality 비교하기 때문에 같은 원소를 포함한다면 조건을 만족시킬수 있다.

 

2.3.4 인자 없는 when

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

이런식으로 다양한 조건 활용 할수 있으나, set과 같은 컬렉션을 이용하는것에 비해 가독성이 안좋아질수 있다.

 

2.3.5 스마트캐스트: 타입 검사와 타입캐스트

  • is 를 사용하여 타입검사와 스마트캐스팅을 동시에 할수 있다.
  • 자바의 instanceof 와 비슷하지만, 코틀린은 컴파일러가 is 로 검사된 변수를 스마트캐스팅 해준다.
  • as 키워드로 명시적 캐스팅이 가능하지만, if 문에서 is 로 검사했다면 해당 블록 내에서는 명시적 캐스팅을 생략해도된다.

 

2.3.6 리팩토링: if 를 when으로 변경

  • 여러개의 if 문 체인을 when 으로 변경하면 보기 좋다.
  • when 조건블록안에 is 키워드를 사용하면 대응하는 expression 에도 스마트캐스팅이 적용된다.
when (e) {
  is Num -> e.value
  is Sum -> eval(e.right) + eval(e.left)
  else -> throw IllegalArgumentException("Unknown expression")
}

 

2.3.7 if 와 when 분기에서 블록 사용

when (e) {
  is Num -> {
    println("num: ${e.value}")
    e.value
  }
  ...
}
  • 코틀린에서는 block 도 리턴값이 있으므로, 당연히 블록 사용가능하다. 
    • 위 예제에서는 e.value 가 리턴값이 된다.
    • "블록의 마지막 식이 블록의 결과" 가 된다.

2.4 이터레이션: while 과 for 루프

2.4.1 while 루프

  • while
  • do-while
  • 위 두가지는 자바와 다르지 않다

2.4.2 숫자 이터레이션: 범위와 수열

  • 코틀린에는 자바의 for (다른 언어들에 존재하는 for(i=0;i<10;i++) 이런 형태) 가 없다.
  • 대신에 범위(range) 를 사용
  • 범위는 두 값으로 이루어진 구간. 시작값과 끝 값이 있다
val oneToTen = 1..10
  • 양 끝값을 포함한다.
  • 역순과 증가값 사용
for (i in 100 downTo 1 step 2) {
  println(i)
}

100
98
96
...
  • until 을 사용하면 끝 값을 포함하지 않는다
  • 즉 (x in 0 until size) 는 (x in 0..size - 1) 과 동일하다

2.4.3 맵에 대한 이터레이션

  • 컬렉션에 대해서 자바와 비슷하게 for in 루프를 사용한다.

 

 for((key, value) in map) {
   println("$key = $value")
 }​
  • 코틀린에서는 map에 get / put 을 하는 대신 map[key], map[key] = value 와 같은 문법으로 값을 가져오거나 설정할수 있다.
  • 맵이 아닌 리스트에서도 위와 동일한 문법이 가능하다
for ((index, element) in list.withIndex()) {
  println("$index: $element")
}
  • 한편, in 키워드는 이터레이션에도 사용되지만 특정 원소가 들어있는지 검사하는 오퍼레이션의 키워드로도 사용된다.

2.4.4 in 으로 컬렉션이나 범위의 원소 검사

fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
fun isNotDigit(c: Char) = c !in '0'..'9'
  • in 연산자는 앞에 ! 를 붙여서 negate 할수있다
  • range 는 Comparable 인터페이스를 구현한 객체라면 모두 사용할수 있다.
  • 컬렉션에도 in 연산을 사용할 수 있다.
println("Kotlin" in setOf("Java, "Scala"))
=> false

2.5 예외 처리

  • 다른 언어와 비슷한 예외처리 방식
  • 자바와 달리 코틀린의 throw는 식이므로 다른 식에 포함될수 있다.

2.5.1. try, catch, finally

  • 자바와 마찬가지로 try catch finally 사용 가능
  • 자바와 다르게 checked exception 개념이 없다

2.5.2 try 를 식으로 사용

fun readNumber(reader: BufferedReader) {
  val number = try {
    Integer.parseInt(reader.readLine())
  } catch (e: NumberFormatException) {
    return
  }
  
  println(number)
}
  • try 는 코틀린의 다른 제어문과 같이 식이다. try 의 결과값을 변수에 대입할 수 있다.
  • 위 예제는 number 를 변수에 담거나, 오류가 발생하는 경우 함수에서 리턴한다.
  • catch 문에서도 결과값을 돌려줄수 있다.
fun readNumber(reader: BufferedReader) {
  val number = try {
    Integer.parseInt(reader.readLine())
  } catch (e: NumberFormatException) {
    0 // <- 이렇게
  }
  
  println(number)
}

'kotlin & spring' 카테고리의 다른 글

코틀린 java-test-fixtures 플러그인  (0) 2021.04.24