Kotlin Code Convention(~Formatting)

rivermoon·2024년 11월 12일
post-thumbnail

디렉터리 구조

파일명(파스칼 케이스)

class layout의 구조 순서

[property, secondary constructor, method, companion object]
함수의 순서는 위에서 아래로 읽었을 때 논리의 흐름을 따라갈 수 있는가

impl layout

인터페이스 멤버와 동일한 순서 적용


naming rules

  • 패키지는 항상 소문자, 패키지안에 여러 단어를 이어야 할 경우 Camel case
  • 클래스와 객체의 이름은 Pascal Case
  • 함수, 속성, 지역변수는 Camel Case
    -> 예외적으로 클래스 인스턴스를 생성하는 팩토리 함수는 Pascal Case다.
  • 테스트에서는 백틱으로 묶인 공백과 함께 메서드 명을 정의할 수 있다.(API 30이상)
  • 상수(const, val 속성 객체, 최상위 객체?(사용자 정의 get함수가 없는))의 이름은 Screaming Snake Case
const val MAX_COUNT = 8
val USER_NAME_FIELD = "UserName"

동작 또는 변경 가능한 데이터가 있는 개체를 보유하는 최상위 수준 또는 개체 속성의 이름은 Camel Case

val mutableCollection: MutableSet<String> = HashSet()

싱글톤 객체에 대한 참조를 보유하는 속성의 이름은 객체 선언과 동일한 명명 스타일을 사용할 수 있다.

val PersonComparator: Comparator<Person> = /*...*/

enum class의 속성은 Screaming Snake CaseUpper Camel Case

Choose good names

  • 클래스 명은 명사 또는 명사구
  • 메서드명은 메서드가 수행하는 작업을 나타내는 동사 혹은 동사구.
    또한 이름은 메소드가 객체를 변경하는지 아니면 새 객체를 반환하는지를 암시해야 한다.
  • 예를 들어 sort는 컬렉션을 제자리에 정렬하는 반면 sorted는 컬렉션의 정렬된 복사본을 반환.
  • 이름은 엔터티의 목적이 무엇인지 명확하게 나타내야 하므로 이름에 의미 없는 단어(Manager, Wrapper)를 사용하지 않는 것이 가장 좋다.
  • 선언 이름의 일부로 약어를 사용할 때 두 글자로 구성된 경우(IOStream) 이를 대문자로 사용해라. 더 긴 경우 첫 글자만 대문자로 표시(XmlFormatter, HttpInputStream).

Formatting

  • 들여쓰기는 공백 4개를 사용한다. 탭을 사용하지마라.
  • 중괄호의 경우 여는 중괄호를 구문이 시작되는 줄 끝에 배치하고 닫는 중괄호를 여는 구문과 수평으로 정렬된 별도의 줄에 배치한다.
if (elements != null) {
    for (element in elements) {
        // ...
    }
}

Horizontal Whitespace(가로 공백)

  • 이항 연산자(a + b) 주위에 공백을 넣는다.
    ->예외: 범위 연산자(0..i) 주위에 공백을 두지 마라.
  • 단항 연산자(a++) 주위에 공백을 넣지 마라.
  • 제어 흐름 키워드(if, when, for 및 while)와 해당 여는 괄호 사이에 공백을 넣는다.
  • 기본 생성자 선언, 메서드 선언 또는 메서드 호출에서 여는 괄호 앞에 공백을 넣지 마라.
class A(val x: Int)

fun foo(x: Int) { ... }

fun bar() {
    foo(1)
}
  • (, [, or ], ) 뒤에 공백을 넣지 마라.
  • . or ?.: foo.bar().filter { it > 2 }.joinToString(), foo?.bar(). 주위에 공백을 두지마라?
  • // 뒤에 공백을 둬라(주석)
  • 유형 매개변수를 지정하는 데 사용되는 꺾쇠 괄호 주위에 공백을 넣지 마라(class Map<K, V> { ... }).
  • ::: Foo::class, String::length 주위에 공백을 넣지 마라.
    ? 앞에 공백을 넣지 마라. Null 허용 유형을 표시하는 데 사용된다: String?.

Colon

다음 시나리오에서는 : 앞에 공백을 넣어라.

  • 유형과 상위 유형을 구분하는 데 사용되는 경우.
  • 슈퍼클래스 생성자 또는 동일한 클래스의 다른 생성자에게 위임하는 경우.
  • object 키워드 뒤에.
  • 선언과 해당 유형을 구분할 때 : 앞에 공백을 넣지 마라.
  • : 뒤에는 항상 공백을 넣으세요.
abstract class Foo<out T : Any> : IFoo {
    abstract fun foo(a: Int): T
}

class FooImpl : Foo() {
    constructor(x: String) : this(x) { /*...*/ }

    val x = object : IFoo { /*...*/ }
}

Class headers

  • 몇 가지 기본 생성자 매개변수가 있는 클래스는 한 줄로 작성.
class Person(id: Int, name: String)
  • 더 긴 헤더가 있는 클래스는 각 기본 생성자 매개변수가 들여쓰기가 있는 별도의 줄에 있도록 형식을 지정해야 한다.
  • 또한 닫는 괄호는 새 줄에 있어야 한다.
  • 상속을 사용하는 경우 슈퍼클래스 생성자 호출 또는 구현된 인터페이스 목록은 괄호와 같은 줄에 있어야 한다.
