ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 2장 - 코틀린 기초
    Kotlin/코틀린인액션요약 2022. 9. 10. 00:01
    728x90

    2장에서 다루는 내용

    - 함수, 변수 , 클래스 enum 프로퍼티를 선언하는 방법

    - 코틀린의 여러  제어구조

    - 스마트 캐스트

    - 예외 던지기와 예외 잡기

     

    단, 여기에서 배운 내용으로 작성하는 코드는 코틀린다운 코드라고 부르기는 어려움

     

    함수와 변수

    fun main(args: Array<String>){
    	println("Hello,world!")
    }

    함수를 선언할 때 fun 키워드를 사용합니다.

     

    파라미터 이름 뒤에 그 파라미터의 타입을 씁니다.

     

    System.out.println 대신에 println을 사용합니다.(자바 라이브러리 함수를 간결하게 사용할 수 있는 래퍼를 제공합니다)

     

    끝에 세미콜론(;)을 사용하지 않아도 됩니다.

     

    함수

    fun max(a: Int, b:Int) : Int{
    	return if(a>b) a else b
    }
    println(max(1,2))
    //출력 2

    fun으로 함수를 선언하고 함수 이름은 max, 파라미터 목록은 a, b 반환 타입은 Int입니다.

     

    위처럼 본문이 중괄호로 둘러쌓인 함수를 블록이 본문인 함수라고 합니다.

     

    반대로 다음과 등호와 식으로 이뤄진 함수를 식이 본문인 함수라고 부릅니다.

    fun max(a: Int, b: Int) Int = if(a>b) a else b //식이 본문인 함수

     

    코틀린에서는 식이 본문인 함수가 더 자주 쓰이며 여기에 if, when, try 등의 더 복잡한 식도 자주 쓰입니다.

    또한 여기서 반환타입인 Int를 생략해도 컴파일러가 함수 본문 식을 분석하여 결과 타입을 추론합니다. (타입 추론)

     

    변수

    val question = "삶, 우주, 그리고 모든 것에 대한 궁극적인 질문"
    val answer = 42
    val answer : Int = 42

    타입 추론을 사용해도 되고 타입을 명시해도 됩니다.

    부동소수점을 사용하면 변수 타입은 Double이 됩니다.

     

    val immutable = 42;
    var mutable = 42;

    val은 value의 약자로 변경 불가능한 참조를 저장하는 변수입니다. (자바의 final)

    var은 variable의 약자로 변경 가능한 참조를 저장하는 변수입니다.

     

    var키워드를 사용하면 변수의 값은 변경할 수 있지만 변수의 타입은 바꿀 수 없습니다.

    만약 다른 타입의 값을 저장하고 싶다면 변환 함수를 사용하여 타입을 변환시키거나 강제 형 변환을 해야 합니다.

     

    문자열 템플릿

    fun main(args: Array<String>){
    	val name = if(args.size >0) args[0] else "Kotlin"
    	println("Hello, $name!")
    }

    변수 앞에 $를 사용하여 변수를 문자열 안에서 사용할 수 있습니다.

    변수 이름뿐만 아니라 복잡한 식도 {}로 감싸서 넣을 수 있습니다.

    변수 이름을 사용할 때도 {}로 감싸는 습관을 들이면 더 좋습니다.

     

    클래스와 프로퍼티

    간단한 자바 클래스

    public clas Person{
    	private final String name;
        
    	public Person(String name){
    		this.name = name;
    	}
        
    	public String getName(){
    		return name;    
    	}
    }

    이를 코틀린으로 변환하면 다음과 같습니다. (코틀린 파일에 자바 코드를 복사하면 변환할 거냐는 메시지가 나옴)

    class Person(val name: String)

     

    프로퍼티

    자바에서는 필드와 접근자를 한데 묶어 프로퍼티라고 부릅니다.

    코틀린은 프로퍼티를 언어 기본 기능으로 제공합니다.

    앞서 변수에서 보았듯이 val은 읽기 전용이며 var는 읽기/쓰기가 가능합니다.

     

    코틀린은 val을 사용하면 비공개(private) 필드를 만들며 게터를 만들어냅니다.

    코틀린은 var를 사용하면 비공개(private) 필드를 만들며 게터, 세터를 만들어냅니다.

     

    코틀린 클래스 사용하기

    val person = Person("Bob", true)
    println(person.name)
    //Bob
    println(person.isMarried)
    //true

    프로퍼티 이름으로 사용해도 코틀린이 자동으로 게터를 호출해줍니다.

    마찬가지로 person.isMarreid = false는 세터처럼 동작합니다.

     

    프로퍼티 접근자 커스텀하기

    직사각형 클래스인 Rectangle을 정의하며 자신이 정사각형인지 알려주는 기능을 만들어보겠습니다.

    class Rectangle(val height: Int, val width: Int){
    	val isSqure: Boolean
    	get(){
    		return height== width
    	}
    }
    
    val rectangle = Rectangle(41,43)
    println(rectangle.isSqure)
    //false

     

    디렉터리와 패키지

    패키지를 import 하면 패키지 안에 있는 모든 선언을 사용할 수 있습니다.

    또한 이름으로 함수를 임포트 하여 사용할 수 있습니다.

     

     

    enum과 when

    when은 자바의 switch를 대치하면 훨씬 더 강력합니다.

    앞으로 더 자주 사용할 프로그래밍 요소입니다.

     

    enum은 자바보다 코틀린에서 더 많은 키워드를 사용해야 하는 흔치 않은 예입니다.

    enum class Color{
    	RED, ORANGE, YELLOW, GREEN
    }

     

    자바와 마찬가지로 enum 클래스에 프로퍼티나 메서드를 정의할 수 있습니다.

    enum class Color(
    	var r: Int, var g: Int, var b: Int
    ){
    	RED(255,0,0), GREEN(0,255,0), BLUE(0,0,255);
        
        fun rgb() = (r * 256 + g) * 256 + b
    }
    
    println(Color.BLUE.rgb())
    //255 출력

    코틀린에서 유일하게 세미콜론이 필수인 부분입니다.

     

    when을 활용한 enum

    fun getMnemonic(color: Color) =
    	when (color){
    		Color.RED -> "Richard"
    		COLOR.GREEN -> "Gave"                    
    		Color.BLUE -> "Battle"        
        }

    각 분기 끝에 break를 넣지 않아도 됩니다.

     

    또한 , 을 활용하여 한 분기에 여러 값을 사용할 수 있습니다.

    Color.RED, Color.ORANGE, Color.YELLOW -> "warm"

     

    enum 상수 값을 임포트 하여 enum 클래스 수식어 없이 enum을 사용할 수 있습니다.

    import ch02.colors.Color.*
    fun getWarmth(color: Color) = when(color){
    	RED, ORANGE, YELLO -> "warm"
    }

     

    인자 없는 when

    매번 인자를 사용하여 분기 조건에 있는 것을 비교해야 합니다.

    인자가 없는 when을 사용하면 불필요한 객체 생성을 막을 수 있지만 코드 읽기가 약간 어려워집니다.

    fun mixOptimized(c1: Color, c2: Color) =
    when{
    	(c1 == YELLO && c2 == BLUE) || (c1 == BLUE && c2 == YELLO) -> GREEN
    }
    
    println(mixOptimized(BLUE,YELLO))
    //출력 : GREEN

     

    스마트 캐스트

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

    Expr은 인터페이스입니다.

    Num 클래스는 value라는 프로퍼티만 존재하는 단순한 클래스로 Expr 인터페이스를 구현합니다.

    Sum 클래스는 Expr 타입의 객체라면 어떤 것이나 Sum 연산의 인자가 될 수 있습니다.

    따라서 Num, Sum 클래스가 인자로 올 수 있습니다.

     

     

    인터페이스를 이용한 연산을 구현한 코드입니다.

    interface Expr
    class Num(val value: Int) : Expr
    class Sum(val left: Expr, val right: Expr) : Expr
    
    fun main() {
        println(eval(Sum(Num(1), Num(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로 변수 타입을 검사합니다.

    이때 e as Num으로 캐스팅하는 게 불필요합니다.

    코틀린은 is로 검사하고 나면 캐스팅하지 않아도 처음부터 그 변수가 원하는 타입으로 선언된 것처럼 사용할 수 있습니다.

    이를 스마트 캐스트라고 부릅니다.

     

    리팩터링 : if를 when으로 변경

    코틀린에서 if는 값을 만들어 내기 때문에 자바와 달리 3항 연산자가 따로 없습니다.

    이런 특성을 이용하면 if 식을 본문으로 사용하여 return 문과 중괄호를 없앨 수 있습니다.

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

     

    이 코드를 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")
    }

     

    while과 for 사용

    while, do-while이 존재하며 자바와 크게 다르지 않습니다.

     

    코틀린에서 자바의 for 루프에 해당하는 요소가 없습니다.

     

    .. 연산자로 시작 값과 끝 값을 연결해서 범위를 만들어냅니다.

    val oneToTen = 1.. 10

     

    코틀린의 범위는 폐구간 즉, 양끝을 포함하는 구간입니다. (즉, 1.. 10은 10을 포함합니다)

     

    피즈 버즈 게임을 구현해보겠습니다.

    피즈버즈 게임은 3으로 나누어 떨어지면 피즈, 5로 나누어 떨어지면 버즈 , 3과 5로 모두 나누어 떨어지면 피즈 버즈를 외치는 게임입니다.

     

    fun fizzBuzz(i: Int) = when{
        i % 15 == 0 -> "FizzBuzz "
        i % 3 == 0 -> "Fizz "
        i % 5 == 0 -> "Buzz "
        else -> "$i" //다른 경우 자기자신 반환
    }
    
    fun main() {
        for(i in 1..100){
            print(fizzBuzz(i))
        }
    }

     

    이번에는 100부터 거꾸로 세되 짝수만으로 게임을 진행하는 코드입니다.

    fun main() {
        for(i in 100 downTo 1 step 2){
            print(fizzBuzz(i))
        }
    }

     

    만약 1.. size을 활용하여 1~size-1까지 표현하고 싶다면 for(x in 0 until size)를 활용할 수 있습니다.

     

    Map에 대한 이터레이션

    import java.util.*
    
    fun main() {
        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")
        }
    
    }

     

    in 키워드를 통해 컬렉션이나 범위의 원소 검사

    fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
    fun isNotDigit(c: Char) = c !in '0'..'9'

     

    when과 in 같이 활용하기

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

     

    코틀린의 예외 처리

    코틀린의 예외처리는 자바나 다른 언어의 예외 처리와 비슷합니다.

    다른 점은 예외 인스턴스를 만들 때도 new를 붙일 필요가 없습니다.

     

    try, catch, finally

    import java.io.BufferedReader
    
    fun main() {
        fun readNumber(reader: BufferedReader) : Int? { //던질 수 있는 예외를 명시할 필요가 없습니다
            try{
                val line = reader.readLine()
                return Integer.parseInt(line)
            } catch(e: NumberFormatException){ //예외 타입을 :의 오른쪽에 씁니다
                return null
            }
            finally {
                reader.close()
            }
        }
    }

     

    try 식으로 사용하기

    fun readNumber(reader : BufferedReader) {
        val number = try{
            Integer.parseInt(reader.readLine())
        } catch (e: NumberFormatException){
            null
        }
        println(number)
    }
    
    fun main() {
        val reader=  BufferedReader(StringReader("not a number"))
        readNumber(reader)
    }

     

    'Kotlin > 코틀린인액션요약' 카테고리의 다른 글

    6장 - 코틀린 타입 시스템  (0) 2022.09.16
    5장 - 람다로 프로그래밍  (0) 2022.09.13
    4장 - 클래스, 객체, 인터페이스  (1) 2022.09.12
    3장 - 함수 정의와 호출  (1) 2022.09.11
    1장 - 코틀린소개  (0) 2022.09.08

    댓글

Designed by Tistory.