본문 바로가기
Programming/Kotlin

Java와 Kotlin 비교: default value, when, 확장함수

by Renechoi 2023. 10. 22.

Kotlin의 default value 사용하기


public class Exam08 {
    public static void main(String[] args) {

        var registerDto = new Store();

        new Exam08(registerDto);
    }

    public Exam08(Store store){
        // null point -> 터짐 
        var stringRegisteredAt = toLocalDateTimeString(store.getRegisteredAt());
    }

    public String toLocalDateTimeString(LocalDateTime localDateTime){

        // null을 방지하려고 이런식으로 방어 로직을 짬 
        LocalDateTime temp; 
        if(localDateTime == null){
            temp = localDateTime.now();
        }

        // 혹은 optional로 받아서 orElseGet으로 

        return localDateTime.format(DateTimeFormatter.ofPattern("yyyy MM dd"));
    }
}


@Data
class Store {
    private LocalDateTime registeredAt;
}



// 이러한 null 체크 로직, optional을 코틀린에서 더 쉽게 처리할 수 있다면 ? 


fun main() {

    Exam08(Store())
}

data class Store(
    // data 클래스를 만들 때부터 null일 수 있음을 명시해줌 
    var registeredAt: LocalDateTime ?= null

)

class Exam08{

    constructor(store: Store){
    }

    // 메서드에서 반환 -> 콜론으로 -> 뭘 리턴하는지는 마지막에 표현함 
    fun toLocalDateTimeString(localDateTime: LocalDateTime?):String{

        // 이것만으로 변수 세팅이 됨 
//        var temp = localDateTime ?: LocalDateTime.now()

        // ?을 작성하면 -> 
//        return localDateTime?.format(DateTimeFormatter.ofPattern("yyyy MM dd"))

        // 합치면 ->
        return (localDateTime ?: LocalDateTime.now()).format(DateTimeFormatter.ofPattern("yyyy MM dd"))
    }
}

Kotlin에서의 Null Safety

Kotlin은 널 안정성(Null Safety)을 명시적으로 다루어, 자바에서 발생하는 Null Pointer Exception을 피할 수 있는 여러 방법을 제공한다.

기본값(Default Value)

Kotlin에서는 함수나 클래스의 파라미터에 기본값을 설정할 수 있다. 이를 통해 널 값을 피하거나 대체할 수 있다.

data class Store(
    var registeredAt: LocalDateTime ?= null
)

Store 클래스의 registeredAt 프로퍼티에 기본값으로 null을 설정했다. 이렇게 하면 Store 객체를 생성할 때 registeredAt을 명시적으로 설정하지 않아도 된다.

Elvis 연산자(?:)

Kotlin에서는 ?: 연산자를 사용하여 널 체크를 더 간결하게 할 수 있다.

var temp = localDateTime ?: LocalDateTime.now()

위 코드에서 localDateTimenull이라면 LocalDateTime.now()temp에 할당된다.

메서드 예제

fun toLocalDateTimeString(localDateTime: LocalDateTime?):String {
    return (localDateTime ?: LocalDateTime.now()).format(DateTimeFormatter.ofPattern("yyyy MM dd"))
}

Java와 Kotlin의 차이점

  • Java에서는 Null 체크를 위해 명시적인 조건문이나 Optional을 사용해야 한다.
  • Kotlin에서는 Elvis 연산자, 기본값 설정 등을 통해 더 간결하고 안전한 코드를 작성할 수 있다.

when

Kotlin의 when 표현식은 Java의 switch문과 if-else 조건문을 대체할 수 있는 문법이다. 코드를 더 간결하고 읽기 쉽게 만들 수 있게 도와준다.


public class Exam09 {
    public static void main(String[] args) {

    }

    public Exam09(StoreUser storeUser){
        // service logic 

        if (storeUser.getRole().equals("MASTER")){

        } else if (storeUser.getRole().equals("ADMIN")) {

        } else if (storeUser.getRole().equals("USER")){

        }



        switch (storeUser.getRole()){
            case "MASTER":
                break;
            case "ADMIN":
                break;
            case "USER":
                break;
            default:
                // 로직 
        }

        try {

        } catch (Exception e){
            if (e instanceof NullPointerException){

            }
        }

    }
}


@Data
class StoreUser{
    private String name;
    private String role;
}


// java에서 이와 같이 분기문을 처리하는 방식 -> 코틀린에서는 ? 

fun main(){

    when(""){
        ""-> {

        }

        "MASTER" ->{

        }
    }



    var result = when(""){
        "MASTER" -> {
            "master"
        }
        else -> {
            "default"
        }
    }



    var any : Any = "";

    // instanceof 와 동일 
    when (any) {
        is String ->{}
        is Int ->{}
        is Boolean ->{}
    }


    // switch 문과 매우 동일하게 생겼지만 break을 안해도 된다. 
    var exception = RuntimeException()
    when(exception) {
        is NullPointerException ->{}
    }


    // 표현식을 담을 수도 있다. 
    var number = 10

    when (val n = number % 2){
        0 -> {println(n)}
        else -> { println(n)}
    }

}

기본 사용법

Java에서의 분기문

Java에서는 switch문과 if-else로 분기 처리를 한다.

