본문 바로가기

Language & Framework/Java

Kotlin Scope Function 복습

 

Kotlin에는 다양한 Scope Function들이 존재한다.

 

근데 솔직히 난 let 말고는 거의 사용해본 적이 없음..

공부했었는데 이제 뭔지 기억도 안남..

run은 let이랑 비슷한 것 같은데 가독성만 떨어지고 왜 있는지 잘 모르겠음..

특히 with는 가독성 개판의 주범 같음.. 그래도 뭔가 있었던 것 같음..

 

그래서 간만에 복습하기로 했다.

 

Scope functions don't introduce any new technical capabilities, but they can make your code more concise and readable.

 

Scope Function은 대단히 특별한 기능을 제공하는 것은 아니고, 우리 코드를 간결하고 읽기 쉽게 만들어주는 것이 존재 이유다. 가독성을 위해 태어난 친구를 본래 목적에 맞게 사용하기 위해 복습해보는 시간~~

 

대단한 내용은 없으니 님들도 그냥 공식 문서 읽으셈.. 여긴 그냥 제 일기장임..

이 글도 별다른 결론 없이 끝남..

 

https://kotlinlang.org/docs/scope-functions.html#function-selection

 

Scope functions | Kotlin

 

kotlinlang.org

 

 

우리의 친절한 이웃 Kotlin Document에 간략한 사용 가이드가 있다.

함수명 객체 참조 반환값 확장함수 여부(Boolean) 사용하는 경우
let it 람다 반환값 true - non-null 객체에 대한 람다 실행 시
- local scope에서 표현식을 변수로 도입해야할 때
run this 람다 반환값 true - 객체 설정 및 결과 연산 (apply와 유사하나 최종 표현식의 결과를 반환)
- local scope에서 표현식을 gruping하고 결과를 반환
run - 람다 반환값 false - 반환 값 없이 코드 블록을 실행하고 싶을 때
with this 람다 반환값 false - 주어진 객체에 대한 여러 함수 호출을 grouping하고 마지막 표현식 결과를 반환
apply this 원본 객체 yes - 객체 설정 (값 세팅에 대한 말인 듯.)
also it 원본 객체 yes - 추가 효과를 수행하며 원래 객체를 반환하고 싶을 때

 

 

5가지 Scope 함수의 가장 큰 차이점은 두가지다.

1. 객체를 참조할 때 사용하는 키워드

2. 반환값이 원본 객체인지, 람다 반환값인지

 

우선 keyword가 this와 it이 있는데 무슨 차이가 있는지 궁금할 것이다.

this는 수신 객체를 의미하며 객체 내부에서 사용할 때와 마찬가지로 생략될 수 있다.

 

 

음.. 이렇게 생략될 수 있긴 한데, 생략하는 게 좋은 선택일까?

 

In most cases, you can omit this when accessing the members of the receiver object, making the code shorter. On the other hand, if this is omitted, it can be hard to distinguish between the receiver members and external objects or functions. So having the context object as a receiver (this) is recommended for lambdas that mainly operate on the object's members by calling its functions or assigning values to properties.

 

this를 생략하면 수신 객체의 멤버와 외부 객체나 함수를 구별하기 어려울 수 있다고 한다.

그러면 뭐다? 그냥 무조건 쓰는 게 좋다.

 

이럴 때는 이렇게 쓰고~ 저럴 때는 저렇게 쓰고~

이런 규칙은 나 혼자 개발할 때나 적용 가능함.. 그냥 늘 쓰셈..

 

반면 it 키워드의 경우 완전히 생략될 수 없으며, 이 "it"이라는 것은 우리가 java에서도 늘 보던 Lambda의 인자와 같다.

Lambda의 인자명을 명시적으로 정하게 될 경우 기존 자바의 .map() .forEach()에서 사용하던 것과 동일하게 사용할 수 있고, 이를 생략하고 싶다면 it을 사용하면 된다.

 

 

 

나의 경우에는 Scope Function이 연달아 체이닝되지 않고, 한 번에 모든 연산이 끝나서 결과를 반환하는 경우 it 키워드를 사용한다.

처음에는 뭣도 모르고 run과 let, it과 this를 남발했는데. 그러면 아래와 같은 코드가 된다.

 

똥냄새 나는 코드

 

일부러 기존 코드를 최대한 이해하기 어렵고 구린 상태로 개조해봤다.내가 작성하고 일주일 뒤에 다시 보면 이해하기 어려운 코드는 남이 봤을 때는 똥냄새나는 코드가 된다.

 

되도록 인자명을 명확하게 사용하도록 하자.난 run도 별로 안 좋아함.. 이건 알아서 하셈..

 

반환 타입에 대해서는 별도의 설명은 생략한다.바로 위위에 있는 예제에서 isAdultUser()를 어떻게 사용하고 있는지 보면 알 것.

 

 

 

그리고 이제 각 Scope Function에 대해 자세히 작성해야할 타이밍인데, 문서랑 다른 블로그 글들을 아무리 읽어봐도 정말 도저히 let 외에 다른 스코프 함수를 사용해야할 이유를 잘 모르겠다.. 그나마 also 정도?

 

명시적인 걸 좋아하다보니 this 키워드를 사용해야하는 run, with, apply는 모두 꺼려지고 also는 테스트 코드 작성할 때 몇 번 사용해본 적은 있는데 서비스 코드에서는 아직 써본 적이 없다..let님이 너무 전지전능하셔서..

 

그냥 남들 코드 읽을 때 이해할 수 있는 정도로만 숙지하고 넘어감..님들도 문서 보고 이해하셈..

 

그나마 내가 좋아하는 takeIf와 takeUnless에 대해서만 올리고 마무리해야겠음.

 

 

 

takeIf는 주어진 predicate가 참일 경우 해당 객체를 반환하고, takeUnless는 반대로 동작한다.

 

 

 

위의 takeIf문은 아래와 같이 대체될 수 있는데, 일반적으로 체이닝이 길어질 때 간결하게 코드를 작성할 수 있어서 주로 사용하게 된다.

근데 주의할 점이 있다.

 

 

언뜻 보면 아무 문제 없어보이는 코드이지만, doSomething()은 늘 호출되는 불상사가 일어난다.

만약 doSomething이 user의 메서드라면 takeIf()에서 T를 null로 반환하여 해당 메서드가 실행되지 않겠지만, 외부 함수 실행 시에는 takeIf가 null이건 말건 어차피 apply 블럭이 실행되기 때문이다.

 

위 코드가 올바르게 동작하게 하려면 takeIf { it.isAdultUser() } 뒤에 물음표(?.)를 붙여줘야 한다.

 

오늘도 그럼 20000