오늘도 더 나은 코드를 작성하였습니까?

코틀린 기초 본문

Kotlin in Action/코틀린 기초

코틀린 기초

hik14 2020. 8. 6. 21:49

함수

- 기본구조

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

1.  fun 함수 선언 키워드

2.  파라미터 a: Int --> name: type

3.  fun(... ):  반환 타입 {... }

 

- 식이 본문인 함수

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

* statement(문) vs expression(식)

expression 은 값을 만들어 내며 다른 식의 하위 요소로 계산될 수 있지만

statement 은 자신을 포함한 블록의 최상위 요소로 존재하며 아무런 값을 생성 하지 못한다.

 

java에서는 모든 제어구조가 statement인 반면 코틀린에서는 loop를 제외하면 대부분의 제어구조가 expression로 되어있다.(코틀린에서는 if, when try 등 전부 expression이다.)

 

함수의 본문이 식으로 표현될 경우 간결하게 표현을 할 수 있다. 식에는 타입이 정해져 있기 때문에 반환 타입을 생략이 가능하다. 블록이 본문인 함수는 불가능하다. 블록이 본문인 함수라면 return은 필수적이다.

 

변수

- 기본

val answer: Int = 42

val answer = 42

val answer: Int
answer = 42

1. 키워드(val var) 변수명: 타입 

2. 할당되는 값에 따라 컴파일러가 타입을 추론 하기 때문에 2번째 줄처럼 타입을 생략할 수 있다.

3. 선언 후 할당을 경우 생략할 수 없다. (컴파일러가 할당되는 값을 특정할 수 없기 때문에 타입 추론이 불가능하다.)

 

- val  var

 

val (Value) : 변경 불가능한(immutable) 참조를 저장하는 변수다. 한번 초기화되고 나면 재할당은 불가능하다.

자바의 final과 매우 유사하다.

 

var (Variable) :  변경 가능한(mutable) 참조다. 재할당이 가능하고 자바의 일반적 변수와 동일하다.

 

 * 코틀린 코드를 작성할 때 기본적으로 모든 변수를 val 키워드를 사용해서 선언하고 나중에 변경될 필요가 있을 경우 var로 고쳐서 사용한다. 변경 불가능한 참조와 변경 불가능한 객체를 부수효과가 없는 함수와 조합하여 사용하면 코드가 함수형 코드에 가까워진다.

문자열 템플릿

fun printStr(str: String){
    println( "$str")
    println("Hello ${if (str.isNotEmpty()) str else "이름 없음"}!")
}

- 문자열 리터럴 "... " 안에 변수를 사용할 수 있게 한다. 

- 단일 변수에는 $ 복잡 한식은 ${... } 형태로 사용한다

- 가능하면 단일 변수라도 ${ } 형태로 사용한다.

 

클래스와 프로퍼티

Java

public class Person {

    private final String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }   
}

Kotlin

class Person(val name: String) 

 

 

- 간단한 자바 클래스를 코틀린 클래스로 표현한 것이다.

-  코틀린 class 가시성 변경자는 default로 Public이기 때문에 생략되었다.

-  생성자는 (val name: String)으로 표현되었다.

-  name 멤버 변수+ getter는 val로 표현되었다.

 

프로퍼티란?

 

클래스란 개념의 목적은 데이터를 캡슐화해서 데이터를  다루는 한 주체 아래 가두는 것이다.

일반적으로 필드에 데이터를 저장 후 getter 또는 setter 통해 데이터를 접근 및 공개한다.

프로퍼티란 필드 + 접근자 메서드(getter, setter) 묶어 부르는 명칭이다.

 

val - 읽기 전용 프로퍼티로 비공개 필드와 getter를 제공한다.

var - 읽고 쓸 수 있는 프로퍼티로 비공개 필드와 getter / setter를 제공한다. 

 

 

커스텀 접근자

class Rectangle(val height: Int, val width: Int){
    val isSquared: Boolean
    get() = height== width
}

높이와 너비를 가지고 정사각형인지 아닌지 판별의 결과를 가지는 isSquared 프로퍼티는

get() {.. }를 이용해 코틀린에서 기본적으로 생성해주는 getter가 아닌 커스텀 되었다. 

 

enum( 열거형 클래스)

- 코드가 단순해지며 가독성이 올라간다.

- 인스턴스 생성 및 상속을 방지하며 상수의 안정성을 보장한다.

- enum 클래스 자체는 인터페이스를 구현할 수 있다.

// 상수 프로퍼티 r g b 선언
enum class Color(val r: Int, val g: Int, val b:Int ){

