티스토리 뷰

1. Source code organization (소스 코드 구성)

1.1 Directory structure(디렉토리 구조)

  • 혼합 언어 프로젝트에서의 Kotlin 소스 파일은 Java 소스 파일과 동일한 소스 루트에 있어야 하며 동일한 디렉토리 구조 를 따라야 한다. (각 파일은 각 패키지 명령문에 해당하는 디렉토리에 저장되어야 합니다.)
  • 순수한 Kotlin 프로젝트에서 권장되는 디렉토리 구조는 공통 루트 패키지가 생략 된 패키지 구조를 따랴아 한다.
    • 공통 패키지 명을 뺀 나머지 패키지 구조를 따른다.
    • 공통 패키지 : com.example.kotlin = root 디렉토리
    • 서브 패키지 : com.example.kotlin.foo.bar = foo/bar 디렉토리
  • 모든 소스 파일은 UTF-8로 인코딩 되어야 합니다.

1.2 Source file names(소스 파일 이름)

  • Kotlin 파일에 하나의 클래스(관련 최상위 수준의 선언이 포함 될 수 있음) 가 포함 된 경우 해당 파일의 이름은 클래스 이름과 동일해야 하며 .kt 확장자가 추가되어야 합니다.

  • Kotlin 파일에 여러 클래스 또는 최상위 레벨 선언만 포함되어 있으면 파일에 포함 된 것을 설명하는 이름으로 파일 이름을 지정해야 합나다.

// MyClass.kt  
class MyClass { }

// Bar.kt
class Bar { }
fun Runnable.toBar(): Bar = // …

// Map.kt
fun <T, O> Set<T>.map(func: (T) -> O): List<O> = // …
fun <T, O> List<T>.map(func: (T) -> O): List<O> = // …
  • 카멜 표기법 사용 (CamelCase)
    • 예시 파일명 : ProcessDeclarations.kt
  • 파일 이름은 코드가 하는 것들을 설명해야 합니다.. 따라서 “Util”과 같은 의미 없는 단어를 사용하지 말라고 권장합니다.

1.3 Source file organization(소스 파일 구성)

  • 연관되어 진 여러 선언(클래스, 최상위 함수 또는 속성)을 동일한 Kotlin 소스 파일에 포함하나, 파일 크기가 너무 크지 않고 수백 줄을 초과하지 않아야 한다
  • 클래스에 모든 클라이언트와 관련된 클래스의 확장 함수를 넣을 때 클래스 자체에 정의 된 동일한 파일에 이 함수를 넣는 것이 좋습니다.
  • 특정 클라이언트에 대해서만 의미가 있는 확장 기능을 정의할 때는 해당 클라이언트 코드 옆에 확장 기능을 지정합니다.

1.4 Class layout(클래스 레이아웃)

  • 일반적으로 클래스의 내용은 다음 순서로 정렬합니다.
  1. 속성 선언 및 Initializer 블록
  2. 보조 생성자
  3. 메소드 선언
  4. Companion Object(동반자 객체) : Kotlin에서는 정적 선언을 할 수 없지만, 클래스를 초기화하지않고 내부의 코드에 접근해야 한다거나, 팩토리 메소드를 구현해야 할 경우 Companion Object를 사용하여 구현할 수 있습니다.
  • 메소드 선언은 사전 순 또는 가시성에 따라 정렬하지 말고, 일반 메소드와 확장 메소드를 분리하지 않습니다.
  • 연관된 메소드끼리 함께 위치하도록 구성해야 합니다. 다른 사람이 봤을 때 로직을 이해하기 쉽도록 배치합니다.
  • 중첩 클래스는 해당 클래스를 사용하는 코드 다음에 배치 합니다.
  • 중첩 클래스가 외부에서만 사용하도록 의도되어 있고, 클래스 내부에서 참조되지 않은 경우 Companion Object(동반자 객체) 뒤의 끝에 넣습니다.

1.5 Interface implementation layout(인터페이스 구현 레이아웃)

  • 인터페이스를 구현할 할 때 구현 멤버들의 순서를 인터페이스 멤버와 동일한 순서로 유지합니다.
  • 필요한 경우, 구현에 사용되는 privated method 들을 배치합니다.

1.6 Overload layout

항상 각 클래스 다음에 넣습니다.

