Kotlin in Action: 2. Kotlin basics
Basic elements: functions and variables
Kotlin이 아직 신생언어이고 자바와 호환성이 있다고 하지만 어느 부분에서 비슷한 것인지 헷갈리기 때문에 이 부분은 Java와 비교해가면서 공부하면 재미있을 것 같다!
Kotlin과 java에서 메인함수에서 Hello, world!를 출력하는 코드는 다음과 같다.
//Kotlin
fun main(args: Array<String>){
println("Hello, world!")
}
//Java
public class HelloWorld{
public static void main(String[] args){
System.out.println("Hello,world!");
}
}
함수 선언 방법, Kotlin에서는 function의 약어인 fun으로 함수를 선언해주면 된다.
Java가 C나 python같은 언어와 달랐던 점이 class와 함수 선언이 너무 길다는 점이었는데 이 부분에서 확실히 간편해졌다는 생각이 들었다.
선행하는 class가 필요하지 않다. 보면 알다시피 Java에서는 class안에 함수를 넣어줘야한다. Kotlin에서 함수는 따로 바깥에 class로 감싸주지 않고, 파일의 가장 윗단에 선언될 수 있다.
코틀린의 기본 가시성이 public이므로 public 가시성 변경자를 굳이 사용해 줄 필요가 없다
(모르겠는 부분) 배열이 class여서 Java와 달리 Kotlin은 배열을 따로 선언해주는 특별한 문법이 없다
이 외에도 출력 라이브러리 사용하는 방법이나, 세미콜론이 없다는 점 등 Java와의 다른 점을 찾아볼 수 있었다.
Functions(함수 만들기)
Kotlin에서 함수는 선언 방식이 두가지가 있는데, 중괄호와 리턴문이 함께있는 함수와, 등호를 사용해 중괄호와 리턴 값을 없앤 함수가 있다! 전자의 함수의 이름은 건너뛰고.. 후자의 함수는 expression bodies라고 불린다.
전자의 경우는..
fun max(a: Int, b: Int): Int {
return if (a > b) a else b
}
println(max(1, 2))
/*fun 함수이름(인자1, 인자2) 리턴 타입{
..함수 내용
}*/
이렇게 생겼다!
후자의 경우는
fun max(a: Int, b: Int): Int = if(a>b) a else b
이렇게 생겼다. (IntelliJ Idea에는 이 두 함수를 서로 바꾸는 기능을 가지고 있다고 한다)
Expression Body 단순화 하기
이미 단순하게 생긴 함수를 더 단순하게 만드는 방법이 있는데 리턴값의 타입을 명시해주지 않으면서 더 짧게 만들 수 있다.
다른 언어의 경우 리턴값을 항상 명시해줘야하는데, Kotlin엔 타입추론(Type Inferences) 기능이 있기 때문에 리턴값을 명시해주지 않아도 컴파일러가 문맥을 파악해서 리턴되는 값의 타입을 파악한다.
Variables(변수)
위에도 얘기했고 첫장 리뷰를 쓰면서도 얘기했듯이 Java와 달리 Kotlin은 변수 선언을 할 때 타입 선언을 따로 안해줘도 된다. (컴파일러가 알아서 문맥을 파악한다는 타입 추론 개념때문에)
단, 초기화를 하지 않고 선언을 먼저하는 변수라면 변수 타입을 항상 명시해주어야한다.
val answer: Int
answer = 42
(어려운 부분 또 등장)Mutable and Immutable Variables
변수를 선언할 때 사용하는 두가지의 키워드가 있다.
하나는 앞에서 설명한 val(어원: value)이고, 다른 하나는 var(어원: variable)이다.
val변수는 변경 불가능한 변수로, 선언이 완료되면 다시 지정될 수 없다. Java에서 final 변수와 상응하는 개념이다.
var변수는 변경 가능한 변수로, 변수의 값이 바뀔 수 있다. Java의 일반 변수에 해당한다.
아무리 val변수의 참조가 그 자체로 변할 수 없다 하더라도, 그것을 가리키는 객체는 바뀔 수 있다.
(6장에서 자세히 다룬다고 한다)
그리고 var은 변수의 값이 바뀔 수 있지만, 변수의 타입은 바뀔 수 없고, 만일 바뀐 타입으로 재정의 되었을 경우 컴파일 오류가 발생한다.
문자열 지정: 문자열 템플릿
문자열 리터럴이 필요한 곳에 변수를 넣되 변수 앞에 $를 추가해야한다.
만일 $문자를 문자열에 넣고 싶으면 아래와 같이 하면 된다.
fun main(args: Array<String>){
val name = if(args.size>0) args[0] else "Kotlin"
println("Hello, $name)
}
//만약 $을 문자열에 넣고 싶으면
//println("\$x")과 같이 \을 왈용해 $를 이스케이프 시켜야 함
Classes and properties
클래스의 가시성 변경자에 대해서 나오는데 앞에서 설명했다.
Java와 다른 예시 코드를 보면
class Person(val name: String)
//Java는?
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
(class에서도 public이면 굳이 다시 지정해주지 않아도 된다는 의미)
Java에서는 클래스 외부에서 필드에 접근할 경우 반드시 메소드를 통해 접근해야한다. 프로퍼티는 메소드를 통해 관리되는 데이터이다. Kotlin은 Property를 언어 기본으로 제공해서 Java에서 getter와 setter 메소드를 대신한다.
https://thositeom.tistory.com/49
선택 표현과 처리: enum과 when
when은 java의 switch를 대신하는 프로그래밍 요소이다.
enum에 대해서 잘 모르겠어서 코틀린문서를 가져왔다.
https://kotlinlang.org/docs/enum-classes.html
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), GREEN(0, 255, 0), BLUE(0, 0, 255),
INDIGO(75, 0, 130), VIOLET(238, 130, 238);
fun rgb() = (r * 256 + g) * 256 + b
}
enum 클래스 안에 메소드를 정의하는 경우 enum 상수 목록과 메소드 정의 사이에 세미콜론을 넣어야 한다.
When
Kotlin의 when은 Java의 Switch보다 강력하다.
분기 조건에 상수만을 사용할 수 있는 자바 switch와 달리 코틀린 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(BLUE, YELLOW))
위의 코드에서는 집합 비교를 사용하여 c1과c2를 함께 setOf 함수를 사용해 비교했다.
자바의 switch와 달리 when 절에서는 각 분기의 끝에 break를 넣지 않아도 된다.
인자없는 when 사용
이전의 함수는 비교대상을 Set인스턴스를 생성한다. 보통은 이런 비효율성이 크게 문제가 되지 않는다. 하지만 이 함수가 자주 호출된다면 쓰지 않는 객체가 늘어나기 때문에, 함수를 고쳐써야한다.
fun mixOptimized(c1: Color, c2: Color) =
when {
(c1 == RED && c2 == YELLOW) ||
(c1 == YELLOW && c2 == RED) ->
ORANGE
(c1 == YELLOW && c2 == BLUE) ||
(c1 == BLUE && c2 == YELLOW) ->
GREEN
(c1 == BLUE && c2 == VIOLET) ||
(c1 == VIOLET && c2 == BLUE) ->
INDIGO
else -> throw Exception("Dirty color")
}
스마트캐스트: 타입 검사와 타입 캐스트를 조합
코틀린에서는 is를 사용해 변수 타입을 검사한다. Java의 instanceof는 그 타입에 속한 멤버에 접근하기 위해서 명시적으로 변수 타입을 캐스팅을 해야하는 반면 Kotlin에서는 컴파일러가 캐스팅을 해준다.(일명 스마트캐스트)
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
return n.value
}
if (e is Sum) {
return eval(e.right) + eval(e.left)
}
throw IllegalArgumentException("Unknown expression")
}
스마트 캐스트는 is로 변수에 든 값의 타입을 검사한 다음에 그 값이 바뀔 수 없는 경우에만 작동한다.
원하는 타입으로 명시적으로 타입 캐스팅하려면 as 키워드를 사용한다.
While문, for 문 itertation
수에 대한 이터레이션: 범위와 수열
fun main(args: Array<String>) {
for (i in 100 downTo 1 step 2) {
print(fizzBuzz(i))
}
}
fun recognize(c: Char) = when (c) {
in '0'..'9' -> "It's a digit!"
in 'a'..'z', in 'A'..'Z' -> "It's a letter!"
else -> "I don't know…"
}
Try, Catch, Finally
// 조건이 참이면 number의 값이 초기화되고 거짓이면 초기화되지 않고 throw를 호출한다.
val number = try {
Integer.parseInt(reader.readLine())
} catch (e: NumberFormatException) {
return // 예외가 발생한 경우 catch 블록 다음의 코드는 실행되지 않는다.
}