Dev/Kotlin

Kotlin (제네릭, null, null 값 허용하기, 함수 파라미터에 null 허용 설정하기, Nullable, Safe Call, Elvis Operator)

Walker_ 2024. 4. 23. 15:04
7. 제네릭
제네릭 Generics은 입력되는 값의 타입을 자유롭게 사용하기 위한 도구,

다음은 실제 MutableList의 선언부.
public interface MutableList<E> : List<E>, MutableCollection<E>

클래스명 옆에<E>부분에 String 같은 특정 타입이 지정되면 클래스 내부에 선언된 모든 E에 String 타입으로 지정.
결과적으로 var list: Array<E>가 var list: Array<String>으로 변경이 되는 것.

이렇게 설계된 클래스는 주로 구현하는 용도로 사용하며 컬렉션이나 배열에서 입력되는 값의 타입을 특정하기 위해 다음과 같이 사용.
var list: MutableList<String> = mutableListOf("abc", "def", "ghi")

 

fun testGenerics() {
    // String으로 제네릭을 사용했기 때문에 list 변수에는 문자열만 담을 수 있음.
    var list: MutableList<String> = mutableListOf()
    list.add("abc")
    list.add("def")
    list.add("ghi")
//    list.add(30) // 입력 오류가 발생

    // String 타입의 item 변수로 꺼내서 사용할 수 있음.
    for (item in list) {
        println("리스트에 저장된 값은 ${item}입니다")
    }
}

 

코틀린은 null 값의 처리에 많은 공을 들인 언어.
null은 프로그래밍하면서 항상 이슈의 중심에 있는데 null로 인해 프로그램 전체, 혹은 앱 전체가 멈출 수 있기 때문.

프로그램이 멈출 수 있는 상황을 코드로 만들어 봄
1개 메서드를 가진 클래스를 만듬

 

class One {
    fun printMe() {
        print("null safety")
    }
}

 

main() 함수 안에 one 변수를 하나 선언하고 타입으로 클래스를 지정.
그리고 특정 조건이 만족할 때만 선언한 변수에 생성자를 호출해서 저장하는 조건문 if를 만듬
그리고 변수를 통해 해당 클래스의 메서드를 호출
fun main() {
    var one: One
    if (1 > 2) {
        one = One()
    }
    one.printMe()
}

 

이 코드에서 조건이 false이기 때문에 one 변수는 아무것도 없는 null 상태가 됨.
print 메서드를 호출하면 null 포인터 예외가 발생하면서 프로그램이 다운,
물론 IDE에서 오류를 발생시켜 컴파일 되지 않도록 막아줌

하지만 코드양이 많아 지면 이런 상황이 언제든지 발생할 수 있는데, 코틀린은 이런 상황을 방지하기 위해서 안전장치를 마련해둠.
그 결과물이 Null Safety

 

1. null 값 허용하기 : ?
코틀린에서 지정하는 기본 변수는 모두 null이 입력되지 않음
null 값을 입력하기 위해서는 변수를 선언할 때 타입 뒤에 ? (Nullable, 물음표)를 입력.

var variable : String?

1) 변수에 null 허용하기

변수의 타입 뒤에 물음표를 붙이지 않으면 null 값을 입력할 수 없음
null 예외를 발생시키고 싶지 않다면 기본형으로 선언.

 

fun main() {
    var nullable: String? // 타입 다음에 물음표를 붙여서 null 값을 입력할 수 있음.
    nullable = null
    
    var notNullable : String
//    notNullable = null // 일반 변수에는 null을 입력 할 수 없음
}
2) 함수 파라미터에 null 허용 설정하기

함수의 파라미터에도 null 허용 여부를 설정할 수 있음
함수의 파라미터가 null을 허용하려면 해당 파라미터에 대해서 null 체크를 먼저 해야만 사용할 수 있음
 

 

fun nullParameter(str: String?) {
    // 파라미터 str에 null이 허용되었기 때문에 함수 내부에서 null 체크를 하기 전에는 str을 사용할 수 없음
    if (str != null) {
        var length = str.length
    }
}

 

위의 코드처럼 str 파라미터를 조건문에서 null인지 아닌지를 체크해야지만 사용할 수 있음

3) 함수의 리턴 타입에 null 허용 설정하기
함수의 리턴 타입에도 물음표를 붙여서 null 허용 여부를 설정할 수 있음
fun nullReturn(): String? {
    return null
}
함수의 리턴 타입에 Nullable이 지정되어 있지 않으면 null 값을 리턴할 수 없음
2. 안전한 호출 : ?.
변수를 Nullable로 만들기 위해서 물음표를 사용
?.(Safe Call, 물음표와 점)을 사용하면 null 체크를 간결하게 할 수 없음

Nullable인 변수 다음에 ?.을 사용하면 해당 변수가 null일 경우 ?. 다음의 메서드나 프로퍼티를 호출하지 않음
 
fun testSafeCall(str: String?): Int? {
    // 문자열 길이를 반환하는 length 프로퍼티를 호출했는데 str 변수 자체가 null일 경우는 length 프로퍼티를 호출하지 않고
    // null을 반환
    
    var result: Int? = str?.length
    return result
}
3. Null 값 대체하기 : ?:
?: (Elvis Operator, 물음표와 클론)을 사용하면 원본 변수가 null일때 넘겨줄 기본값을 설정할 수 있음

다음 코드에서 Sage Call 다음에 호출되는 프로퍼티 뒤에 다시 ?:을 붙이고 0이라는 값을 표시.
이렇게 호출하면 str 변수가 null일 경우 가장 뒤에 표시한 0을 반환

 

fun testElvis(str: String?): Int {
    // 문자열 길이를 반환하는 length 프로퍼티를 호출했는데 str 변수 자체가 null일 경우는 length 프로퍼티를 호출하지 않고
    // null을 반환

    var resultNonNull: Int = str?.length ?: 0
    return resultNonNull
}

 

물음표의 위치와 형태에 따라 Nullable, Safe Call, Elvis Operatro가 구분.

* Nullable
표기법 : 선언하는 변수의 타입 다음에 ? 표기.
사용 목적 : null을 입력받기 위해 사용.
사용 예 : var nullable: String?

* Safe Call
표기법 : 선언된 변수의 이름 다음에 ?. 표기
사용 목적 : null일 때 ?. 다음에 나오는 속성이나 명령어를 처리하지 않기 위해 사용.
사용 예 : var result = 변수?.length

* Elvis Operator
표기법 : 선언된 변수의 이름 다음에 ?: 표기
사용 목적 : null일 때 ?: 다음에 나오는 값을 기본 값으로 사용
사용 예 : var result = 변수 ?: 0

 

 

 


공부 과정을 정리한 것이라 내용이 부족할 수 있습니다.

부족한 내용은 추가 자료들로 보충해주시면 좋을 것 같습니다.

읽어주셔서 감사합니다 :)