kotlin in action 2장 - 기초
2022. 1. 24. 18:24ㆍkotlin & 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 와 같은 문법으로 사용할 수 있다.
- val 멤버변수는 getter 와 private field 를 만들어낸다.
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 |
---|