자바 개발자를 위한 코틀린 필수사항

·2021년 12월 4일
1
post-thumbnail

📌 더 적은 타이핑


1. 세미콜론은 생략해도 된다

모든 표현식이나 명령문을 세미콜론으로 끝낼 필요가 없다.
세미콜론이 필요할 때는 한 줄에 두 개 이상의 표현식이나 명령문을 사용 할 때뿐이다.

2. 변수 타입 지정은 생략해도 된다

코틀린은 정적타입의 언어이다. 그렇다고 해서 변수 타입을 꼭 지정해야 한다는 의미가 아니다.
정적타입이란 변수의 타입이 컴파일 시점에 검증되고 정해져야 한다는 의미다.

입력

val greet = "hello"
    println(greet)
    //::class 객체의 코틀린 클래스를 확인
    println(greet::class)
    //::javaClass java 클래스를 확인
    println(greet.javaClass)

💻 출력

hello
class kotlin.String
class java.lang.String

변수 greet을 String 타입으로 추론한다


❗️타입추론을 사용할 때 변수 이름에 타입이 포함되지 않도록 주의해야 한다.
개발자들은 그동안 코드에서 타입에 대한 정보를 제공받았기 때문에 변수이름에 타입을 포함시키려는 경향이 있다.
//올바르지 않은 변수명
val taxRateDouble=0.08
val dTaxRate=0.08

타입정보가 생략이 가능한 경우에는 타입을 생략하고 적절한 변수 이름을 정하고 타입 추론을 사용하자

3. 클래스와 함수는 생략 가능하다

java와 같은 언어와 다르게, 코틀린은 명령문이나 표현식이 메소드에 속할 필요가 없고, 메소드는 클래스에 속할 필요가 없다
스크립트에서 실행될 때 코틀린은 JVM에서 실행하기 위해 필수적으로 필요한 랩퍼(wrapper)클래스와 메소드를 생성한다

아래에 나오는 소스 코드에서 함수는 클래스에 속하지 않는다 그리고 함수에 있는 코드는 단독으로 동작하고 다른 어떤 함수의 한 부분도 아니다.

입력

fun nofluff(){
    println("notfluff called..")
    throw RuntimeException("oops")
}
println("not in a function, calling notfluff")
try {
    nofluff()
}catch (e:Exception){
    val stacktrace=e.stackTrace
    println(stacktrace[0])
    println(stacktrace[1])

}

💻 출력

not in a function, calling notfluff
notfluff called..
chapter2.Standalone.notfluff(standalone.kts:6)
chapter2.Standalone.<init>(standalone.kts:10)

실행결과를 통해 알 수 있는 것

  • 코틀린은 메소드를 만들라고 강요하지 않는다
  • 해당 코드를 자동으로 클래스로 감싸준다

코틀린은 notfluff 함수를 Standalone이라는 동기화된 클래스의 메소드 안으로 넣었다.
여기서 클래스의 이름은 파일 이름으로 추론된다(standalone.kts)
출력 결과에 나온 <init>을 봤을 때 단독적으로 동작하는 코드는 클래스의 생성자 안으로 들어간걸 알 수 있다.

3. try-catch는 선택사항이다

Java 컴파일러를 사용할 땐 명시적 예외(Checked Exception)를 확실히 처리(catch)하거나 전달(throw)해야한다. 그에 반해 코틀린은 어떠한 예외도 처리하도록 강제하지 않는다

만약에 try-catch문이 없는 함수를 호출했을 때 예외가 발생하면 자동으로 해당 함수를 호출한 함수 또는 호출한 코드로 전달된다. 호출한 코드나 함수에서 예외를 핸들링하지 않는다면 프로그램을 종료된다.

예를 들어, Java의 Thread 클래스에 속한 sleep() 메소드는 명시적 예외를 전달한다.

