관리 메뉴

너와 나의 스토리

Kotlin coding convention 정리 본문

Programming Language/Kotlin

Kotlin coding convention 정리

노는게제일좋아! 2022. 7. 1. 14:27
반응형

 

Kotlin 사이트에 나와있는 내용을 기반으로 번역 및 추가 내용을 정리하였습니다.

 

 

Class Layout

  • class의 내용은 다음 순서로 정의돼야 합니다.
    1. Property 선언과 initializer blocks
    2. Secondary constructors
    3. Method 선언
    4. Companion Object
class Layout() {
    // Property
    var name = "name"
    val age: Int

    // Initializer block
    init {
        age = 20
    }

    // Secondary constructors
    constructor(_name: String) : this() {
        name = _name
    }
    
    // Companion object
    companion object {
        private const val CONST_VAL = 5
    }
}
  • Note
    • Method 선언을 알파벳이나 가시성으로 정렬하지 말고, 확장 메서드와 일반 메서드를 구분하지 말라.
    • 대신 관련 있는 것들끼리 묶어서 클래스를 위에서부터 아래로 보면서 로직을 따라갈 수 있도록 하라.
    • 순서를 선택하고 고정하라
      • 모든 클래스들은 고수준부터 순서대로 정의되던가 반대 순서로 정의되던가 순서를 결정해서 이 순서를 항상 고수하라.
    • Nested classes는 그 클래스들을 사용하는 코드 다음에 위치시켜라.
      • 만약 그 클래스들이 외부에서 사용되고 클래스 내에서는 참조되지 않는다면, companion object 뒤인 클래스의 마지막에 위치시켜라.
    • Interface를 구현하는 경우, 인터페이스에 정의된 순서와 동일하게 멤버 함수를 구현하라.
      • 만약 필요한 private method가 있다면 해당 interface에 있는 메서드를 구현한 메서드 다음에 넣어라.
    • overloads 메서드들은 항상 묶어서 둬라.

 

 

Naming Rules

  • Package 이름
    • 항상 lowercase이고 underscores(_)는 사용 금지.
    • 여러 단어로 된 이름은 권장하지 않음. 
    • 하지만 꼭 여러 단어로 된 이름을 사용해야 한다면 camelcase로 정의하라. (ex: myProject)
  • Class나 objects 이름
    • 항상 uppercase로 시작하고 camelcase 사용 (ex: DeclarationProcessor)
  • Function 이름
    • properteis나 local variables는 lowercase로 시작해서 camelcase 사용하고, underscores 사용 금지.
    • 예외적으로 클래스의 인스턴스를 생성하는 팩토리 함수는 추상 리턴 타입과 같은 이름을 가질 수 있다.
// normal property and local variable
fun processDeclarations() { /*...*/ }
var declarationCount = 1


// Factory function
interface Foo { /*...*/ }

class FooImpl : Foo { /*...*/ }

