Kotlin in Action - 2장 코틀린 기초

kotlin in action - 2장. 코틀린 기초를 다시 보며 정리하는 내용입니다.

1. 함수

블록({ })이 본문인 함수

  • 본문이 중괄호 ({ })로 둘러싸인 함수를 말함
  • 함수 선언은 fun 키워드를 사용
  • a:Int -> 파라미터 이름 뒤에 파라미터 타입을 선언
  • Int : Return Type
  • 세미콜론 ( ; )은 사용하지 않아도 됨
  • 아래와 같이 블록( { } )이 본문인 함수는 return 을 명시해줘야함
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
  • 컴파일러가 타입을 분석해 타입을 정해주는 타입추론이 가능하기 때문에 식이 본문인 함수에서는 Return Type은 생략 가능하다.

    fun max(a:Int, b:Int) = if(a>b) a else b
    

2. 변수

코틀린에서는 타입 지정을 생략하는 경우가 흔하다(타입추론 덕분)

val answer = 42      // 타입 지정 생략. 단, 초기화 식이 있어야지만 생략이 가능하다
val answer: Int = 42 // 타입 지정

변경 가능한 변수와 변경 불가능한 변수

  • val(value) : 변경 불가능(immutable). 자바의 final 에 해당됨

    • 블록을 실행할 때 정확히 한 번만 초기화되면 된다

      val message: String
      if (messageSend()) {
        message = "success"
      } else {
        message = "Fail"
      }
      
    • val 참조는 불변이지만, 참조가 가리키는 객체 내부의 값은 변경될 수 있다

      val languages = arrayListOf("Java") // 불변
      languages.add("Kotlin") // 객체 내부의 값은 변경 가능
      
  • var(variable) : 변경 가능(mutable). 자바의 일반 변수

  • 기본적으로 모든 변수를 val 를 사용해서 불변 변수로 선언하고, 필요한 부분만 var로 변경하자

변수사용(문자열 템플릿)

  • Hello, World! 출력하기

    fun main(args: Array<String>) {
    	val name = "World"  
    	println("Hello, $name!") // $name : name의 내용이 출력됨
    }
    
  • 문자열 이스케이프

    println("\$x") // $x 가 출력됨
    
  • 복잡한 식은 중괄호( { } )로 둘러싸서 사용할 수 있다

    println("Hello, ${ if (args.size > 0) args[0] else "someone" }!") // 큰 따옴표도 사용가능, 배열의 값도 사용가능, if문 사용가능
    

3. 클래스와 프로퍼티

  • Person class의 Java와 Kotlin 비교

    • Java

      public class Person {
            
        private final String name;
            
        pubilc Person(String name) {
          this.name = name;
        }
            
        public String getName() {
          return name;
        }
            
      }
      
    • Kotlin

      class Person(val name: String)
      
      • 위와 같이 코드가 없이 데이터만 저장하는 클래스를 Value Object(값 객체)라고 함
      • kotlin의 기본 가시성은 public 이므로 생략

프로퍼티

  • val 로 선언한 프로퍼티 : 읽기 전용(getter)

  • var 로 선언한 프로퍼티 : 읽기/쓰기 가능

    class Person (
    	val name: String,  // private field, 읽기 전용이므로 kotlin은 public getter를 생성해준다
      var isMarried: Boolean // private field, 읽기/쓰기 가능하므로 kotlin은 public setter, public getter를 생성해준다
    )
    
  • 사용하는 방법

    val person = Person("Bob", true) // new 사용하지 않고, 생성자를 호출함
    println(person.name) // Bob 출력. 프로퍼티 이름을 직접 사용하면 자동으로 getter를 호출해준다
    println(person.isMarried) // true 출력
    person.isMarrie = false // setter를 호출해서 값을 변경할 수 있음
    

커스텀 접근자(Getter)

  • 자신이 정사각형인지 아닌지를 알려주는 class

    class Rectangle(val height:Int, val width: Int) {
      val isSquare: Boolean
      	get() {  // 커스텀 접근자 
          return height == width
        }
      	// 블록이 아닌 식으로도 구현 가능
      	// get() = height == width
    }
    

코틀린의 디렉터리와 패키지

  • kotlin에서는 함수도 클래스처럼 임포트해서 사용할 수 있다

    package com.eomdev.kotlin.basic // package 선언
      
    class Rentangle(val height: Int, val width: Int) {
      val isSquare: Boolean
      	get() = height == width
    }
      
    fun createRandomRectangle(): Rentangle {
      val random =  Random()
      return Rentangle(random.nextInt(), random.nextInt())
    }
    
    import com.eomdev.kotlin.basic.createRandomRectangle // createRandomRectangle 함수 임포트
      
    fun main(args: Array<String>) {
      println(createRandomRectangle().isSquare) // import한 함수 사용
    }
    
  • kotlin에서는 한 파일에 여러 클래스를 넣을 수 있다. 파일 이름도 마음대로 정의 가능.

4. enum과 when