JAVA

    public static void main(String[] args) {
        try {
            System.out.println("Lemme take a nap");
            //예외처리를 안하면 Unhandled exception 오류발생 
            Thread.sleep(1000);
            System.out.println("ah that feels good");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    

코틀린은 InterruptedException을 어떻게 처리할지 고민하느라 시간을 쓸 필요가 없다.

Kotlin

println("Lemme take a nap")
Thread.sleep(1000)
println("ah that feels good")

위 코드에는 try-catch문이 없지만 첫줄이 실행되고 1초후에 두번째 줄이 출력된다.
코틀린에서는 불필요한 catch 블록을 만들 필요가 없다. 개발자가 다루지 않은 예외는 자동으로 호출한 코드로 전파된다

📌 현명한 경고


코틀린 컴파일러는 코드 안의 다양한 잠재적 문제들을 찾아낸다
예를들어 함수나 메소드에 사용되지 않는 파라미터가 존재한다면 컴파일러가 경고를 줄것이다.
아래의 스크립트에서 compute()에서 전달된 파라미터는 사용되지 않는다.

입력

fun compute(n:Int)=0
println(compute(4))

💻 출력

0
unused.kts:3:13: warning: parameter 'n' is never used
fun compute(n:Int)=0
            ^

스크립트가 실행될 때 결과를 표시하면서 사용하지 않은 파라미터에 대한 경고를 함께 줄것이다

코틀린에서는 -Werror 옵션으로 경고를 오류처럼 다룰 수 있다.
커맨드 라인으로 해당 옵션을 실행할 수 있다.

입력

 kotlinc-jvm -Werror -script unused.kts

💻 출력

error: warnings found and -Werror specified
src/chapter2/unused.kts:3:13: warning: parameter 'n' is never used
fun compute(n:Int)=0
            ^

옵션이 없을 때와 대조적으로 실행결과 없이 오류 리포트만 나온다

📌 var 보다는 val


이뮤터블 변수를 정의하기 위해서 val을 사용한다.

val pi:Double = 3.14
val pi = 3.14 //타입이 명확하면 생략도 가능

변수 이름보다 타입을 앞에 쓰던 Java와는 다르게 코틀린에서는 변수 이름을 먼저 쓰고 콜론(:)을 쓰고 타입을 명시한다. 두 방법 모두 pi의 값을 변경할 수 없다.

val은 Java의 final과 비슷하다. val로 정의한 변수의 값을 변경하려는 시도를 하면 컴파일 오류가 발생한다.

val pi:Double = 3.14
pi = 3.14 //오류:val에는 재할당이 불가능하다.

변수의 값을 바꿀 수 있도록 하려면 var을 사용한다. var로 정의된 변수는 뮤터블하다.

var score = 10
println(score) //10
score = 11
println(score) //11

일반적으로 뮤터블보다 이뮤터블을 사용하는 것이 선호된다.
그 이유는 아래 코드를 보면 알 수 있다.

var factor = 2
fun doubleIt(n: Int) = n * factor
factor = 0
println(doubleIt(factor))

이 코드의 결과를 예상해보면 두가지 경우가 나온다

  • 4가 출력된다
  • 0이 출력된다

이렇게 뮤터블리티는 코드를 추론하기 어렵게 만든다.
뮤터블한 코드는 오류가 발생할 가능성이 높고 병렬화하기 어렵다.

물론 val도 주의해서 사용해야 한다. val은 참조에 대한 이뮤터블리티만을 보장해주고, 객체의 변화는 방지할 수 없다.

val message=StringBuilder("hello")
//message=StringBuilder("world") 오류 발생
message.append(" world")
println(message)

String은 이뷰터블이지만 Stringbuilder는 뮤터블이다. val은 참조에 대한 이뷰터빌리티만을 보장해주고, 객체의 변화는 방지할 수 없다.
위의 코드에서 message는 이뮤터블이다. 그러나 참조하는 객체가 그 변수를 이용해서 변경되었다.

📌 향상된 동일성 체크


java와 마찬가지로 코틀린도 두 가지 종류의 동일성 체크가 있다.

  • java의 euqual() 메소드와 코틀린의 == 연산자 : '구조상의 동일성'
  • java의 == 연산자, 코틀린의 === 연산자 : '참조상의 동일성'
    두 비교 대상이 같은 객체를 참조하는 경우 true 반환

코틀린의 == 는 java의 euqual() 보다 뛰어나다

아래의 코드를 보자

입력

println("hi" == "hi") 
println("hi" == "Hi")
println("hi" == null)
println(null == null)

💻 출력

true
false
false
true
equality.kts:3:9: warning: condition '"hi" == null' is always 'false'
println("hi" == null)
        ^
equality.kts:4:9: warning: condition 'null == null' is always 'true'
println(null == null)

만약 java의 equal()로 실행되었다면 NullPointerException이란 결과가 나왔을 것이다.
그러나 코틀린은 null을 안전하게 다룬다.

📌 문자열 템플릿

코틀린은 큰 따옴표 문자열 안에서 $ 심볼은 변수 앞에 붙여주면 어떤 변수라도 문자열 안으로 들어간다.
들어가야 할 표현식이 변수 하나보다 복잡한 명령문이면 $ {} 감싸서 사용할 수 있다.
$ 심볼 뒤에 변수이름 이나 표현식이 없고 /$ 을 사용하면 $ 은 그냥 문자로 표현된다.

입력

val price= 12.25
val taxRate=0.08
val output="The amount $price after tax comes to $${price*(1+taxRate)}"
val disclamier="The amount is in US$, that's right in \$only"
println(output)
println(disclamier)

💻 출력

The amount 12.25 after tax comes to $13.23
The amount is in US$, that's right in $only

var보다 val을 사용하라는 경고는 여기서도 적용된다

입력

var factor = 2
fun doubleIt(n: Int) = n * factor
var message = "The factor is $factor"
factor = 0
println(doubleIt(2))
println(message)

💻 출력

0
The factor is 2

함수 doubleIt()의 factor의 값은 함수가 호출된 시점에서 사용되지만 message의 factor의 값은 message가 만들어질 때 사용되었다.

이런 종류의 차이들이 인지부하를 증가시키고 코드를 유지보수하기 어렵게 만드니 주의해야한다.

📌 Raw 문자열

코틀린에서는 이스케이프 문자를 사용하는 대신에 시작과 끝에 큰따옴표 3개를 이용해 raw문자열을 사용할 수 있다.

기존 자바 문자열

String name = "Eve"
String escaped = "The kid asked, \"How 's it going, $name\""

코틀린에서의 문자열

val raw = """ The kid asked, "How 's it going, $name" """

💻 출력

The kid asked, "How 's it going, 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..."""
Dear Eve, a quick reminder about 
the party we have scheduled next Tuesday 
at the 'Low Ceremony Cafe' at Noon. | Please plan to...


문자열에 들여쓰기가 포함 될 경우에는 rimMargin()을 이용한다

수직선 (|)문자가 나올 떄 까지 공백을 제거한다

fun createMemoForTrim(name: String): String {
    if (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()
            
                    return memo
    }
    return " "
}

수직선 문자대신 다른 문자를 사용하고 싶을경우(~)

fun createMemoForTrim(name: String): String {
    if (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("~")

        return memo
    }
    return " "
}

💻 출력

Dear Eve, a quick reminder about 
the party we have scheduled next Tuesday 
at the 'Low Ceremony Cafe' at Noon. | Please plan to...

📌 표현식은 많이, 명령문은 작게

명령문은 아무것도 리턴하지 않을 뿐만 아니라, 부작용도 가지고있다. 여기서 말하는 부작용은 싱태가 변하고, 변수를 변하게 하는 것들을 말한다. 그에 반해 표현식은 결과를 리턴해주고, 어떤 상태도 변화시키지 않는다.

기존 자바 형태의 코드

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"

}

명령문은 아무런 리턴값을 주지 않기 때문에 뮤터블 변수를 만들어, 메소드 안에서 해당 변수를 수정했다.

코틀린으로 작성

fun canVote(name: String, age: Int): String {
  val status = if (age > 17) "yes, please vote" else "nope, please come back"

 return "$name, $status"
 
 }

해당 변수를 더 이상 변화시킬 필요가 없으므로 var이 아닌 val을 사용할 수 있다
변화된 코드는 덜 지저분하고, 오류도 덜 발생한다.

코틀린은 try-catch 코드도 표현식 취급한다

fun tryExpr(blowUp: Boolean): Int {
    return try {
        if (blowUp) {
            throw RuntimeException("fail")
        }
        2
    } catch (ex: Exception) {
        4
    } finally {

    }
}

tryExpr(false)
tryExpr(true)

예외가 발생하지 않는 경우 try식 안의 마지막 부분이 결과가 된다. 반대로 예외가 발생하면 catch 마지막 부분이 결과가 된다.

🔑 정리

코틀린은 가장 기본적인 프로그래밍 작업에서 사용되는 관용적인 코드 대부분을 없애버렸다.

세미콜론은 선택사항이고, 변수 선언을 할 때는 타입추론을 사용한다. 예외처리도 강요하지 않는다.
변수의 이뷰터빌리티와 뷰터빌리티를 미리 선택하기 때문에 프로그램의 안전성도 올라간다.

이런 점들이 프로그래밍을 쉽게 해주고 동시에 코틀린은 오류로부터 당신을 보호해 주기 위해서 현명한 경고를 해준다.

코틀린은 더 적은 코드로 Java와 같은 결과물을 만들 수 있다.



출처 : 다재다능 코틀린 프로그래밍

profile
개발하고싶은사람

0개의 댓글