쟈미로그

[Kotlin] 자바 개발자를 위한 코틀린 입문 - 1 본문

Kotlin

[Kotlin] 자바 개발자를 위한 코틀린 입문 - 1

쟈미 2023. 7. 2. 23:42

새 프로젝트 언어가 코틀린으로 정해지면서 코틀린 공부의 필요성이 코앞으로 닥쳤다.

인프런 최태현 강사님의 자바 개발자를 위한 코틀린 입문 강의를 듣고 요약해보잣!

 


<섹션 1 - 코틀린에서 변수와 타입, 연산자를 다루는 방법>

01강. 코틀린에서 변수를 다루는 방법

1. 변수 선언 키워드 - var, val

코틀린은 자바와 달리 변수 선언 시 무조건 선언 키워드 var, val를 써줘야함.
이 선언 키워드 역할은 수정 가능 여부를 명시하는 것임.

  • var : variable. 가변 변수.
  • val : value. 불변 변수. (자바의 final)

 

타입 선언

코틀린에선 컴파일러가 자동으로 타입을 추론해주기 떄문에 의무적으로 타입을 쓸 필욘없음.

원한다면 : 기호로 표현 가능.

초기값 지정 안한 경우엔 컴파일러가 타입 추론을 못하므로 타입을 명시해줘야 컴파일 에러가 안남. 

타입은 그냥 명시하는게 좋아보인다..

 

val 컬렉션에는 element를 추가할 수 있다!

/* Java */
final List<Integer> num1 = Arrays.asList(1, 2);
num1.add(3);  // final 컬렉션은 원소추가는 가능함.

 

자바와 동일하게, 코틀린에서도 val 키워드가 붙은 콜렉션에 원소 추가는 가능함.

이유?
final은 한번 지정한 참조에 대한 변경을 허용 안하는 것.
즉, 객체가 메모리에 올라가있을 때 그 메모리 주소를 변경 못하게 하는 것이라 새로운 메모리 할당은 가능.

 

2. 코틀린에서 Primitive Type

primitive type과 reference type을 갖는 자바와 달리 코틀린은 primitive type이 따로 없음. 


성능상 문제 없을까?

코틀린 독스 보면 <실행시엔 primitive value로 표현되지만 코드에선 평범한 클래스처럼 보인다.> 라고 돼있음.
즉, 타입 클래스가 기본 타입까지 포함하고 있어서, 연산할 때는 코틀린이 내부적으로 primitive type으로 바꿔서 잘 처리해준다는 뜻. 
(실제로 인텔리제이 Tools -> Kotlin -> Show kotlin by-recode 해보면 코틀린 to 바이트코드 파일이 뜨는데, 얘를 디컴파일해서 자바코드를 얻어보면 primitive type으로 선언돼있다.)

->  프로그래머가 boxing/unboxing 고려안해도 되도록 코틀린이 알아서 처리해줌!!

 

3. 코틀린에서 nullable 변수

자바는 모든 레퍼런스 타입은 null이 가능함.
이렇게 “null이 가능함”을 코틀린에선 어떻게 표현할까?
-> 코틀린은 기본적으로 모든 변수가 null이 불가능함!!

null이 가능하게 하려면, 물음표(?) 키워드를 변수 선언 시 타입 뒤에 붙여줘야함.

/* Kotlin */
var num1: Long? = 10L
num1 = null  // 가능

 

4. 코틀린에서 객체 인스턴스화

자바와 달리 new를 안붙임.

/* Kotlin */
var person = Person()

02강. 코틀린에서 null을 다루는 법

1. 코틀린에서 null 체크