if (storeUser.getRole().equals("MASTER")) {
    // MASTER 로직
} else if (storeUser.getRole().equals("ADMIN")) {
    // ADMIN 로직
} else if (storeUser.getRole().equals("USER")) {
    // USER 로직
}

Kotlin에서의 when

Kotlin에서는 when을 사용하여 이를 훨씬 간결하게 표현할 수 있다.

when(storeUser.role) {
    "MASTER" -> {
        // MASTER 로직
    }
    "ADMIN" -> {
        // ADMIN 로직
    }
    "USER" -> {
        // USER 로직
    }
    else -> {
        // 기타 로직
    }
}

when의 다양한 사용법

결과 값을 변수에 할당

when 표현식의 결과 값을 변수에 할당할 수 있다.

val result = when(storeUser.role) {
    "MASTER" -> "master"
    else -> "default"
}

Type Check

Kotlin에서 wheninstanceof와 같은 역할도 할 수 있다.

when(any) {
    is String -> {}
    is Int -> {}
    is Boolean -> {}
}

표현식 사용

Kotlin에서는 when 내부에서도 표현식을 사용할 수 있다.

when (val n = number % 2) {
    0 -> { println(n) }
    else -> { println(n) }
}

장점과 특징

  • break문이 필요 없다. 각 분기마다 자동으로 break가 적용된다.
  • 다양한 표현식과 함께 사용할 수 있어 유연하다.
  • 코드가 간결해져 가독성이 향상된다.

확장 함수

코틀린에서는 확장 함수(extension function)라는 강력한 기능을 제공한다. 이를 통해 이미 존재하는 클래스에 새로운 함수를 추가할 수 있다. Java에서 복잡하게 작성해야 했던 코드를 코틀린에서는 매우 간결하게 만들 수 있다.


public class Exam10 {

    public Exam10(ExamUser examUser){
        if (examUser!=null && examUser.getName() != null){
            if (!examUser.getName().isBlank()){
                // 로직 
            }
        }

        // 혹은 

        var optionalUser = Optional.ofNullable(examUser);

        optionalUser.ifPresent(it ->{
            Optional.ofNullable(examUser.getName()).ifPresent(name->{
                if(!name.isBlank()){
                    // 로직 
                }
            });
        });

        // 이러한 방어 코드를 작성하게 됨 

    }
    public static void main(String[] args) {
        new Exam10(new ExamUser());    
        new Exam10(new ExamUser("aa"));
    }
}



@Data
@NoArgsConstructor
@AllArgsConstructor
class ExamUser{
    private String name;
}




// notblank를 사용하려고 이렇게 util 함수를 만들기도 한다. 
class StringUtils{
    public static boolean notBlank(String value){
        return !value.isBlank();
    }
}


class ObjectUtils{
    public static boolean isNotNull(Object object){
        return object != null;
    }
}


// 이러한 코드를 코틀린에서는 ? 

fun main(){
    val user = ExamUser(name = "abcd");
    exam10(user)
}



fun exam10(examUser: ExamUser?){
    examUser?.let {
        it.name?.let{name ->
            println(name)
        }
    }


    examUser?.let{
        if(it.name.isNullOrEmpty()){
            // 로직 
        }

        // 뒤에 만든 함수를 마치 스트링 클래스에 생긴 메서드처럼 동작을 함 -> 내가 원하는 특정 클래스를 이미 존재하는 클래스에 추가가 가능함 
        if(it.name.isNotNullOrBlank()){

        }



        if (examUser.isNotNull() && examUser.name.isNotNullOrBlank()){
            // 로직 
        }

    }
}
data class ExamUser(
    var name: String ?=null
)

// 코틀린의 확장 함수 -> extension function
fun String.isNotNullOrBlank(): Boolean{
    return !this.isNotNullOrBlank();
}



// 코틀린에서 모든 Object의 조상은 Any

fun Any?.isNotNull():Boolean{
    return this!=null;
}


// 중요한 점은 확장 함수 -> 자주 사용하는 함수들을 만들 수도 있고 이미 만들어진 것을 정의할 수 있음 

Java에서의 방어 코드

Java에서는 객체나 필드의 Null 여부를 체크하는 방어 코드를 작성해야 한다.

if (examUser!=null && examUser.getName() != null){
    if (!examUser.getName().isBlank()){
        // 로직 
    }
}

Util 함수를 이용한 방법

Java에서는 별도의 유틸리티 클래스를 만들어 이러한 로직을 재사용할 수 있게 한다.

public static boolean notBlank(String value){
    return !value.isBlank();
}

Kotlin에서의 확장 함수

하지만 코틀린에서는 확장 함수를 사용해 이러한 로직을 더 간결하고 직관적으로 만들 수 있다.

fun String.isNotNullOrBlank(): Boolean{
    return !this.isNotNullOrBlank();
}

Null 체크 간소화

fun Any?.isNotNull():Boolean{
    return this!=null;
}

확장 함수를 이용한 코드

이러한 확장 함수를 이용하면, 코드를 다음과 같이 간결하게 작성할 수 있다.

if (examUser.isNotNull() && examUser.name.isNotNullOrBlank()){
    // 로직 
}

장점

  • 기존 클래스를 수정하지 않고도 새로운 기능을 추가할 수 있다.
  • 코드가 간결해져 가독성이 향상된다.
  • 확장 함수를 재사용하여 코드 중복을 줄일 수 있다.



reference:

반응형