728x90
Enum
enum class Color {
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
- Enum은 자바 선언보다 코틀린 선언에 더 많은 키워드를 써야 한다
- 코틀린에서는 enum class를 사용한다.
- 코틀린에서 enum은 소프트 키워드 라 부른다.
- enum은 class 앞에 있을 때는 특별한 의미를 지니지만 다른 곳에서는 이름에 사용할 수 있다.
Enum 클래스 안에 프로퍼티와 메서드 쓰기
enum class Color(var 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(args: Array<String>) {
println(Color.BLUE.rgb())
}
- 일반적인 클래스와 마찬가지로 생성자와 프로퍼티를 선언한다.
- 각 enum 상수를 정의할 때는 그 상수에 해당하는 프로퍼티 값을 지정해야만 한다.
- 세미콜론이 필수로 들어가야 한다.
- enum 클래스 안에서 메서드를 정의하는 경우 반드시 enum 상수 목록과 메서드 정의 사이에 세미콜론을 넣어야 한다
When으로 enum 클래스 다루기
자바의 switch에 해당하는 코틀린 구성요소가 when이다.
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(args: Array<String>) {
println(getMnemonic(Color.BLUE))
}
- color로 전달된 값과 같은 분기를 찾는다.
- 각 분기의 끝에 break를 넣지 않아도 된다.
- 성공적으로 매치되는 분기를 찾으면 switch는 그 분기를 실행한다.
- 한 분기 안에서 여러 값을 매치 패턴으로 사용할 수도 있다.
- 값 사이를 콤마(,)로 분리한다.
fun getWarmth(color: Color) =
when(color){
Color.RED, Color.ORANGE, Color.YELLOW -> "warm"
Color.GREEN -> "neutral"
Color.BLUE, Color.INDIGO, Color.VIOLET -> "cold"
}
fun main(args: Array<String>) {
println(getWarmth(Color.ORANGE))
}
더 간단하게 만들기
import Color.*
fun getWarmth(color: Color) =
when(color){
RED, ORANGE, YELLOW -> "warm"
GREEN -> "neutral"
BLUE, INDIGO, VIOLET -> "cold"
}
When과 임의의 객체를 함께 사용
when의 분기 조건은 임의의 객체를 허용한다.
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")
}
println(mix(Color.BLUE, Color.YELLOW))
- when 식의 인자로 아무 객체나 사용할 수 있다.
- 코틀린 표준 라이브러리에는 인자로 전달받은 여러 객체를 그 객체들을 포함하는 집합인 Set객체로 만드는 setOf함수가 있다.
- 집합은 원소가 모여 있는 컬랙션으로 각 원소의 순서는 중요하지 않다
- when 식은 인자 값과 매치하는 조건값을 찾을 때까지 각 분기를 검사한다.
- 여기서는 객체 사이를 매치할 때 동등성을 사용한다.
- when 분기 조건 부분에 식을 넣을 수 있기 때문에 많은 경우 코드를 더 간결하고 아름답게 작성이 가능하다
인자 없는 when 사용
이전의 코드는 함수 인자로 주어진 두 색이 when의 분기 조건에 있는 다른 두 색과 같은지를 비교하기 위해 여러 Set인스턴스를 생성하는 단점이 있다.
이러한 함수가 자주 호출되면 불필요한 가비지 객체가 늘어나므로 고치는 것이 좋다
fun mixOptimized(c1:Color, c2: Color) =
when {
(c1 == RED && c2 == YELLOW) || (c2 == RED && c1 == YELLOW) ->
ORANGE
(c1 == YELLOW && c2 == BLUE) || (c2 == BLUE && c1 == YELLOW) ->
GREEN
(c1 == BLUE && c2 == VIOLET) || (c2 == VIOLET && c1 == BLUE) ->
INDIGO
else -> throw Exception("Dirty color")
}
println(mixOptimized(Color.BLUE, Color.YELLOW))
- when에 아무 인자도 없으려면 각 분기의 조건이 불리언 결과를 계산하는 식이어야한다.
- 추가 객체를 만들지 않는다는 장점이 있지만 가독성이 떨어진다.
스마일 캐스트
트리구조의 SUM , NUM의 조합 구하기
interface Expr
class Num(val value: Int) : Expr //Value라는 프로퍼티만 존재하는 단순한 클래스로 Expr 인터페이스를 구현한다
//Expr 타입의 객체라면 어떤 것이나 sum 연산의 인자가 될 수 있다.
//따라서 Num이나 다른 Sum 인자가 올수 있다.
class Sum(val left: Expr, val right: Expr) : Expr
fun main(args: Array<String>) {
println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
}
- 식을 트리 구조로 저장한다
- NUM 은 항상 말단 노드, Sum은 자식이 둘 있는 중간 노드이다
- 식을 위한 인터페이스(Expr) 이 있고, Sum, Num 클래스는 그 Expr인터페이스를 구현한다.
- Expr인터페이스는 아무 메서드도 선언하지 않으며, 단지 여러 타입의 식 객체를 아우르는 공통 타입 역할만 수행한다
- 클래스가 구현하는 인터페이스를 지정하기 위해서 콜론(:) 뒤에 인터페이스 이름을 사용한다
- Expr인터페이스에는 두 가지 구현 클래스가 존재하므로 2가지 경우를 고려해야 한다
- 어떤 식이 수라면 그 값을 반환
- 어떤 식이 합계라면 좌항과 우항의 값을 계산한 다음 두 값을 합한 값을 반환
코틀린에서 자바 형식으로 작성
fun eval(e: Expr): Int{
if(e is Num){
val n = e as Num
return n.value
}
if(e is Sum){
return eval(e.right) + eval(e.left)
}
throw IllegalArgumentException("Unknown expression")
}
- 코틀린에서는 is를 사용해 변수 타입을 검사한다
- 자바의 instanceof와 비슷하다
- 자바에서는 instanceof로 확인 한 다음 그 타입에 속한 멤버에 접근하기 위해 명시적으로 변수 타입을 캐스팅해야 한다
- 코틀린에서는 프로그래머 대신 컴파일러가 캐스팅을 해준다
- is로 검사하고 나면 굳이 변수를 원하는 타입으로 캐스팅하지 않아도 마치 처음부터 그 변수가 원하는 타입으로 선언된 것처럼 사용 가능하다
- 실제로는 컴파일러가 캐스팅해준 것 => 스마트 캐스트
- 스마트 캐스트는 is로 변수에 든 값의 타입을 검사한 다음에 그 값이 바뀔 수 없는 경우에만 작동한다.
- val 이 아니거나 val이지만 커스텀 접근자를 사용하는 경우에는 해당 프로퍼티에 대한 접근이 항상 같은 값을 내놓는다고 확신할 수 없기 때문
if를 When으로 변경
코틀린에서는 if가 값을 만들어 내기 때문에 3항 연산자가 따로 없다.
fun eval(e: Expr): Int =
if (e is Num) {
e.value
} else if (e is Sum) {
eval(e.right) + eval(e.left)
} else {
throw IllegalArgumentException("Unknown expression")
}
fun main(args: Array<String>) {
println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
}
- if의 분기에 식이 하나밖에 없다면 중괄호 생략 가능
- if분기에 블록을 사용하는 경우 그 블록의 마지막 식이 그 분기의 결괏값이다.
when으로 변환
fun eval(e: Expr): Int =
when (e) {
is Num -> {
e.value
} is Sum -> {
eval(e.right) + eval(e.left)
} else -> {
throw IllegalArgumentException("Unknown expression")
}
}
- is를 사용하게 된다면 스마트 캐스트가 이뤄지기 때문에 Num, Sum변수에 접근할 때 변수를 강제로 캐스팅할 필요가 없다
- if나 when의 각 분기에서 수행해야 하는 로직이 복잡해지면 분기 본문에 블록을 사용할 수 있다.
if와 When의 분기에서 블록 사용
if와 When의 분기에서 블록 사용할 경우 마지막 문장이 블록 전체의 결과가 된데
fun evalWithLogging(e: Expr): Int =
when (e) {
is Num -> {
println("num:${e.value}")
e.value
}
is Sum -> {
val left = evalWithLogging(e.left);
val right = evalWithLogging(e.right);
println("sum: $left + $right")
left + right
}
else -> throw IllegalArgumentException("Unknown expression")
}
fun main(args: Array<String>) {
println(evalWithLogging(Sum(Sum(Num(1), Num(2)), Num(4))))
}
- 블록의 마지막 식이 블록의 결과라는 규칙은 블록이 값을 만들어내야 하는 경우 항상 성립한다.
- 이 규칙은 함수에 대해서는 성립하지 않는다.
- 식이 본문인 함수는 블록을 본문으로 가질 수 없고 블록이 본문인 함수는 내부에 return 문이 반드시 있어야 한다
728x90
'KotlinInAction > 코틀린 기초' 카테고리의 다른 글
| 코틀린의 예외 처리 (0) | 2022.07.26 |
|---|---|
| While과 for 루프 (0) | 2022.07.26 |
| 클래스와 프로퍼티 (0) | 2022.07.25 |
| 함수와 변수 (0) | 2022.07.25 |