반응형
본 포스트는 지인들과 스터디한 내용을 정리한 포스트입니다
http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9788931463422
1. Java와 다른 Kotlin 특징
- Java에서 사용하던 익숙한 버릇을 버리고 다음 내용들을 살펴보자
- 코틀린은 개발자가 변수를 만들때 뮤터블(변경 가능), 이뮤터블(변경 불가능)인지 선택해야한다
- 뮤터블 변수 사용을 줄이기 위해 명령문보다는 표현식을 선호한다
2. 더 적은 타이핑
- 세미콜론은 생략해도 된다
- 모든 표현식이나 명령문을 세미콜론으로 끝낼 필요가 없다
- 한 줄에 두 개 이상의 표현식이나 명령문을 사용할 때만 필요
- 물론 사용한다고 오류는 안나지만.. 사용하지 않는 게 좋다
- 변수 타입 지정은 생략해도 된다
- 코틀린은 정적 타입 언어지만 반드시 변수의 타입을 꼭 지정해야 한다는 뜻은 아니다
- 정적 타입이란 변수의 타입이 컴파일 시점에 검증되고 정확해져야 한다는 의미
- 코틀린은 컨텍스트에 기반한 스마트한 타입 추론 기능을 가지고 있다
- 타입 확인 코드
val greet = "hello" println(greet) println(greet::class) // 변수에 의해 참조되고 있는 객체의 코틀린 클래스 println(greet.javaClass) // Java 클래스
-
- 코틀린의 타입 추론 기능이 변수에 할당된 값에 기반하여 greet라는 변수의 타입을 String이라고 정의했다
- 타입 추론의 오해
- 코틀린은 컴파일 시점에 타입 추론을 한다 ( 실행 시간에 확인하는 것이 아님, 성능 저하 X )
- 예제코드
- var로 "greet" 라는 뮤터블 객체를 선언 및 초기화
- 코틀린의 타입추론으로 인해 "greet" 변수는 String type으로 취급받는다
- 다시 정수 0을 할당하려고하자 컴파일 오류 발생
- 변수의 이름에 타입 관련된 정보를 표현하지 말자 ( 내부 변수는 어짜피 나만 쓰는 것 )
- 타입 정보 생략이 가능한 경우 타입을 생략, 대신 적절한 변수 이름을 짓고 타입 추론을 사용하자
- 클래스와 함수는 생략 가능하다
- Java 언어와는 다르게, 코틀린은 명령문이나 표현식이 메소드에 속할 필요가 없고 메소드 또한 클래스에 소속될 필요가 없다
- 코드가 컴파일되거나, 스크립트로 실행될 때 코틀린은 JVM에서 실행하기 위해 필수적으로 필요한 랩퍼 클래스와 메소드를 생성한다
- 예제 코드
// standalone.kts import java.lang.Exception import java.lang.RuntimeException fun nofluff() { println("nofluff called...") throw RuntimeException("oops") } println("not in a function, calling nofluff()") try { nofluff() } catch (ex:Exception) { val stackTrace = ex.stackTrace println(stackTrace[0]) println(stackTrace[1]) }
- 실행 결과
-
- 코틀린은 스스로 nofluff() 메서드를 Standalone이라는 동기화된 클래스의 메소드 안으로 넣는다
- 클래스 이름은 파일이름을 통해 추론된다
- 단독적으로 동작하는 코드는 <init> 즉 생성자 안으로 들어간다
- try-catch는 선택사항이다
- Java 컴파일러를 사용할 때는 명시적 예외(Checked Exception)를 확실하게 처리(try-catch)하거나 호출한 쪽으로 던져야 했다(throw)
- 코틀린은 checked이든 unchecked든 상관없이 어떠한 예외도 처리하도록 강제하지 않는다
- try-catch가 없는 함수를 호출했을 경우, 예외가 발생하면 자동으로 해당함수를 호출한 함수 또는 호출한 코드로 전달된다
- 예제 코드
// exceptionHandling.kts println("start") fun test1() { println("start test1") throw Exception("Exception") } fun test2() { println("start test2") test1() } println("start main") try { test2() } catch (ex:Exception) { println(ex.stackTrace[0]) } println("end main")
- 실행 결과
- test1() 메서드가 호출되고 checked Exception을 발생시켰지만 Java와 다르게 호출부인 test2() 메서드에서 반드시 try-catch문을 쓰지않아도 자연스럽게 상위 호출부로 exception을 throw한다
3. 현명한 경고
- 컴파일 시간에 빠른 경고를 받을 수 있다면 개발자들은 잠재적 문제를 사전에 대처할 수 있다
- 코틀린 컴파일러는 코드 안의 다양한 잠재적 문제들을 찾아낸다
- 예제 코드: 함수나 메서드에서 사용되지 않는 파라미터가 존재한다면 컴파일러가 경고해준다
// unused.kts fun compute(n: Int) = 0 println(compute(4)) // result 0 unused.kts:1:13: warning: parameter 'n' is never used fun compute(n: Int) = 0 ^ Process finished with exit code 0
- 코틀린 -Werror 옵션으로 스크립트를 실행시키면 경고를 오류처럼 다룰 수 있다
4. var보다는 val
- 이뮤터블 변수(상수 또는 값)을 정의하기 위해서 다음처럼 val을 사용한다
- 코틀린은 변수의 이름이 변수의 타입보다 중요하다고 여기기 때문에 변수 이름이 타입보다 우선한다
val pi: Double = 3.14
- 변수 타입이 명확하다면 타입추론을 이용해 : Double 구문을 생략할 수 있다
- val
- Java의 final과 비슷하다
- 변수의 값을 바꾸거나 재할당하려는 시도를 한다면 컴파일 오류가 발생
- 이뮤터블
- var
- 뮤터블
- 불명예의 키워드
- 뮤터빌리티(변경가능성)이 있는 변수는 추론하기 어렵게 만들고, 오류를 발생할 가능성을 높인다
- val은 변수나 참조만 상수로 만든다 ( 객체 자체를 상수로 만드는 것은 불가 )
- 즉 Java와 같이 참조에 대한 immutability만 보장해주고, 객체의 변화는 방지할 수 없다
- 가능하면 var보다는 val을 사용하자 ( 권장 )
5. 향상된 동일성 체크
- Java와 마찬가지로 코틀린도 두 가지 동일성 체크가 있다
- 값 비교
- Java equal()
- 코틀린 '=='
- 구조상의 동일성
- 참조 대상 비교
- Java '=='
- 코틀린 '==='
- 참조상의 동일성
- 추가적으로 코틀린의 값 비교('==')는 Java의 equal()보다 뛰어나다
- Java 코드로 str1.equal(str2)를 실행시킬 경우 str1이 null이면 NullPointException을 발생시킨다
- 코틀린의 코드 str1 == str2는 null을 안전하게 다룬다, 즉 str1 혹은 str2이 Null이여도 NPE가 발생하지 않는다
- 즉 코틀린에서 == 연산자를 사용할 때, null 체크를 먼저하고 equal() 메서드를 실행한다
6. 문자열 템플릿
- '+' 연산자를 이용해서 값을 연결해 문자열을 만들면 코드는 장황해지고, 유지보수는 여려워진다
- 문자열 템플릿은 이런 문제를 해결해준다
- 큰 따옴표로 엮어진 문자열 안에서 $ 심볼을 변수 앞에 붙여주면 어떤 변수라도 문자열 안에 들어간다
// stringTemplete.kts val temp1 = "hi" val temp2 = 30 val temp3 = 3.0 val str1 = "$temp1 my age is $temp2, $temp3" println(str1)
- 단순 표현식말고 복잡한 수식을 넣으려면 ${...}로 감싸서 사용하면 된다
val temp1 = "hi" val temp2 = 30 val temp3 = 3.0 var str2 = "$temp1 => ${temp2 * temp3}" println(str2)
- $심볼 뒤에 변수이름이나 표현식이 없으면 $는 그대로 문자 취급된다, 또한 역슬래시를 이용해서 $심볼을 명시적으로 문자취급할 수 있다
val temp1 = "hi" val temp2 = 30 val temp3 = 3.0 var str3 = "\$temp1 => \${temp2 * temp3}$" println(str3) // result // $temp1 => ${temp2 * temp3}$
- 큰 따옴표로 엮어진 문자열 안에서 $ 심볼을 변수 앞에 붙여주면 어떤 변수라도 문자열 안에 들어간다
- 문자열 템플릿은 변수로 초기화될 때 사용되므로 만들어질 당시의 변수값으로 된다, var(뮤터블)를 이용하여 문자열을 만든 후 값을 변경하더라도 변경 전 변수의 값으로 된다 ( var 대신 val을 사용해야하는 이유 )
7. RAW 문자열
- 이스케이프 문자
- 이스케이프 문자를 사용하면 코드가 지저분해진다
- 코틀린에서는 이스케이프 문자를 사용하는 대신 시작과 끝에 큰따옴표 세 개를 이옹해 raw 문자열을 사용할 수 있다
- 예제 코드
// escapeString.kts val name = "joojimin" val escapeStr1 = "The kid asked, \"How's it going, $name?\"" println(escapeStr1) val escapeStr2 = """The kid asked, "How's it going, $name?"""" println(escapeStr2) // results The kid asked, "How's it going, joojimin?" The kid asked, "How's it going, joojimin?"
- 이스케이프할 필요가 없는 작고, 단순하고, 간단한 문자열이라면 ""로 작성하자
- 문자열이 복잡하거나 여러 줄을 써야할 경우엔 raw 문자열(""" """)을 사용하자
- 멀티라인 문자열
- 기존 멀티라인 문자열을 만들기 위해서는 '+'를 이용하여 멀티 라인을 구성해야했다
- 코틀린에서는 raw 문자열(""" """)을 이용해 '+' 연산자 없이도 멀티라인 문자열을 만들 수 있다
- 예제 코드
// MultiLineString.kts val name = "Eve" val memo = """Dear $name, a quick reminder about the party we have scheduled next Tuesday at the 'Low Ceremony Cafe' at Noon. | Please plan to...""" println(memo)
- 결과
- 여기서 문제가 있다, String을 표현하기 위해서 라인을 추가했지만 코드 포메터에 의해 두번째, 세번째 라인에 들여쓰기가 포함이 됐다 ( 혹은 뎁스가 늘어나면서 표현하기 싫은 들여쓰기가 추가된 경우도 마찬가지 )
- 이를 해결하기 위해 각 라인 앞에 default 들여쓰기 제거 문자 | 을 추가하고 trimMargin() 메서드를 통해 들여쓰기를 제거하면된다 ( 들여쓰기 제거 문자를 바꾸고 싶으면 trimMargin 파라미터로 넘기면된다 )
-
val name = "Eve" val memo = """Dear $name, a quick reminder about the |party we have scheduled next Tuesday at |the 'Low Ceremony Cafe' at Noon. | Please plan to...""".trimMargin() println(memo)
- 줄바꿈이 시작되는 첫번째 문자에만 적용된다
8. 표현식은 많이, 명령문은 적게
- Java, C#, JavaScript 같은 언어들은 표현식보다는 명령문을 더 많이 가지고 있다( if문, for문, try문 등등 )
- 명령문은 아무것도 리턴하지 않으며, 상태가 변하고, 변수를 변하게 하고 등등 어두운 면을 가지고 있다
- 반면 표현식은 결과를 리턴해주고, 어떠한 상태도 변화시키지 않는다
- 기존 명령문 코드 형식 ( 코틀린 )
fun canVote(name: String, age: Int): String { var status: String if ( age > 17 ) { status = "yes, please vote" } else { status = "nope, please come back" } return "$name, $status" } println(canVote("Eve", 12))
- 위 코드를 표현식으로 변경한 형식 ( 코틀린 )
fun canVoteWithExpressionStyle(name: String, age:Int): String { val status = if (age > 17) "yes, please vote" else "nope, please come back" return "$name, $status" } println(canVoteWithExpressionStyle("Eve", 12))
- 명령문보다 표현식을 활용하면 var대신 val를 사용할 수 있을 뿐더러, 훨씬 간결한 코드가 작성되는 것을 예제로 확인해 볼 수 있다
728x90
반응형
'Study > 다재다능 코틀린 프로그래밍' 카테고리의 다른 글
Day 6. 오류를 예방하는 타입 안정성 (0) | 2021.11.02 |
---|---|
Day 5. 컬렉션 사용하기 (0) | 2021.10.31 |
Day 4. 외부 반복과 아규먼트 매칭 (0) | 2021.10.27 |
Day 3. 함수를 사용하자 (0) | 2021.10.25 |
Day 1. 코틀린 시작하기 (0) | 2021.10.19 |