Effective kotlin을 읽고
코틀린 철학: 생산성, 확장성, 유지보수 성, 신뢰성, 효율성
코틀린만의 장점(타언어 대비 특징)
- Item 5. 예외를 활용해 코드에 제한을 걸어라
- Item 17. Named Argument를 사용하라
- Item 47. 인라인 클래스의 사용을 고려하라
- Item 49. 하나 이상의 처리 단계를 가진 경우에는 시퀀스를 사용하라
자바
- Item 40. equals의 규약을 지켜라
- Item 41. hashCode의 규약을 지켜라
- Item 42. compareTo의 규약을 지켜라
1장 안정성
안정성 : 크래시가 적으면 사용자와 개발자 모두에게 좋고, 상당한 비즈니스 가치를 제공한다.
Item 1. 가변성을 제한하라
시간의 변화에 따라서 변하는 요소를 표현할 수 있다는 것은 유용하지만, 상태를 적절하게 관리하는 것이 생각보다 꽤 어렵다.
프로그램을 이해하고 디버그하기 힘들어진다.
시점에 따라 값이 달라질 수 있기 때문에 코드의 실행을 추론하기 어려워진다.
멀티스레드 프로그램일 때는 적절한 동기화가 필요하다.
테스트하기 어렵다.
상태 변경이 일어날 때 이러한 변경을 다른 부분에 알려야 하는 경우가 있다.
가변성은 시스템의 상태를 나타내기 위한 주요한 방법이다. 하지만, 변경이 일어나야 하는 부분을 신중하고 확실하게 결정하고 사용해야 한다.
val은 읽기 전용 프로퍼티이지만, 변경할 수 없음을 의미하는 것은 아니다. (custom getter or Delegate)
코틀린이 내부적으로 immutable 하지 않은 컬렉션을 외부적으로 immutable 하게 만들어서 얻어지는 안정성이다. 그런데 개발자가 ‘시스템 해킹'을 시도해서 다운캐스팅을 할 때 문제가 된다.
mutable 컬렉션을 사용하는 것이 처음에는 더 간단하게 느껴지겠지만, mutable 프로퍼티를 사용하면 객체 변경을 제어하기가 더 쉽다.
mutable 객체를 외부로 노출하지 않는 방법
- 방어적 복사
- 읽기 전용 슈퍼타입으로 업캐스트
Item 2. 변수의 스코프를 최소화하라
코드를 분석할 때는 어떤 시점에 어떤 요소가 있는지를 알아야 한다.
이때 요소가 많아져서 프로그램에 변경될 수 있는 부분이 많아지면, 프로그램을 이해하기가 어려워진다.
변수는 읽기 전용 또는 읽고 쓰기 전용 여부와 상관 없이, 변수를 정의할 때 초기화되는 것이 좋다.
Item 3. 최대한 플랫폼 타입을 사용하지 말라
플랫폼 타입이란 다른 언어에서 오는 타입
Item 4. Inferred 타입으로 리턴하지 말라
할당 때 inferred 타입은 정확하게 오른쪽에 있는 피연산자에 맞게 설정된다는 것을 기억해야 한다. 절대로 슈퍼클래스 또는 인터페이스로는 설정되지 않는다.
안전을 위해서 외부 API를 만들 때는 반드시 타입을 지정하고, 이렇게 지정한 타입을 특별한 이유와 확실한 확인 없이는 제거하지 말아야 한다.
Item 5. 예외를 활용해 코드에 제한을 걸어라
require, check, assert, return과 throw를 활용한 Elvis 연산자
Item 6. 사용자 정의 오류보다는 표준 오류를 사용하라
Item 7. 결과 부족이 발생할 경우 null과 Failure를 사용하라
충분히 예측할 수 있는 범위의 오류는 null과 Failure를 사용하고,
예측하기 어려운 예외적인 범위의 오류는 예외를 throw해서 처리하는 것이 좋다.
Item 8. 적절하게 null을 처리하라
변수를 일단 선언하고, 이후에 사용하기 전에 값을 할당해서 사용하기로 한다면
이후 프로퍼티를 계속 unpack해야 하므로 사용하기 귀찮고
해당 프로퍼티가 실제로 이후에 의미 있는 null 값을 가질 가능성 자체를 차단해버린다
→ 때문에 이런 경우네는 lateinit 또는 Delegates.notNull을 사용해야 한다.
!! 연산자가 의미 있는 경우는 굉장히 드물다.
일반적으로 nullability가 제대로 표현되지 않는 라이브러리를 사용할 때 정도에만 사용해야 한다. 코틀린을 대상으로 설계된 API를 활용한다면, !! 연산자를 사용하는 것을 이상하게 생각해야 한다.
!! 연산자를 보면 반드시 조심하고, 무언가가 잘못되어 있을 가능성을 생각하자.
lateinit 한정자는 프로퍼티가 이후에 설정될 것임을 명시하는 한정자이다.
lateinit을 사용할 경우에도 비용이 발생한다.
만약, 초기화 전에 값을 사용하려고 하면 예외가 발생한다.
처음 사용하기 전에는 반드시 초기화가 되어 있을 경우에만 lateinit을 붙이는 것이다. 만약 그런 값이 사용되어 예외가 발생한다면, 그 사실을 알아야 하므로 예외가 발생하는 것은 오히려 좋 은 일이다.
lateinit을 사용할 수 없는 경우도 있다.
JVM에서 기본타입과 연결된 타입으로 프로퍼티를 초기화해야 하는 경우이다.
Item 9. use를 사용하여 리소스를 닫아라
InputStream / OutputStream
java.sql.Connection
java.io.Reader
java.new.Socket / java.uti.Scanner
모든 리소스는 최종적으로 리소스에 대한 레퍼런스가 없어질 때, 가비지 컬렉터가 처리한다.
하지만 굉장히 느리며 (쉽게 처리되지 않는다) 그동안 리소스를 유지하는 비용이 많이 들어간다.
따라서 더 이상 필요하지 않다면, 명시적으로 close 메소드를 호출해 주는 것이 좋다.
Item 10. 단위 테스트를 작성하라
2장 가독성
Item 11. 가독성을 목표로 설계하라
항상 가독성을 생각하며 코드를 작성해야 한다. 가독성은 사람에 따라 다르게 느낄 수 있다.
숙련된 개발자만을 위한 코드는 좋은 코드가 아니다. 구현 A와 구현 B는 사실 비교조차 할 수 없을 정도로 A가 훨씬 가독성이 좋은 코드이다. 사용 빈도가 적은 관용구는 코드를 복잡하게 만든다. 그리고 그런 관용구들을 한 문장 내부에 조합해서 사용하면 복잡성은 훨씬 빠르게 증가한다. 구현 A는 수정하기 쉽고, 디버깅도 더 간단하다. 익숙하지 않은 구조를 사용하면, 이처럼 잘못된 동작을 코드를 보면서 확인하기 어렵다. 이런 이야기를 let은 절대로 쓰면 안된다로 이해하는 사람들이 꽤 많다. 극단적이면 안된다.
어떤 것이 비용을 지불할 만한 코드인지 아니지는 항상 논란이 있을 수 있다. 균형을 맞추는 것이 중요하다. 일단 어떤 구조들이 어떤 복잡성을 가져오는지 등을 파악하는 것이 좋다. 또한 두 구조를 조합해서 사용하면, 단순하게 개별적인 복잡성의 합보다 훨씬 커진다.
Item 12. 연산자 오버로드를 할 때는 의미에 맞게 사용하라
DSL을 설계할 때는 제외
Item 13. Unit?을 리턴하지 말라
Item 14. 변수 타입이 명확하지 않은 경우 확실하게 지정하라
val data = getSomeData() 위의 코드는 타입을 숨기고 있다. 가독성을 위해 코드를 설계할 때 읽는 사람에게 중요한 정보를 숨겨서는 안 된다. ”코드를 읽으면서 함수 정의를 보며 타입을 확인하면 되지 않나?” 라고 생각할 수도 있지만 이는 곧 가독성이 떨어진다는 의미이다.
Item 15. 리시버를 명시적으로 참조하라
무언가를 더 자세하게 설명하기 위해서, 명시적으로 긴 코드를 사용할 때가 있다. (this 혹은 라벨)
리시버가 명확하지 않다면, 명시적으로 리시버를 적어서 이를 명확하게 해 주어야 한다. 레이블 없이 리시버를 사용하면, 가장 가까운 리시버를 의미한다.
Item 16. 프로퍼티는 동작이 아니라 상태를 나타내야 한다.
파생 프로퍼티 : var을 사용해서 만든 읽고 쓸 수 있는 프로퍼티
프로퍼티를 함수 대신 사용할 수도 있지만, 그렇다고 완전히 대체해서 사용하는 것은 좋지 않다.
원칙적으로 프로퍼티는 상태를 나타내거나 결정하기 위한 목적으로만 사용하는 것이 좋고, 다른 로직 등을 포함하지 않아야 한다.
연산 비용이 높거나, 복잡도가 O(1)보다 큰 경우 : 관습적으로 프로퍼티를 사용할 때 연산 비용이 많이 필요하다고 생각하지 않는다. 비즈니스 로직(애플리케이션의 동작)을 포함하는 경우 : 프로퍼티가 로깅, 리스너 통지, 바인드된 요소 변경과 같은 단순한 동작 이상을 할 거라고 기대하지 않는다. 결정적이지 않은 함수 : 실행마다 다른 값이 나올 때 변환의 경우 : Int.toDouble() 처럼 타입 변환이 이루어지는 경우 getter에서 프로퍼티의 상태 변경이 일어나야 하는 경우
Item 17. Named Argument를 사용하라
Item 18. 코딩 컨벤션을 지켜라
3장 재사용성
Item 19. knowledge를 반복하여 사용하지 말라
두 코드가 함께 knowledge를 나타내는지, 다른 knowledge를 나타내는지는 ‘함께 변경될 가능성이 높은가? 따로 변경될 가능성이 높은가?’라는 질문으로 어느 정도 결정할 수 있다. (단일 책임 원칙)
많은 개발자는 Don't Repeat Yourself 라는 문장을 엄격하게 지키려고 비슷해 보이는 코드는 모두 추출하려는 경향이 있습니다. 극단적인 것은 언제나 좋지 않습니다. 항상 균형이 중요합니다. 어떤 것을 추출해야 할지 결정하기 어려울 수 있습니다.