class Person(
    id: Int,
    name: String,
    surname: String
) : Human(id, name) { /*...*/ }
  • 긴 상위 유형 목록이 있는 클래스의 경우 콜론 뒤에 줄 바꿈을 넣고 모든 상위 유형 이름을 가로로 정렬한다.
class MyFavouriteVeryLongClassHolder :
    MyLongHolder<MyFavouriteVeryLongClass>(),
    SomeOtherInterface,
    AndAnotherOne {

    fun foo() { /*...*/ }
}
  • 클래스 헤더가 긴 경우 클래스 헤더와 본문을 명확하게 구분하려면 위의 예와 같이 클래스 헤더 뒤에 빈 줄을 넣거나 여는 중괄호를 별도의 줄에 넣는다.
class MyFavouriteVeryLongClassHolder :
    MyLongHolder<MyFavouriteVeryLongClass>(),
    SomeOtherInterface,
    AndAnotherOne
{
    fun foo() { /*...*/ }
}

Functions

  • 함수 서명이 한 줄에 맞지 않으면 다음 구문을 사용해라.
fun longMethodName(
    argument: ArgumentType = defaultValue,
    argument2: AnotherArgumentType,
): ReturnType {
    // body
}
  • 단일 표현식으로 구성된 본문이 있는 함수에는 표현식 본문을 사용하는 것이 좋다.
fun foo(): Int {     // bad
    return 1
}

fun foo() = 1        // good

Expression bodies

  • 함수에 첫 번째 줄이 선언과 같은 줄에 맞지 않는 식 본문이 있는 경우 첫 번째 줄에 = 기호를 넣고 식 본문을 4개의 공백으로 들여쓴다.
fun f(x: String, y: String, z: String) =
    veryLongFunctionCallWithManyWords(andLongParametersToo(), x, y, z)

Properties

  • 매우 간단한 읽기 전용 속성의 경우 한 줄 형식을 고려해라.
val isEmpty: Boolean get() = size == 0
  • 더 복잡한 속성의 경우 항상 get 및 set 키워드를 별도의 줄에 입력해라.
val foo: String
    get() { /*...*/ }

이니셜라이저가 있는 속성의 경우 이니셜라이저가 길면 = 기호 뒤에 줄바꿈을 추가하고 이니셜라이저를 4개의 공백으로 들여쓴다.

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

Control flow statements

  • when 문에서 분기가 한 줄 이상인 경우 빈 줄을 사용하여 인접한 케이스 블록과 분리하는 것을 고려해라.
private fun parsePropertyValue(propName: String, token: Token) {
    when (token) {
        is Token.ValueToken ->
            callback.visitValue(propName, token.value)

        Token.LBRACE -> { // ...
        }
    }
}
  • 중괄호 없이 조건과 같은 줄에 짧은 분기를 배치한다.
when (foo) {
    true -> bar() // good
    false -> { baz() } // bad
}

Method calls

  • 긴 인수 목록에서는 여는 괄호 뒤에 줄 바꿈을 넣는다.
  • 인수를 공백 4개로 들여쓴다.
  • 밀접하게 관련된 여러 인수를 같은 줄에 그룹화한다.
  • 인수 이름과 값을 구분하는 = 기호 주위에 공백을 넣어라.
drawSquare(
    x = 10, y = 10,
    width = 100, height = 100,
    fill = true
)

Lambdas

  • 람다 식에서는 중괄호 주위와 매개변수를 본문에서 구분하는 화살표 주위에 공백을 사용해야 한다.
  • 호출이 단일 람다를 사용하는 경우 가능할 때마다 괄호 바깥으로 전달해라.
list.filter { it > 10 }
  • 람다에 대한 레이블을 할당하는 경우 레이블과 여는 중괄호 사이에 공백을 넣지 마라.
fun foo() {
    ints.forEach lit@{
        // ...
    }
}
  • 여러 줄의 람다에서 매개변수 이름을 선언할 때 첫 번째 줄에 이름을 입력하고 그 뒤에 화살표와 줄 바꿈을 입력한다.
appendCommaSeparated(properties) { prop ->
    val propertyValue = prop.get(obj)  // ...
}

매개변수 목록이 너무 길어서 한 줄에 다 들어갈 수 없다면 화살표를 별도의 줄에 넣어라.

Trailing commas

후행 쉼표다.

class Person(
    val firstName: String,
    val lastName: String,
    val age: Int, // trailing comma
)

후행 쉼표 사용의 여러 가지 이점.

  • 모든 초점이 변경된 값에 맞춰지므로 버전 제어 차이점이 더욱 깔끔해짐.

  • 요소를 쉽게 추가하고 재정렬할 수 있다.
    요소를 조작하는 경우 쉼표를 추가하거나 삭제할 필요가 없다.

  • 개체 이니셜라이저의 코드 생성을 단순화한다. 마지막 요소에는 쉼표가 있을 수도 있습니다.

후행 쉼표는 전적으로 선택 사항이다.
코드는 쉼표 없이도 계속 작동한다.
Kotlin 스타일 가이드는 선언 사이트에서 후행 쉼표 사용을 권장한다.


References

https://kotlinlang.org/docs/coding-conventions.html

profile
Android Developer

0개의 댓글