2. 코틀린 기초
CS/코틀린 인 액션

2. 코틀린 기초

1. 함수와 변수

코틀린 함수 정의는 다음과 같이 한다

fun max(a: Int, b: Int): Int {
	return if (a > b) a else b
}

주목할 건 코트린에서 if는 문(statement)이 아닌 식(expression)이다. 즉, 값을 내놓는 것.

중괄호가 없다면 중괄호의 마지막 줄을 return한다.

 

val(value)는 한번 초기화하면 끝나고(immutable)(자바의 final), var(variable)은 변경 가능(mutable)한 참조다.

 

2. 클래스와 프로퍼티

다음 자바의 코드와 코틀린의 코드는 같다.

// 자바
public class Person {
    private final String name;
    
    public Person(String name) {
    	this.name = name;
    }
    
    public String getName() {
    	return name;
    }
}
// 코틀린
class Person(val name: String)

이유는 필드와 접근자를 한데 묶어 프로퍼티(property)라고 부르는데, 코틀린은 기본적으로 이 프레임워크를 활용하고 있기 때문. val는 getter만 내부에서 만들고, var는 getter, setter 둘 다 만든다.

 

class Person {
    val name: string, // getter만 만듦
    var isMarried: Boolean // setter도 만듦
}

 

그래서 자바와 코틀린의 사용법도 다음과 같이 다르다.

// 자바
Person person = new Person("Bob", true);
System.out.println(person.getName());
System.out.println(person.isMarried());
// 코틀린
val person - Person("Bob", true)
println(person.name)
println(person.isMarried)

저렇게 그냥 변수를 가져오는것 같이 보여도 사실 getter를 사용하는 것. setter를 사용하는 것도 person.isMarried = false를 쓴다.

 

커스텀 접근자는 다음과 같이 만든다.

class Rectangle(val height: Int, val width: Int) {
    val isSquare: Boolean
    	get() { // 프로퍼티 게터 선언
        	return height == width
        }
}

비록 val이지만 상황에 따라 다르게 출력된다.

 

 

3. 선택 표현과 처리: enum과 when

코틀린도 enum를 정의하지만, 뒤에 class가 붙는다.

enum class Color {
    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
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
}
println(Color.BLUE.rgb())

 

식을 위한 Expr도 있다는데 솔직히 잘 모르겠음. 직접 해야 이해가는 애인것 같다.

 

interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr

fun eval(e: Expr): Int {
    if (e is Num) {
    	val n = e as Num // e에서 타입 추론할거라 사실 as Num은 중복임
        return n.value
    }
    if (e is Sum) {
    	return eval(e.right) + eval(e.left)
    }
    throw IllegalArgumentException("Unknown expression")
}
println(eval(Sum(Sum(Num(1), Num(2)), Num(4))) // 7

IDE에서 e를 컴파일러가 캐스팅을 수행해주며 이를 스마트 캐스트 라고 부른다.

 

if와 when을 중괄호 없이 다음처럼도 정의 가능

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")
    }

println(eval(Sum(Num(1), Num(2)))
fun eval(e: Expr): Int = 
    when (e) {
        is Num -> 
        	e.value
        is Sum -> 
        	eval(e.right) + eval(e.left)
        else -> 
        	throw IllegalArgumentException("Unknown expression")
	}

 

4. 대상을 이터레이션: while과 for 루프

 

fun fizzBuzz(i: Int) = when {
    i % 15 == 0 -> "FizzBuzz "
    i % 3 == 0 -> "Fizz "
    i % 5 == 0 -> "Buzz "
    else -> "$i "
}
for (i in 1..100) {
	print(fizzBuzz(i))
}
for (i in 100 down To 1 step 2) {
	print(fizzBuzz(i))
}

코틀린의 1..10은 폐괄호라 10까지 포함하여 표시됨. 싫으면 1 until 10 하면 된다.

 

Map으로 ((key, value) in maps) 의 반복도 가능. 또 .withIndex() (python의 enumerate)도 있음.

원소 검사 (c in 'a'..'z' || c in 'A'..'Z')로 포함, (c !in 'a'..'z')로 미포함 여부 검사 가능(boolean 반환). when과 함께도 사용 가능

 

 

5. 코틀린 예외 처리

기존 자바의 try, catch, finally와 매우 유사. 하지만 if처럼 식으로 사용 가능하기도 함.

fun readNumber(reader: BufferReader): Int? {
    try {
    	val line = reader.readLine()
        return Interger.parseInt(line)
    }
    catch (e: NubmerFormatException) {
    	return null
    }
    finally {
    	reader.close()
    }
}

val reader = BufferedReader(StringReader("239"))
println(readNumber(reader))

 

 

fun readNumber(reader: BufferReader): Int? {
    val number = try {
    	Integer.parseInt(reader.readLine())
    }
    catch (e: NubmerFormatException) {
    	return
    }
    println(number)
}

val reader = BufferedReader(StringReader("not a number"))
println(readNumber(reader)) // 아무것도 출력되지 않음

이럴 경우 함수가 그냥 return에서 끝나서 number에는 아무것도 저장되지 않음. 계속 하게 하려면 null을 반환하면 된다.

 

fun readNumber(reader: BufferReader): Int? {
    val number = try {
    	Integer.parseInt(reader.readLine())
    }
    catch (e: NubmerFormatException) {
    	null
    }
    println(number)
}

val reader = BufferedReader(StringReader("not a number"))
println(readNumber(reader)) // null