enum class

  • 간단한 enum class

    enum class Color { // enum class 키워드를 사용한다
      BLACK, WHITE
    }
    

class 는 키워드이므로, 클래스를 표현하는 변수 등을 정의할 때는 clazz나 aClass 와 같은 이름을 사용한다

  • 프로퍼티와 메소드가 있는 enum 클래스

    enum class Color(
      // 상수 프로퍼티 정의
      val r: Int,
      val g: Int,
      val b: Int
    ) {
      
      RED(255, 0,0), // 각 상수를 생성할 때 그에 대한 프로퍼티 값
      YELLOW(255, 255, 0),
      GREEN(0, 255, 0);  // 주의!! enum 상수 목록과 메소드 정의 사이에 반드시 세미콜론을 사용해야함!!
        
      fun rgb() = (r * 256 + g) * 256 + b // 메소드 정의
    }
    

when

  • java의 switch를 대신함
  • if와 마찬가지로 값을 만들어 내는
fun findName(color: Color) = // 식이므로 = 연산자로 사용 가능하다
	when (color) {
    Color.RED -> "Richard"
    Color.YELLOW -> "York"
    Color.GREEN -> "Gave"
  }


println(findName(Color.YELLOW))
  • 한 when 분기 안에서 여러 값을 사용하는 방법

    fun getWarmth(color: Color) = 
    	when (color) {
        Color.RED, Color.YELLOW -> "warm" // , 로 구분해서 여러 값을 사용할 수 있다
        Color.GREEN -> "natural"
      }
    
  • 임의의 객체와 함께 사용하기

    • 예) 두 색을 혼합했을 때, 정해진 색을 알려주는 함수

      fun mix(c1: Color, c2: Color) =
      	when(setOf(c1, c2)) {						// when 식의 인자로 아무 객체나 사용 가능. 여기서는 Set객체로 만드는 setOf 함수를 사용함
          setOf(RED, YELLOW) -> ORANGE
          setOf(YELLOW, BLUE) -> GREEN
              
          else -> throw Exception("Dirty Color") // 매치되는 조건이 없는 경우
              
        }
      
  • when에 인자를 사용하지 않는 방법

    • 코드는 읽기 어려워지지만, 성능을 향상 시키기 위한 방법
    fun mix(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") // 매치되는 조건이 없는 경우
          
      }
    
  • when 에 블록문을 사용할 수 도 있다

    fun mix(c1: Color, c2: Color) =
    	when(setOf(c1, c2)) {		
        setOf(RED, YELLOW) -> {
          ...
          ORANGE // 블록의 마지막 식이 반환된다
        } 
        setOf(YELLOW, BLUE) -> {
         ....
          GREEN
        }
          
        else -> throw Exception("Dirty Color")
          
      }
    

스마트 캐스트(타입검사 + 타입캐스트)

  • 자바의 경우

    if (c instanceof MessageDummyService) {
      MessageDummyService messageService = (MessageDummyService)c;
      messageService.send();
    }
    
  • kotlin의 경우

    if (c is MessageDummyService) { // kotlin은 is가 자바의 instanceof와 비슷하다
        messageService.send(); // java에서는 타입변환을 해줘야하지만, kotlin에서는 컴파일러가 자동으로 캐스팅을 해준다. 이것을 스마트 캐스트라고 한다.
    }
    
  • 스마트 캐스트 조건

    • is 로 타입을 검사한 다음에 값이 바뀔 수 없는 val 이어야함
    • 커스텀 접근자를 사용하지 않아야 함
  • 명시적으로 타입 캐스팅을 위해서는 as 키워드를 사용해야한다

    val n = e as Num
    

수에 대한 이터레이션

  • range

    val oneToTen = 1..10 // .. 연산자를 사용. 1 ~ 10 까지
    
    • 피즈버즈 게임(3,5로 나누어 떨어지면 피즈버즈, 3으로 나누어떨어지면 피즈, 5로 나누어 떨어지면 버즈)

      fun fizzBuzz(i: Int) =  when {
        i % 15 == 0 -> "FizzBuzz"
        i % 3 == 0 -> "Fizz"
        i % 5 == 0 -> "Buzz"
        else - "$i "
      }
          
      // for..in 루프문 사용
      for( i in 1..100) {
        println(fizzBuzz(i))
      }
          
      
  • 증가 값 있는 for

    • downTo : 감소
    for ( i in 100 downTo 1 step 2) { // 100부터 1까지 감소하는데, 2씩 감소시킨다
      println(fizzBuzz(i))
    }
    
  • map 이터레이션

    val list = arrayListOf("10", "11", "1001")
    for ((index, element) in list.withIndex()) {
      println("$index: $element")
    }
    
  • in을 사용해서 컬렉션이나 범위의 원소 검사하기

    fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z' // in
    fun isNotDigit(c: Char) = c !in '0'..'9' // !in 사용해서 범위에 속하지 않은지 검사
    "Kotlin" in setOf("Java", "Scala") // Set에 Kotlin은 없다. false
    

출처 : Kotlin in Action http://acornpub.co.kr/book/kotlin-in-action