fun Foo(): Foo { return FooImpl() }
  • Test methods 이름
    • backticks(`)로 묶어서 공백을 포함한 이름을 사용할 수 있다.
class MyTestCase {
     @Test fun `ensure everything works`() { /*...*/ }

     @Test fun ensureEverythingWorks_onAndroid() { /*...*/ }
}
  • Property 이름
    • const로 선언된 상수 이름 또는 Top-level/object에 선언된 val은 uppercase 사용하고 단어는 underscore로 구분한다.
    • Mutable 데이터를 가지는 top-level/object 프로퍼티의 이름은 camelcase로 이름 짓는다.
    • 싱글톤 객체에 대한 참조를 가지는 프로퍼티의 이름은 객체 선언과 동일한 이름을 사용할 수 있다.
// constants 
const val MAX_COUNT = 8

// top-level or object val
object {
     val USER_NAME_FIELD = "UserName"
}

// mutable data를 가지는 top-level or object property
val mutableCollection: MutableSet<String> = HashSet()

// singleton 객체 참조하는 property
val PersonComparator: Comparator<Person> = /*...*/
  • Enum 상수 이름
    • uppercase에 underscore로 단어 구분 가능. (ex: FLOOR_COLOR)
    • upper camel case도 가능 (ex: Color)
  • Backing property 이름
    • 개념적으로는 같은 값이지만 하나는 public API에서 사용하고 하나는 구현 세부 사항에서 사용하는 프로퍼티가 존재한다면, private 프로퍼티의 이름에 underscore을 붙인다.
class C {
    private val _elementList = mutableListOf<Element>()

    val elementList: List<Element>
         get() = _elementList
}
  • 좋은 이름 고르기
    • 클래스 이름: 명사, 명사절 
      • ex: Person, PersonReader
    • 메서드 이름: 동사, 동사절
      • ex: close, readPersons
      • 객체를 변경하거나 새 객체를 반환하는지 여부를 암시하는 이름을 선정해야 한다.
      • ex: sort는 collection을 제자리에 정렬. sorted는  collection의 정렬된 복사본을 반환.
      • Manager, Wrapper같이 의미 없는 단어는 피하라. 
      • acronym(단어의 머리글자로 만든 말 ex: IO)가 포함된 경우, 대문자를 사용한다. ex: IOStream
      • 첫 문자가 긴 경우 첫 글자만 대문자로 사용하라. ex: XmlFormatter

 

Class headers

  • primary constructor 파라미터가 한 개라면 한 줄에 작성한다.
class Person(id: Int, name: String)
  • 파라미터가 여러 개라면 각 줄에 나눠서 작성한다. 
  • 닫는 괄호는 새로운 줄에 작성한다.
  • 상속을 하는 경우, superclass 생성자나 인터페이스는 닫는 괄호와 같은 줄에 위치시킨다.
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() { /*...*/ }
}

 

Modifiers order

  • 여러 개의 modifier를 가질 경우 아래의 순서를 따른다. 
public / protected / private / internal
expect / actual
final / open / abstract / sealed / const
external
override
lateinit
tailrec
vararg
suspend
inner
enum / annotation / fun // as a modifier in `fun interface`
companion
inline / value
infix
operator
data

 

Annotations

  • 선언 전 줄에 어노테이션 위치시킨다.
@Target(AnnotationTarget.PROPERTY)
annotation class JsonExclude
  • 매개변수 없는 어노테이션의 경우 한 줄에 여러 개를 둘 수 있다.
@JsonExclude @JvmField
var x: String
  • 매개변수가 없고 하나뿐인 어노테이션은 선언부와 같은 줄에 둘 수 있다.
@Test fun foo() { /*...*/ }
  • 파일 어노테이션은 파일 설명 주석과 다음에 위치시킨다. 패키지로부터 한 줄 띄고 위에 선언한다.
/** License, copyright and whatever */
@file:JvmName("FooBar")

package foo.bar

 

Expression bodies

  • body가 한 줄에 들어가지 않으면 =를 첫 줄에 넣고 다음 줄에 expression body를 둔다.
fun f(x: String, y: String, z: String) =
    veryLongFunctionCallWithManyWords(andLongParametersToo(), x, y, z)

 

Properties

  • 간단한 read-only 프로퍼티는 한 줄에 쓰는 것을 고려하라.
val isEmpty: Boolean get() = size == 0
  • 복작합 프로퍼티의 경우 get이나 set keyword를 다음 줄에 넣어라.
val foo: String
    get() { /*...*/ }

 

Control flow statements

  • if나 when의 조건문이 여러 줄이면  &&또는 ||를 작성하고 다음 줄에 조건을 작성한다.
if (!component.isSyncing &&
    !hasAnyKotlinRuntimeInScope(module)
) {
    return createKotlinNotConfiguredPanel(module)
}

 

 

 

출처

- https://kotlinlang.org/docs/coding-conventions.html#source-file-names

반응형
Comments