[Kotlin in Action] 02장 - 코틀린 기초
2장 코틀린 기초
2.1 기본 요소: 함수와 변수
- 함수를 정의할 때 fun 키워들 사용한다.
2.1.1 Hello, World
fun main(args: Array<String>) {
println("Hello, world!")
}
2.1.2 함수
fun max(a: Int, b:Int): Int {
return if (a > b) a else b
}
식이 본문인 함수
fun max(a: Int, b: Int): Int = if (a > b) a else b
2.1.3 변수
- 코틀린에서는 변수 이름 뒤에 타입을 명시하거나 생략 가능
val answer = 42
val answer: Int = 42
변경 가능한 변수와 변경 불가능한 변수
- val(value)
- 변경 불가능한 참조를 저장하는 변수
- 초기화하고 나면 재대입이 불가능
- 자바의 final
- var(variable)
- 변경 가능한 참조
- 자바의 일반 변수
2.1.4 문자열 템플릿
fun main(args: Array<String>) {
val name = if (args.size > 0) args[0] else "Kotlin"
println("Hello, $name!")
}
- 문자열 템플릿
- 자바의 (“Hello, “ + name + “!”) 과 동일
- 존재하지 않는 변수를 문자열 템플릿 안에서 사용하면 컴파일 오류 발생
- 특수문자 사용 시
- println(“$x”)
- 복잡한 식 사용 시 중괄호 사용
- println(“Hello, ${args[0]}!”)
- println(“Hello, ${if (args.size > 0) args[0] else “someone”}!”)
- 컴파일된 코드는 StringBuilder를 사용하고 문자열 상수와 변수의 값을 append로 문자열 빌더 뒤에 추가함
2.2 클래스와 프로퍼티
Person 클래스
/* 자바 */
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
/* 코틀린 */
class Person(val name: String)
- 코틀린의 기본 가시성은 public이므로 이런 경우 생략 가능
2.2.1 프로퍼티
- 자바에서는 필드와 접근자를 한데 묶어 프로퍼티라고 부른다.
클래스 안에서 변경 가능한 프로퍼티 선언하기
class Person {
val name: String, // 읽기 전용 프로퍼티 - 코틀린은 (비공개)필드와 (공개) 게터를 만들어낸다.
var isMarried: Boolean // 쓸 수 있는 프로퍼티 - 코틀린은 (비공개)필드, (공개) 게터, (공개) 세터를 만들어낸다.
}
Person 클래스 사용
/* 자바 */
Person person = new Person("Bob", true);
person.getName();
person.isMarried();
person.setMarried(false);
/* 코틀린 */
val person = Person("Bob", true)
person.name
person.isMarried
person.isMarried = false
2.2.2 커스텀 접근자
class Rectangle (val height: Int, val width: Int) {
val isSquare: Boolean
get() { //프로퍼티 게터 선언
return height == width
}
}
2.3 enum과 when
2.3.1 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), YELLOW(255, 255, 0);
fun rgb() = (r * 256 + g) * 256 + b
}
Color.RED.rgb()
2.3.2 when으로 enum 클래스 다루기
when을 사용해 올바른 enum 값 찾기
- 자바의 switch
fun getMnemonic(color: Color) =
when (color) {
Color.RED -> "Richard"
Color.ORANGE -> "Of"
Color.YELLOW -> "York"
}
getMnemonic(Color.RED)
한 when 분기 안에 여러 값 사용하기
fun getMnemonic(color: Color) =
when (color) {
Color.RED, Color.ORANGE -> "warm"
Color.YELLOW -> "Of"
}
getMnemonic(Color.RED)
2.3.3 when과 임의의 객체를 함께 사용
- 코틀린 when의 분기 조건은 임의의 객체를 허용
fun mix(c1: Color, c2: Color) =
when (setOf(c1, c2)) { //when 식의 인자로 아무 객체나 사용 가능
setOf(RED, YELLOW) -> ORANGE
setOf(YELLOW, BLUE) -> GREEN
else -> throw Exception("Dirty color")
}
mix(BLUE, YELLOW)
- Set 객체로 만드는 setOf 함수 사용
2.3.4 인자 없는 when 사용
- 2.3.3 예제의 비효율점
- 매번 Set 인스턴스를 생성
- 가비지 객체 늘어남
- 인자 없는 when 사용 시 불필요한 객체 생성 막을 수 있음
- 그러나 가독성은 떨어짐
- 매번 Set 인스턴스를 생성
fun mixOptiomized(c1: Color, c2: Color) =
when {
(c1 == RED && c2 == YELLOW) || (c1 == YELLOW && c2 == RED) -> ORANGE
(c1 == YELLOW && c2 == BLUE) || (c1 == BLUE && c2 == YELLOW) -> GREEN
else -> throw Exception("Dirty color")
}
mixOptiomized(BLUE, YELLOW)
2.3.5 스마트 캐스트 : 타입 검사와 타입 캐스트를 조합
- 스마트 캐스팅 - 변수를 원하는 타입으로 캐스팅하지 않아도 원하는 타입으로 사용할 수 있는 것
- 컴파일러가 캐스팅 수행
- 원하는 타입으로 명시적으로 타입 캐스팅하려면 as 키워드 사용
- val n = e as Num
2.3.6 리팩토링: if를 when으로 변경
- 코틀린의 if가 값을 만들어내기 때문에 자바와 달린 3항 연산자가 따로 없다.
값을 만들어내는 if 식
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")
}
- if 분기에 식이 하나밖에 없다면 중괄호 생략 가능
- 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")
}
2.4 대상을 이터레이션: while과 for 루프
2.4.2 수에 대한 이터레이션: 범위와 수열
- 코틀린에서는 범위를 사용
for(i in 1..100) {
print(fizzBuzz(i))
}
증가 값을 갖고 범위 이터레이션 하기
for(i in 1..100 downTo 1 step 2) {
print(fizzBuzz(i))
}
98 94 92...
2.4.3 맵에 대한 이터레이션
맵을 초기화하고 이터레이션하기
val binaryReps = TreeMap<Char, String>()
for (c in 'A'...'F') {
val binary = Integer.toBinaryString(c.toInt())
binaryReps[c] = binary
}
for ((letter, binary) in binaryReps) {
println("$letter = $binary")
}
2.4.4 in으로 컬렉션이나 범위의 원소 검사
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
fun isNotDigit(c: Char) = c !in '0'..'9'
2.5 코틀린의 예외 처리
- 자바와 비슷
자바와 마찬가지로 try 사용하기
fun readNumber(reader: BufferedReader): Int? { //함수가 던질 수 있는 예외를 명시할 필요가 없다.
try {
val line = reader.readLine()
return Integer.parseInt(line)
}
catch (e: NumberFormatException) {
return null
}
finally {
reader.close()
}
}
- 코틀린은 체크 예외와 언체크 예외를 구별하지 않는다.
2.5.2 try를 식으로 사용
fun readNumber(reader: BufferedReader) {
val number = try {
Integer.parseInt(reader.readLine()) //이 식의 값이 try 식의 값이 된다.
} catch (e: NumberFormatException) {
null
}
}