2. Naming rules (이름 지정 규칙)

  • Java naming convention 을 따름

  • Package

    • 항상 소문자로 권장 (com.example.project)
    • 일반적으로 권장하지는 않지만, 여러 단어를 사용해야 할 경우 간단히 병합하거나 카멜 표기법 사용(com.example.myProject)
  • Class, Object : 카멜 표기법 사용(첫 문자a는 대문자)

    object EmptyDeclarationProcessor : DeclarationProcessor() { ... }

2.1 Function names (함수 이름)

  • 함수, 속성 및 지역 변수의 이름은 소문자로 시작하여 카멜 표기법을 사용하며, 밑줄은 사용하지 않습니다.

    var declarationCount = ...
  • 클래스의 인스턴스를 만드는데 사용되는 팩토리 함수는 생성되는 클래스 이름과 동일할 수도 있습니다.

    class FooImpl : Foo { ... }  
    fun Foo(): Foo { return FooImpl(...) }

2.2 Names for test methods (테스트 메소드의 이름)

  • 테스트 코드의 경우 메소드에 backtick(`), space, underscore(_)를 사용할 수 있습니다.

    class MyTestCase {
    
      @Test fun `ensure everything works`() {
      }
    
      @Test fun ensureEverythingWorks_onAndroid() {
      }
    
    }

2.3 Property names (속성 이름)

  • 상수 : 대문자와 _(underscore) 사용 가능

    const val MAX\_COUNT = 8  
    val USER\_NAME\_FIELD = "UserName"
  • 변경 가능한 데이터를 가진 객체 : 카멜 표기법

    val mutableCollection: MutableSet = HashSet()
  • 싱글톤 객체 : 객체 선언과 동일하게 사용

    val PersonComparator: Comparator = …
  • enum 상수

    enum class Color { RED, GREEN }

2.4 Names for backing properties

  • 클래스가 개념상으로 동일하지만 하나는 public API의 일부이고, 다른 하나는 구현 세부 정보가 있는 private property 이름은 접두사로 _(underscore)를 사용합니다.

  • private 속성을 반환하는 public 속성을 사용하는 경우, private 속성 앞에 _(underscore)를 붙여 사용합니다.

    class C {
    
        private val _elementList = mutableListOf<Element>()
    
        val elementList: List<Element>
            get() = _elementList
    
    }

2.5 Choosing good names (좋은 이름 선택)

  • 클래스
    • 무엇을 하는 클래스인지 의미를 표현
    • 명사 또는 명사구의 이름을 사용(List, PersonReader)
  • 메소드
    • 어떤 변화 또는 객체의 반환 작업을 하는 메소드인지 표현
    • 동사 또는 동사구의 이름을 사용(close, readPersons)
  • 선언 이름의 일부가 약어인 경우
    • 약어가 두 문자인 경우는 대문자로 표기(IOStream)
    • 더 긴 경우는 첫번째 문자만 대문자로 사용(XmlFomatter, HttpInputStream)

3. Formatting (서식 지정)

  • 대부분의 경우 Java code convention 을 따름

  • 4 spaces 사용, 탭은 사용하지 않습니다.

  • 중괄호의 경우 구성 시작부분의 끝줄에 여는 중괄호를 입력하고, 열린 구성과 수직적으로 정렬된 별도의 줄을 닫는 중괄호를 입력한다.

  • 세미콜론은 선택사항이지만 생략하는 것을 권장

    if (elements != null) {
    
        for (element in elements) {
            // ...
        } 
    
    }

3.1 Horizontal whitespace (가로 공백)

  • 연산자 앞, 뒤에 공백을 추가합니다.

    • a + b, a / b
    • 예외
      • “range to” 연산 주위에도 공백을 넣지 말아야 합니다. (0..I)
      • 단항 연산 주위에도 공백을 넣지 말아야 합니다. (a++)
  • 중괄호 앞에 공백을 추가 합니다.

    • if, when, for and while
  • 괄호 앞에는 공백을 추가하지 않습니다.

    class A(val x: Int)
    
    fun foo(x: Int) { }
    
    fun bar() {
        foo(1)
    }
  • (, [ 뒤에 또는 ), ] 전에 공백을 추가하지 않습니다.

  • . 또는 ?. 주위에 공백을 추가하지 않습니다.

    foo.bar().filter { it > 2 }.joinToString()  
    foo?.bar()
  • 주석 // 뒤에 공백을 추가 합니다.

    // 주석을 추가합니다.

3.2 Colon

  • 타입과 슈퍼 타입을 분리할 때 공백을 추가합니다.

  • 슈퍼 클래스 생성자나 같은 클래스의 다른 생성자에 위임할 때 공백을 추가 합니다.

  • object 키워드 다음에 공백을 추가 합니다.

  • 항상 : 뒤에 공백을 추가합니다.

  • 속성과 해당 타입을 분리할 때는 앞에 공백을 추가하지 않습니다.

    abstract class Foo : IFoo {
        abstract fun foo(a: Int): T
    }
    
    class FooImpl : Foo() {
        constructor(x: String) : this(x) {
            // ...
        }
    
        val x = object : IFoo { ... } 
    }

3.3 Class header formatting

  • 기본 생성자의 매개 변수가 몇 개 있는 클래스는 한 줄로 작성할 수 있습니다.

    class Person(id: Int, name: String)
  • 헤더가 긴 클래스는 각 기본 생성자 매개 변수가 들여쓰기가 있는 별도의 줄에 있도록 서식을 지정해야 합니다.

  • 또한 닫는 괄호가 새 줄에 있어야 합니다.

  • 상속을 사용한다면 슈퍼 클래스의 생성자 호출이나 구현 된 인터페이스의 목록은 괄호와 같은 줄에 있어야 합니다.

    class Person(
        id: Int,
        name: String,
        surname: String
    ) : Human(id, name) {
        // ...
    }
  • 다중 인터이스의 경우 슈퍼 클래스 생성자의 호출이 먼저 위치해야 하며 각 인터페이스는 다른 행에 있어야 합니다.

    class Person(
        id: Int,
        name: String,
        surname: String
    ) : Human(id, name),
        KotlinMaker {
        // ...
    }
  • 긴 상위 유형의 목록이 있는 클래스의 경우 콜론 다음에 줄 바꿈을 넣고 모든 상위 유형의 이름을 세로로 맞춥니다.

    class MyFavouriteVeryLongClassHolder :
        MyLongHolder<MyFavouriteVeryLongClass>(),
        SomeOtherInterface,
        AndAnotherOne {
        fun foo() {}
    }
  • 클래스 헤더가 길 때 클래스 헤더와 본문을 명확하게 구분하려면 클래스 헤더 다음에 빈 줄을 넣거나 여는 중괄호를 별도의 줄에 넣습니다.

    class MyFavouriteVeryLongClassHolder :
        MyLongHolder<MyFavouriteVeryLongClass>(),
        SomeOtherInterface,
        AndAnotherOne
        {
            fun foo() {}
        }
  • 생성자의 매게 변수에는 일반 들여 쓰기(4 공백)을 사용합니다.
    • 이유 : 기본 생성자에서 선언 된 속성이 클래스 본문에 선언 된 속성 만큼 들여 쓰기가 되도록 해야 합니다.

3.4 Modifiers(수정자)

  • 선언에 여러 수정자가 있는 경우 항상 다음 순서로 입력해야 합니다.

    public / protected / private / internal  
    expect / actual  
    final / open / abstract / sealed / const  
    external  
    override  
    lateinit  
    tailrec  
    vararg  
    suspend  
    inner  
    enum / annotation  
    companion  
    inline  
    infix  
    operator  
    data
  • 모든 특수 효과를 수정자 앞에 배치하도록 합니다.

    @Named("Foo")  
    private val foo: Foo
  • 라이브러리에서 작업하지 않는 한 중복 수정자 (예: public) 를 생략합니다.

3.5 Annotation formatting

  • 어노테이션은 일반적으로 별개의 줄에 붙습니다. 단, 어노테이션이 첨부 된 문장과 같은 들여쓰기가 있어야 합니다.

    @Target(AnnotationTarget.PROPERTY)  
    annotation class JsonExclude
  • 인수가 없는 어노테이션은 같은 줄에 배치할 수 있습니다.

    @JsonExclude @JvmField  
    var x: String
  • 인수가 없는 단일 어노테이션은 해당 선언과 동일한 줄에 배치할 수 있습니다.

    @Test fun foo() { ... }

3.6 File annotation

  • 파일 어노테이션은 package 명령문 앞에 배치되며 빈 줄이 있는 package 와 구분합니다. (package가 아닌 파일을 대상으로 한다는 사실을 강조하기 위함)

    /\*\* License, copyright and whatever \*/  
    @file:JvmName("FooBar")
    
    package foo.bar

3.7 Function formatting

  • 함수 시그니쳐가 한 줄에 들어가지 않으면 다음 구문을 사용하도록 합니다.

    fun longMethodName(  
        argument: ArgumentType = defaultValue,  
        argument2: AnotherArgumentType  
    ): ReturnType {  
        // body  
    }
  • 함수 매개 변수에 일반 들여 쓰기(4 공백)을 사용하도록 합니다.

    • 생성자와 매게 변수와의 일관성을 두기 위함
  • 하나의 표현식으로 구성된 본문이 있는 함수에 표현식 본문을 사용하는 것이 좋습니다.

    fun foo(): Int { // bad case
        return 1 
    }
    
    fun foo() = 1 // good case

3.8 Expression body formatting

  • 함수에 선언된 동일한 행에 맞지 않는 표현식 본문이 있다면 첫 행에 = 부호를 붙입니다.

  • 표현 본문을 4칸만큼 들여 씁니다.

    fun f(x: String) =
        x.length

3.9 Property formatting

  • 매우 간단한 읽기 전용 속성의 경우 한 줄 형식을 고려하도록 합니다.

    val isEmpty: Boolean get() = size == 0
  • 복잡한 속성의 경우 항상 get 및 set 키워드를 별도의 줄에 넣습니다.

    val foo: String
    get() {
        // ...
    }
  • initializer가 있는 속성의 경우 initializer가 길면 등호 다음에 줄 바꿈을 추가하고 initializer를 4칸만큼 들여 씁니다.

    private val defaultCharset: Charset? =  
        EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)

3.10 Formatting control flow statements

  • if 또는 when문의 조건이 여러 행인 경우 항상 명령문 본문 주위에 중괄호를 사용합니다.

  • 명령문 begin에 상대적인 4칸 공백으로 조건의 각 후속줄을 들여쓰기 합니다.

  • 조건의 닫는 괄호와 여는 중괄호를 별도의 줄에 넣습니다.

    if (!component.isSyncing &&
        !hasAnyKotlinRuntimeInScope(module)
    ) {
        return createKotlinNotConfiguredPanel(module)
    }
  • 이전의 중괄호와 같은 줄에 else, catch, finally 키워드와 do/while 루프의 while 키워드를 넣습니다.

    if (condition) {
        // body
    } else {
        // else part
    }
    
    try {
        // body
    } finally {
        // cleanup
    }
  • when 문에서 분기가 단일 행 이상인 경우 case 블록과 빈 행을 구분하도록 합니다.

    private fun parsePropertyValue(propName: String, token: Token) {
        when (token) {
            is Token.ValueToken ->
                callback.visitValue(propName, token.value)
    
            Token.LBRACE -> { // ...
            }
        }
    }
  • 중괄호 없이 상태와 같은 줄에 짧은 가지를 넣도록 합니다.

    when (foo) {
        true -> bar() // good case
        false -> { baz() } // bad case
    }

3.11 Method call formatting

  • 긴 인수 목록에서는 여는 괄호 다음에 줄 바꿈을 넣도록 합니다.

  • 인수는 4칸 들여씁니다.

  • 같은 줄에 여러 가지 밀접하게 관련된 인수를 그룹화 하도록 합니다.

  • 인수 이름과 값을 구분하는 = 기호 주위에 공백을 넣도록 합니다.

    drawSquare(  
        x = 10, y = 10,  
        width = 100, height = 100,  
        fill = true  
    )

3.12 Chained call wrapping

  • 별도로 묶여있는 변수를 선언할 때는 문자 또는 ?. 연산자 다음 행에 하나의 들여쓰기를 하도록 합니다.

  • 체인의 첫 번째 호출은 일반적으로 앞에 줄 바꿈이 있어야 하지만 코드를 생략하도 무방합니다.

    val anchor = owner
        ?.firstChild!!
        .siblings(forward = true)
        .dropWhile { it is PsiComment || it is PsiWhiteSpace }

3.13 Lambda formatting

  • 람다식에서 매게 변수를 본문에 분리하는 화살표 주위와 중괄호 주위에 공백을 사용합니다.

  • 호출이 단일 람다로 사용되면 가능할 때 마다 괄호 밖으로 전달이 되어야 합니다.

    list.filter { it > 10 }
  • 람다 레이블을 할당하는 경우 레이블과 여는 중괄호 사이에 공백을 두지 않습니다.

    fun foo() {
        ints.forEach lit@{
            // ...
        }
    }
  • 여러 줄에 람다에서 매게 변수 이름을 선언할 때는 첫 번째 줄에 이름을 넣고 그 다음에 화살표와 줄 바꿈을 넣습니다.

    appendCommaSeparated(properties) { prop ->
            val propertyValue = prop.get(obj) // ...
    }
  • 매게 변수 목록이 너무 길어 한 줄에 들어갈 수 없는 경우 화살표를 별도의 줄에 입력하도록 합니다.

    foo {
        context: Context,
        environment: Env
        ->
        context.configureEnv(environment)
    }

4. Documentation comments (문서 주석)

  • 더 긴 문서 주석을 보려면 별도의 행에 /** 를 입력하고 각 후속 행을 시작하도록 합니다.

    /**
    * This is a documentation comment
    * on multiple lines.
    */
  • 짧은 주석은 한 줄에 입력할 수 있습니다.

    /\*\* This is a short documentation comment. \*/
  • 일반적으로 @param, @return 태그를 사용하지 않도록 합니다.

  • 대신 매개 변수 설명 및 반환 값을 설명서 주석에 직접 통합하고 매개 변수에 대한 링크를 언급한 곳마다 추가하도록 합니다.

  • 주 텍스트의 흐름이 맞지 않는 긴 설명이 필요한 경우에만 @param, @return을 사용하도록 합니다.

    // Avoid doing this:
    
    /**
     * Returns the absolute value of the given number.
     * @param number The number to return the absolute value for.
     * @return The absolute value.
     */
    fun abs(number: Int) = ...
    
    // Do this instead:
    
    /**
     * Returns the absolute value of the given [number].
     */
    fun abs(number: Int) = ...
  • Copyright / License 주석

     / * 
     * Copyright 2017 Google, Inc. 
     * 
     * 
     * * /

5. Avoiding redundant constructs (중복 구성 피하기)

  • 일반적으로 Kotlin의 특정 구문 구조가 선택사항이며 IDE에서 중복으로 강조 표시된 경우 코드에서 생략해야 합니다.
  • 명확성을 위해 코드에 불필요한 구문 요소를 남겨두지 않습니다.

5.1 Unit

  • 함수가 Unit을 반환하면 반환 유형을 생략해야 합니다.

    fun foo() { // ": Unit" is omitted here

    }

5.2 Semicolons

  • 가능한 경우 세미콜론을 생략하도록 합니다.

5.3 String templates

  • 문자열 템플릿에 간단한 변수를 삽입할 때는 중괄호를 사용하지 않습니다.

  • 더 긴 표현식에 대해서만 중괄호를 사용합니다.

    println("$name has ${children.size} children")

6. Idiomatic use of language features (언어 기능의 관용적 사용)

6.1 Immutability(불변성)

  • 변경할 수 없는 데이터를 가변적으로 사용하는 것이 바람직합니다.

  • 초기화 후 로컬 변수와 속성이 수정되지 않으면 항상 var 대신 val을 사용하도록 합니다.

  • 변경되지 않는 컬렉션을 선언하려면 항상 불변 컬렉션 인터페이스를 사용합니다. (Collection, List, Set, Map)

  • 팩토리 함수를 사용하여 컬렉션 인스턴스를 만들 때는 가능한 불변 컬렉션 유형을 반환하는 함수를 사용합니다.

    // Bad: use of mutable collection type for value which will not be mutated  
    fun validateValue(actualValue: String, allowedValues: HashSet) { ... }
    
    // Good: immutable collection type used instead  
    fun validateValue(actualValue: String, allowedValues: Set) { ... }
    
    // Bad: arrayListOf() returns ArrayList, which is a mutable collection type  
    val allowedValues = arrayListOf("a", "b", "c")
    
    // Good: listOf() returns List  
    val allowedValues = listOf("a", "b", "c")

6.2 Default parameter values(기본 매개 변수 값)

  • Overload 된 함수를 선언할 때는 기본 매개 변수 값을 사용하여 함수를 선언하는 것이 좋습니다.

    // Bad case  
    fun foo() = foo("a")  
    fun foo(a: String) { ... }
    
    // Good case  
    fun foo(a: String = "a") { ... }

6.3 Type aliases(별칭 유형)

  • 코드베이스에서 여러 번 사용되는 유형의 매개 변수가 있다면 typealias를 사용하는 것이 좋습니다.

    typealias MouseClickHandler = (Any, MouseEvent) -> Unit  
    typealias PersonIndex = Map<String, Person>

6.4 Lambda parameters

  • 짧고 중첩되지 않은 람다에서는 매게 변수를 명시적으로 선언하는 대신 it 규칙을 사용하는 것이 좋습니다.
  • 매개 변수가 있는 중첩 된 람다에서 매개 변수는 항상 명시적으로 선언되어야 합니다.

6.5 Returns in a lambda

  • 람다에서 라벨이 지정된 여러 개의 리턴을 사용하지 않도록 합니다.
  • 람다를 재구성하여 단일 리턴을 갖도록 해야 합니다.
  • 단일 리턴이 불가능한 경우 익명 함수로 변환하는 것을 고려하도록 합니다.
  • 람다에서 마지막 문장에 레이블이 지정된 리턴 값을 사용하지 않도록 합니다.

6.6 Named arguments

  • 모든 매개 변수의 의미가 문맥에서 절대적으로 명확하지 않다면 메소드가 동일한 기본 유형의 여러 매개 변수

  • 또는 Boolean 유형의 매개 변수를 사용하는 경우 Named arguments로 개수와 상관없이 원하는 위치에 값을 대입할 수 있습니다.

    drawSquare(x = 10, y = 10, width = 100, height = 100, fill = true)

6.7 Using conditional statements

  • if, if 및 when의 조건문을 사용하는 것이 좋습니다.

    return if (x) foo() else bar()
    
    return when(x) {
        0 -> "zero"
        else -> "nonzero"
    }
  • 위의 내용은 다음과 같은 경우에 적합합니다.

    if (x)
        return foo()
    
    else
        return bar()
    
    when(x) {
        0 -> return "zero"
        else -> return "nonzero"
    }

6.8 if or when

  • binary conditions의 경우 when 대신에 if를 사용하는것이 좋습니다.

    when (x) {  
        null -> ...  
        else -> ...  
    }
    
    if (x == null) ... else ...
  • 세 가지 이상의 옵션이 있는 경우는 when의 사용을 선호합니다.

6.9 Using nullable Boolean values in conditions (조건에 nullable Boolean 값 사용)

  • 조건문에 Nullable Boolean를 사용해야 하는 경우라면 if (value == true) 또는 if (value == false)를 사용합니다.

6.10 Using loops(루프 사용하기)

  • higher-order 함수를 루프에 사용하는 것이 좋습니다.(filter, map 등)
    예외 : forEach(forEach 의 수신자가 nullable 이거나 더 긴 체인 호출의 일부로 사용되지 않는 한 일반적으로 for loop를 대신 사용하는 것을 선호합니다.)

6.11 Loops on ranges(범위의 루프)

  • until 함수를 사용해서 열린 범위를 반복하도록 합니다.

    for (i in 0..n - 1) { ... } // bad case  
    for (i in 0 until n) { ... } // good case

6.12 Using strings

  • 문자열 템플릿을 사용하는 것이 좋습니다.

  • \n escape sequences 을 사용하는 대신에 다중 행을 사용하는 것을 권장합니다.

  • 여러 줄의 문자열에서 들여 쓰기를 유지하려면 trimMargin, trimIndent 를 사용합니다.

    assertEquals("Foo Bar".trimIndent(), value)
    
    val a = "if(a > 1) {
        | return a
        |}".trimMargin()

6.13 Functions vs Properties

  • 인수가 없는 함수는 읽기 전용 속성과 호환될 수 있습니다.

  • 의미는 비슷하지만 서로를 선호할 때는 규칙이 있습니다.

  • 기본 알고리즘이 다음과 같은 경우 함수에 대한 속성을 선호합니다

  • throw 하지 않습니다.

  • 0(1) 복잡도

  • 쉬운 계산 또는 첫 번째 실행에서 캐싱

  • 객체 상태가 변경되지 않은 경우 호출에 대한 동일한 결과를 반환합니다.

6.14 Using extension functions (확장 기능 사용)

  • 확장 기능을 자유롭게 사용합니다.
  • 주로 객체에 대해 작동하는 함수가 있을 때 마다 객체를 receiver로 받아들이는 확장 함수를 고려해야 합니다.
  • API 오염을 최소화 하기 위해 확장 함수의 가시성을 가능한 많이 제한해야 합니다.
  • 필요한 경우 로컬 확장 함수, 멤버 확장 함수 또는 비공개 가시성이 있는 최상위 확장 함수를 사용하도록 합니다.

6.15 Using infix functions (중위 함수 사용)

  • 비슷한 역활을 하는 두 개의 객체에서 작동 할 때만 infix function을 선언합니다.
    • 좋은 예 : and, zip
    • 나쁜 예 : add
  • 메소드가 receiver object를 변경하면 삽입을 선언하면 안됩니다.

6.16 Factory functions

  • 클래스에 대한 팩토리 함수를 선언하는 경우 클래스 자체와 동일한 이름을 지정하면 안됩니다.

  • 고유한 이름을 사용하여 팩토리 함수의 이유를 명확히 해야 합니다.

    class Point(val x: Double, val y: Double) {
        companion object {
            fun fromPolar(angle: Double, radius: Double) = Point(...)
        }
    }
  • 다른 슈퍼 클래스 생성자를 호출하지 않고 기본 인수값을 사용하여 단일 생성자로 축소할 수 없는 여러 Overload 된 생성자가 있는 경우 Overload 된 생성자를 팩토리 함수로 대체하는 것이 좋습니다.

6.17 Platform types

  • 플랫폼 유형의 식을 리턴하는 경우 공용 함수/메소드는 Kotlin 유형을 명시적으로 선언해야 합니다.

    fun apiCall(): String = MyJavaApi.getProperty("name")
  • 플랫폼 유형의 표현식으로 초기화 된 모든 패키지(패키지 또는 클래스 수준)는 명시적으로 Kotlin 유형을 선언해야 합니다.

    class Person {
        val name: String = MyJavaApi.getProperty("name")
    }
  • 플랫폼 형식으로 초기화 된 값은 형태 선언을 가질 수도 있고 가지지 않을 수도 있습니다.

    fun main(args: Array) {
        val name = MyJavaApi.getProperty("name")
        println(name)
    }

6.18 Using scope functions apply/with/run/also/let (범위 함수 사용)

  • Kotlin은 주어진 객체의 Context에서 코드 블록을 실행하는 다양한 함수를 제공합니다.

  • 블록의 여러 객체에서 메소드를 호출하거나 Context의 인스턴스를 인수로 전달하고 있는 경우 this 사용

  • receiver가 블록에서 전혀 사용되지 않는 경우는 also 사용

    // Context object is 'it'  
    class Baz {
        var currentBar: Bar?
        val observable: Observable
        val foo = createBar().also {
            currentBar = it // Accessing property of Baz
            observable.registerCallback(it) // Passing context object as argument
        }
    }
    
    
```
// Receiver not used in the block  
val foo = createBar().also {
    LOG.info("Bar created")
}

// Context object is 'this'  
class Baz {
    val foo: Bar = createBar().apply {
        color = RED // Accessing only properties of Bar
        text = "Foo"
    }
}

```

  • 실행의 결과가 Context Object 일 필요가 있는 경우 apply 사용

  • 블록에서 값을 반환해야 할 경우 with, let 또는 run 사용

    // Return value is context object  
    class Baz {
        val foo: Bar = createBar().apply {
            color = RED // Accessing only properties of Bar
            text = "Foo"
        }
    }
    
    // Return value is block result  
    class Baz {
        val foo: Bar = createNetworkConnection().let {
            loadBar()
        }
    }
  • Context Object가 Nullable 일 경우 apply 또는 run 사용

  • 체인의 호출 결과로 정해지는 경우 with 또는 also 사용

    // Context object is nullable  
    person.email?.let { sendEmail(it) }
    
    // Context object is non-null and accessible directly  
    with(person) {
        println("First name: $firstName, last name: $lastName")
    }

7. Coding conventions for libraries (라이브러리의 코딩 규칙)

  • 멤버의 가시성을 항상 명시적으로 지정해야 합니다. (선언을 실수로 공개 API에 노출시키지 않도록 합니다.)
  • 항상 함수 반환 형식 및 속성 형식을 명시적으로 지정합니다. (구현이 변경될 때 실수로 반환 형식을 변경하지 않도록 합니다.)
  • 새로운 문서를 필요로 하지 않는 재정의를 제외하고는 모든 공용 멤버에 대한 KDoc 주석을 제공합니다. (라이브러리에 대한 문서 생성 지원을 위함)

참고

https://kotlinlang.org/docs/reference/coding-conventions.html
https://developer.android.com/kotlin/style-guide
http://open2jang.tistory.com/30
http://theeye.pe.kr/archives/2619

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
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 29 30
글 보관함