    RED(255, 0,0), //  각 상수를 생성할때 r g b 프로퍼티의 값을 지정한다.
    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 상수는 자신의 anonymous class를 선언할 수 있고 base method를 override 할 수 있다.

enum class Animal(val nickName: String, val age: Int){

    DOG("바둑이", 4){
        override fun say() = println("멍멍")
    },

    CAT("나비",6){
        override fun say() = println("야옹")
    };

    abstract fun say();
    fun printInfo() = println("${nickName} ${age}살")
}

fun main() {
    Animal.CAT.printInfo()
    Animal.CAT.say()

    Animal.DOG.printInfo()
    Animal.DOG.say()
}

When

기본 사용법

 

- when은 java의 switch문과 비슷하지만 좀 더 강력하고 expression이다. 

- 인자로 객체도 가능하며 기타 원시 타입 역시 가능하다.

 

when( 인자 ){

      매칭 값 -> Todo

       else  ->  // 아무것도 매칭 되지 않을 때 실행됨

}

 fun getMnemoic(color: Color) = 
     when(color){
         Color.RED -> "빨강"
         Color.ORANGE -> "오랜지"
         Color.YELLOW -> "노랑"
         Color.GREEN -> "녹색"
         Color.BLUE -> "파랑"
         Color.INDIGO -> "인디고"
         Color.VIOLET -> "바이올렛"
         else -> "아무것도 매칭되지 않을떄"
     }
 

- 동일한 로직을 실행할 매칭 값을 여러 개 지정할 수 있다. 

 

when( 인자 ){

      매칭 값 1, 매칭 값 2 -> Todo

       else  ->  // 아무것도 매칭 되지 않을 때 실행됨

}

 

 

- 인자가 없이 사용도 가능하다

 

when {

       (분기 조건은 반드시 Boolean을 반환하는 식) -> Todo

       else  ->  // 아무것도 매칭 되지 않을 때 실행됨

}

 

Smart Cast

IS

 

- 자바의 instanceof와 유사하지만 자바는 검사만 할 뿐이지만 코틀린의 is는 검사 후 바로 변형해준다.

- 단 검사한 다음에 그 값이 바뀔 수 없는 경우에만 작동한다.

 

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


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

fun main() {
    println(eval(Sum(Sum(Num(1),Num(2)), Num(4))))
}

클래스의 프로퍼티에 대해 스마트 캐스팅을 사용한다면 그 프로퍼티는 반드시 val이고 커스텀 접근자를 선언한 것이면 안된다. 아래 코드를 보면 에러가 나는데 eval 클래스의 exp 프로퍼티가 var이라 변경 가능이기 때문이다.

class eval(var exp: Expr){
    val num: Int
        get()=
            if (exp is Num){ exp.value }else 0;
}

AS는 강제 타입 변경에 사용한다.

 

While For 루프 

while / do - while 문은 자바와 동일하기 때문에 생략한다.

 

for 

 

기본 형태

 

for 아이템 in 범위

범위는 폐구간이 양쪽 끝을 포함한다 val oneToTen = 1.. 10으로 표현한다.

 

fun main() {
    for (i in 1..10)
        println(i)
}

 

반열린 구간은 until 함수를 이용한다. 마지막을 포함하지 않음

fun main() {
    for (i in 1 until 10)
        println(i)
}

downTo 및 step을 이용하여 역방향 수열 및 증가 값을 조절할 수 있다.

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

 

- Map을 포함한 기타 Collection을 이터레이션 할수 있다.

 

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: "+binary)
    }
}
fun main() {

    val list= arrayListOf("10", "11", "12")

    for( (index, element) in list.withIndex() ){
        println("${index} : ${element}")
    }
}

 

- in은 컬렉션 또는 범위 원소에 속하는지 확인할 수 있다.

 

comparable 인터페이스를 구현한 객체라면 객체를 사용해 범위를 만들어 in 연산자를 사용할 수 있다.

컬렉션 내부에 있는지 도 확인 가능하다.

fun recognize(c: Char) = when(c){
    in '0'..'9' -> "한 자리 숫자다"
    in 'a'..'z', in 'A'..'Z' -> "알파벳이다"

    else -> "모르겠다"
}
fun main() {
    println(recognize('a'))

    println("Kotlin" in "Java".."Scala")
    println("Kotlin" in setOf("java", "Scala"))
}

 

예외처리

Checked Exception

 - 컴파일 시 발견될 수 있는 예외 일반적으로 자바는 이런 에러처리를 강제한다.

 

UnChecked Exception

- 런타임 시 발견될 수 있는 예외 

 

코틀린의 예외 처리는 자바와 비슷하며 throws 예외 던지기를 함수에 선언하지 않아도 된다.

 

또한 try catch finally 전부다 식이기 때문에 변수에 할당이 가능하다.

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