/* Kotlin */
fun startsWithA(str: String?): Boolean {
    if (str == null)
    ~~
    return str.startsWith("A);
}

 

코틀린에선 null이 가능한 타입을 완전히 다르게 취급함.
그럼 null 가능 타입만을 위한 기능은 없는가?


2. Safe Call과 Elvis 연산자

  • Safe Call
/* Kotlin */
val str: String? = "a"
str.length // 불가능
str?.length // 가능

 

null이 아니면 실행하고, null이면 뒤에 딸려오는 함수/프로퍼티 등을 실행하지 않음. (그대로 null) 

  • Elvis 연산자 (?:)
/* Kotlin */
val str: String? = "a"
str?.length ?: 0

 

앞의 연산 결과가 null이면 뒤의 값을 사용 (약간 삼항연산자 느낌..?)


엘비스 연산자는 early return에도 사용가능하다!! **

/* Java */
public boolean test(Long num) {
    if (num == null)
    return false;
    ...
}

를 코틀린으로 바꾸면

/* Kotlin */
fun test(num: Long?): Boolean {
    num ?: return false
    ...
}


3. Null 아님 단언 (!!)

Nullable type이긴 하지만, 아무리 생각해도 null이 되면 안되는/될 수 없는 경우
-> !! 로 null 아님 단언을 할 수 있다.

fun startsWithA(str: String?): Boolean {
	return str!!.startsWith("A)
}

 

이렇게 할 경우, 원래라면 str이 nullable 타입이라서 따로 null처리를 하지않고 바로 str.startsWith~~를 해버리면 컴파일에러가 난다.
하지만 !!로 널아님 단언을 하면, 에러가 나지 않는다!
-> 만약 만에하나 null이 들어와버렸다면? **런타임 에러**난다.

그래서 널아님 단언은 진짜 잘 사용해야할듯하다.
진짜 null이 아니어야하는데, 불가피하게 DB 등에서 이미 nullable한 필드로 등록이 돼버렷다거나 하는 상황에서 실제론 null이 들어오면 절대 안된다! 하는 값들은 널아님 단언을 해야할듯?


4. 플랫폼 타입

코틀린에서 자바 코드를 가져다 사용할 때 어떻게 처리될까?!

코틀린은 자바의

  • javax.annotation 패키지
  • android.support.annotation 패키지
  • org.jetbrains.annotation 패키지

등에 있는 null과 관련된 어노테이션들을 인식한다!
그래서 NotNull, Nullable같은 어노테이션들이 붙어있으면 이걸 인식하고 컴파일 타임에 에러를 낼 수 있다.

-> 그런데 만약 이런 어노테이션으로 null 명시가 안돼있다면?
코틀린은 이 값이 nullable인지 아닌지 알 수 없다!
-> 이런 타입을 플랫폼 타입이라 한다.

플랫폼 타입?
코틀린이 null 관련 정보를 알 수 없는 타입
런타임에러가 날 수 있다!

그러므로, 자바-코틀린을 같이 사용한다면 자바에서 null 명시를 잘 해두거나, 자바 라이브러리를 사용하게 된다면 코드를 잘 까보자.


03. 코틀린에서 Type을 다루는 방법

1. 기본타입

코틀린은 타입추론을 해주기때문에, 타입을 명시하지 않아도 된다.
How? -> 선언 시 초기화해준 값을 보고!
(그래서 선언만 하고 초기화 안할 때는 타입 명시해야하는 것)


타입전환 (기본타입 간)

자바에서 타입전환은 암시적으로 일어나지만,
코틀린에선 타입전환을 명시적으로 해줘야한다.
(Ex) Int -> Long이 자바에선 따로 형변환 코드 없이 가능하지만, 코틀린에선 아예 컴파일에러 남.

val num1 = 1
val num2: Long = num1 // 컴파일 에러
val num3: Long = num1.toLong() // 성공


null 가능 변수의 타입전환 (기본타입)

val num1: Int? = 1
val num2: Long = num1.toLong() // 컴파일에러
val num3: Long = num1?.toLong() ?: 0L // 성공


2. 타입 캐스팅

그럼 기본타입이 아닌 일반타입의 타입전환은?

fun pringAgeIfPerson(obj: Any) {
    if (obj is Person) {
        val person = obj as Person
        println(person.age)
    }
}

 

스마트 캐스트

사실 위 코드의
val person = obj as Person
부분은 빼도 된다. 즉, 바로 println(person.age)가 가능한 것. (자바는 안됨)
-> 그 이유는, 코틀린 컴파일러는 컨텍스트를 파악해서 이미 if문을 통해 타입 검증을 했다면 따로 캐스팅을 안해줘도 된다고 스마트 캐스팅을 해주기 떄문!!


null 가능변수의 타입전환 (일반타입)

fun pringAgeIfPerson(obj: Any?) {
    val person = obj as? Person // 그냥 as는 NPE남
    println(person?.age) // person의 타입은 Person?임
}

 

 

as와 as?

-> as와 달리 as?는 value가 해당 타입이 아니었을 때 에러가 나지 않고 null로 처리된다.
-> 즉, as?는 세이프콜 처럼 안전한 타입 형변환이라고 봐도 됨.


3. 코틀린의 특이한 타입 3가지

Any

  • 자바의 Object 역할 (== 모든 객체의 최상위 타입)
  • Int/Long 등 타입 자료형도 Any가 최상위 타입임
  • 자바 Object처럼 equals/hashCode/toString 메소드 존재

Unit

  • 자바의 void 역할
  • 자바 void와 달리, Unit은 그 자체로 제네릭의 타입인자로 사용 가능

Nothing

  • 함수가 정상적으로 끝나지 않았다는 사실을 표현
  • 무조건 예외를 반환하는 함수/무한루프 함수 등에 사용
    (이런 경우가 거의 없으니.. 잘 사용하진 않는다 함)


4. String interpolation / String indexing

문자열 생성

코틀린에선 문자열을 어떻게 만들까?
(자바에선 String.format이나 StringBuilder 등을 사용함)

${변수}를 사용하면 값이 들어감

val person = Person("조호성", 100)
val log = "사람의 이름은 ${person.name}이고 나이는 ${person.age}세 입니다"

 

문자열 인덱싱

자바에선 문자열의 특정 인덱스 문자를 str.charAt(0)으로 가져와야했다. (직접 인덱스로 접근은 안됨)
-> 코틀린은 배열처럼 str[0]같이, 인덱스 접근으로 특정 인덱스 문자 가져오기가 가능!!


04. 코틀린에서 연산자를 다루는 방법

1. 단항 연산자 / 산술 연산자

자바와 완전 동일
(+, -, /, *부터 +=, -= 등 모두 동일)


2. 비교 연산자와 동등성, 동일성

비교 연산자

자바와 동일함.
-> But, 자바와 달리 객체 비교 시 비교연산자를 사용해도 compareTo를 호출해줌!
-> 가독성이 좋아진다


동등성/동일성

  • 동등성(Equality) : 두 객체 값이 같은지
  • 동일성(Identity) : 완전히 동일한 객체인지 (= 주소가 같은지)

자바에선 동일성 비교는 ==, 동등성 비교는 equals를 사용한다.
-> 코틀린에선 동일성 비교는 ===, 동등성 비교는 ==를 사용하면 간접적으로 equals를 호출해즘!


3. 논리 연산자와 코틀린에 있는 특이한 연산자

논리연산자

&&, ||, ! 모두 자바와 동일. 자바처럼 lazy 연산함.

lazy 연산?
a && b 에서 a가 false였거나,
a || b 에서 a가 true였는 등 선행 값 확인으로 이미 결과가 결정됐다면 뒤의 값들은 실행 안하는 것.

 

특이한 연산자

  • in / in!

컬렉션이나 값 범위에 포함돼있다/아니다

  • a..b

a부터 b까지의 범위 객체를 생성한다 

4. 연산자 오버로딩

코틀린에선 객체마다 연산자를 직접 정의할 수 있다.
즉, 덧셈을 plus라는 메소드로 클래스내에 정의한 후 그 메소드 호출로 덧셈을 해야하는 자바와 달리, 
코틀린에선 Plus 메소드 정의 후엔 + 기호로 객체 덧셈이 가능해진다는 것.

'Kotlin' 카테고리의 다른 글

코루틴 입문 - 1. 코루틴 기초  (1) 2023.09.10
[Kotlin] 자바 개발자를 위한 코틀린 입문 - 2  (0) 2023.07.16
Comments