일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 안드로이드 유닛테스트란
- 안드로이드 유닛 테스트
- 2022 플러터 설치
- 플러터 설치 2022
- rxjava hot observable
- 객체
- 안드로이드 레트로핏 crud
- android ar 개발
- 서비스 vs 쓰레드
- 클래스
- 안드로이드 유닛 테스트 예시
- 안드로이드 os 구조
- 안드로이드 레트로핏 사용법
- rxjava disposable
- 스택 자바 코드
- 큐 자바 코드
- 안드로이드 라이선스 종류
- 2022 플러터 안드로이드 스튜디오
- 멤버변수
- 자바 다형성
- 서비스 쓰레드 차이
- 스택 큐 차이
- rxjava cold observable
- jvm이란
- jvm 작동 원리
- ANR이란
- android retrofit login
- 안드로이드 라이선스
- Rxjava Observable
- ar vr 차이
- Today
- Total
목록책/Effective Kotlin (53)
나만을 위한 블로그
[이펙티브 코틀린] 아이템 1. 가변성을 제한하라 [이펙티브 코틀린] 아이템 2. 변수의 스코프를 최소화하라 [이펙티브 코틀린] 아이템 3. 최대한 플랫폼 타입을 사용하지 마라 [이펙티브 코틀린] 아이템 4. inferred 타입으로 리턴하지 마라 [이펙티브 코틀린] 아이템 5. 예외를 활용해 코드에 제한을 걸어라 [이펙티브 코틀린] 아이템 6. 사용자 정의 오류보다는 표준 오류를 사용하라 [이펙티브 코틀린] 아이템 7. 결과 부족이 발생할 경우 null, Failure를 사용하라 [이펙티브 코틀린] 아이템 8. 적절하게 null을 처리하라 [이펙티브 코틀린] 아이템 9. use를 써서 리소스를 닫아라 [이펙티브 코틀린] 아이템 10. 단위 테스트를 만들어라 [이펙티브 코틀린] 아이템 11. 가독성을 목..
immutable 컬렉션보다 mutable 컬렉션이 좋은 점은 성능 면에서 더 빠르다는 것이다. immutable 컬렉션에 요소를 추가하려면 새 컬렉션을 만들면서 여기에 요소를 추가해야 한다. 컬렉션을 복제하는 처리는 비용이 굉장히 많이 드는 처리다. 그래서 이런 복제 처리를 하지 않는 mutable 컬렉션이 성능적 관점에서 좋다. 다만 immutable 컬렉션은 안전하다는 측면에서 좋다. 따라서 지역 변수로 사용할 때는 mutable 컬렉션을 사용하는 게 더 합리적이라고 할 수 있다. 그래서 표준 라이브러리도 내부적으로 어떤 처리를 할 때는 mutable 컬렉션을 쓰도록 구현돼 있다.
코틀린은 기본 자료형(primitive)을 선언할 수 없지만 최적화를 위해 내부적으론 쓸 수 있다. 기본 자료형은 아래 특징이 있다. 일반적인 객체와 달리 추가적으로 포함되는 것들이 없기 때문에 가볍다 값에 접근할 때 추가 비용이 들어가지 않아서 빠르다 따라서 대규모 데이터 처리 시 기본 자료형을 쓰면 상당히 큰 최적화가 이뤄진다. 그런데 코틀린의 List, Set 등의 컬렉션은 제네릭 타입이다. 제네릭 타입에는 기본 자료형을 쓸 수 없으므로 wrap된 타입을 써야 한다. 일반적인 경우에는 이렇게 하는 게 훨씬 처리가 쉬워져서 적합하다. 하지만 성능이 중요한 코드라면 IntArray, LongArray 등의 기본 자료형을 활용하는 배열을 쓰는 게 좋다. 이처럼 기본 자료형을 포함하는 배열은 코드 성능이 ..
모든 컬렉션 처리 메서드는 비용이 많이 든다. 표준 컬렉션 처리는 내부적으로 요소들을 활용해 반복을 돌며 내부 계산을 위해 추가 컬렉션을 만들어 사용한다. 시퀀스 처리도 시퀀스 전체를 wrap하는 객체가 만들어지며 조작을 위해 또 다른 추가 객체를 만들어낸다. 두 처리 모두 요소 수가 많다면 꽤 큰 비용이 들어간다. 따라서 적절한 메서드를 써서 컬렉션 처리 단계 수를 적절하게 제한하는 게 좋다. 컬렉션 처리와 관련해서 비효율적인 코드를 작성하는 이유는 필요없다고 생각해서가 아닌 어떤 메서드가 있는지 몰라서인 경우가 많다. 따라서 어떤 메서드가 있는지 확인해보는 게 좋다.
많은 사람이 Iterable, Sequence의 차이를 잊어버린다. 사실 정의가 거의 동일해서 충분히 이해할 수 있는 일이다. interface Iterable { operator fun iterator(): Iterator } interface Sequence { operator fun iterator(): Iterator } 둘은 완전히 다른 목적으로 설계되서 완전히 다른 형태로 동작한다. Sequence는 지연처리된다. 따라서 시퀀스 처리 함수들을 쓰면 데코레이터 패턴으로 꾸며진 새 시퀀스가 리턴된다. 최종 계산은 toList 또는 count 등의 최종 연산이 이뤄질 때 수행된다. 반면 Iterable은 처리 함수를 쓸 때마다 연산이 이뤄져 List가 만들어진다. 정리하면 컬렉션 처리 연산은 호출 ..
자바는 가비지 컬렉터가 객체 해제와 관련된 모든 작업을 해 준다. 그렇다고 메모리 관리를 완전히 무시하면 메모리 누수가 발생해서 상황에 따라 OOM이 발생하기도 한다. 따라서 더 이상 쓰지 않는 객체의 레퍼런스를 유지하면 안 된다는 규칙 정도는 지키는 게 좋다. 특히 어떤 객체가 메모리를 많이 차지하거나 객체가 많이 생성될 경우에는 규칙을 꼭 지켜야 한다. 안드로이드를 처음 시작하는 많은 개발자가 실수로 흔히 Activity를 여러 곳에서 자유롭게 접근하기 위해 companion 프로퍼티(고전적 형태론 static field)에 이를 할당해 두는 경우가 있다. class MainActivity : AppCompatActivity() { companion object { // 이렇게 하면 메모리 누수가 발..
하나의 값을 갖는 객체도 inline으로 만들 수 있다. 코틀린 1.3부터 도입된 기능으로, 기본 생성자 프로퍼티가 하나인 클래스 앞에 inline을 붙이면 해당 객체를 사용하는 위치가 모두 해당 프로퍼티로 교체된다. inline class Name(private val value: String) { // ... } 이런 인라인 클래스는 타입만 맞다면 값을 곧바로 집어넣는 것도 허용된다. fun main() { val name = Name("김철수") // 위 코드는 컴파일 시 아래와 같이 바뀐다 val name: String = "김철수" } 인라인 클래스의 메서드는 모두 정적 메서드로 만들어진다. fun main() { val name = Name("김철수") name.greet() // 안녕, 내 ..
코틀린 표준 라이브러리의 고차 함수를 보면 대부분 inline 한정자가 붙어 있다. inline 한정자의 역할은 컴파일 시점에 '함수를 호출하는 부분'을 '함수의 본문'으로 대체하는 것이다. inline 한정자를 붙여 함수를 만들면 큰 변화가 일어난다. 일반적인 함수를 호출하면 함수 본문으로 점프하고 본문의 모든 문장을 호출한 뒤에 함수를 호출했던 위치로 다시 점프하는 과정을 거친다. 하지만 '함수를 호출하는 부분'을 '함수 본문'으로 대체하면 이런 점프가 일어나지 않는다. inline 한정자를 쓰면 아래의 장점이 있다. 타입 아규먼트에 reified 한정자를 붙여 사용할 수 있다 함수 타입 파라미터를 가진 함수가 훨씬 빠르게 동작한다 비지역(non-local) 리턴을 사용할 수 있다 단점도 존재한다. ..
객체 생성은 언제나 비용이 들어간다. 상황에 따라선 굉장히 큰 비용이 들어갈 수 있다. 따라서 불필요한 객체 생성은 피하는 게 최적화 관점에서 좋다. JVM에선 하나의 가상 머신에서 같은 문자열을 처리하는 코드가 여러 개 있다면 기존 문자열을 재사용한다. Integer, Long처럼 박스화한 기본 자료형도 작은 경우엔 재사용된다. 기본적으로 Int는 -128~127 범위를 캐시해 둔다. nullable 타입은 int 대신 Integer를 사용하게 강제된다. Int를 쓰면 일반적으로 기본 자료형 Int로 컴파일된다. 하지만 nullable하게 만들거나 타입 아규먼트로 쓸 경우 Integer로 컴파일된다. 기본 자료형은 null일 수 없고 타입 아규먼트로도 쓸 수 없기 때문이다. 객체 생성 비용은 항상 큰가..
어떤 클래스에 대한 확장 함수를 정의할 때 이를 멤버로 추가하는 건 옳지 않다. 확장 함수는 첫 번째 아규먼트로 리시버를 받는 단순한 일반 함수로 컴파일된다. 아래 함수는 그 아래 함수로 컴파일된다. fun String.isPhoneNumber(): Boolean = length == 7 && all { it.isDigit() } fun isPhoneNumber('$this': String): Boolean = '$this'.length == 7 && '$this'.all { it.isDigit() } 이렇게 단순하게 변환되기 때문에 확장 함수를 클래스 멤버로 정의할 수도 있고 인터페이스 안에 정의할 수도 있다. interface PhoneBook { fun String.isPhoneNumber(): B..