23.12.11

KSang·2023년 12월 11일
1

TIL

목록 보기
9/101

오늘은 구글에서 제시하는 표준 스타일 가이드를 공부했다

Google의 Android 코딩 표준

소스파일

모든 소스 파일은 UTF-8로 인코딩되어야 합니다.

UTF-8 -> 문자 인코딩하는 방식

이름지정

최상위 클래스가 하나 뿐인 경우 파일 이름에 대소문자를 구분하는 이름과 .kt 확장자

최상위 수준 선언이 여러 개 포함되어 있으면 파일의 콘텐츠를 설명하는 이름을 선택하고 PascalCase(파일 이름이 복수형인 경우 camelCase가 허용됨)를 적용하고 .kt 확장자를 추가

특수문자

공백문자

문자열과 문자 리터럴의 다른 공백 문자: ASCII 수평 공백 문자를 제외한 다른 공백 문자는 이스케이프 처리됩니다. 이는 예를 들어 줄 바꿈 문자, 탭 문자 등이 포함됩니다

특수 이스케이프 문자 시퀀스 처리: 특수 이스케이프 문자 시퀀스 (예: \b, \n, \r, \t, ', ", \, $) 이러한 특수 이스케이프 문자 시퀀스는 유니코드 이스케이프 문자 대신 해당 시퀀스로 대체됩니다. 예를 들어, \n은 줄 바꿈을 나타내며, \u000a 대신 사용됩니다

구조

.kt 파일은 다음과 같은 순서로 구성됩니다.

  • 저작권 및/또는 라이선스 헤더(선택사항)
  • 파일 수준 주석
  • Package 문
  • Import 문
  • 최상위 수준 선언

저작권/라이선스

파일에 저작권 또는 라이선스 헤더가 포함된 경우 맨 위에 여러 줄로 주석을 넣기

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

KDoc 스타일 또는 한 줄 주석을 사용하지 말기

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

사용하지 말기

파일 수준 주석

use-site target '파일'을 포함하는 주석은 헤더 주석과 패키지 선언 사이에 배치

Package 문

package 문은 열 제한을 받지 않으며 줄바꿈되지 않습니다.

Import 문

클래스, 함수 및 속성의 import 문은 단일 목록으로 그룹화되고 ASCII 정렬됩니다.

모든 유형의 와일드 카드 가져오기는 허용되지 않습니다.

와일드카드: 패키지 내의 모든 클래스, 함수 또는 속성을 가져오는 것

package 문과 마찬가지로 import 문은 열 제한을 받지 않으며 줄바꿈되지 않습니다.

최상위 수준 선언

.kt 파일은 최상위 수준에서 하나 이상의 유형, 함수, 속성 또는 유형 별칭을 선언할 수 있습니다

형식지정

중괄호

else 브랜치가 2개 이상이 아니고 한 줄에 들어가는 when 브랜치 및 if 표현식에는 중괄호X

if (string.isEmpty()) return

val result =
    if (string.isEmpty()) DEFAULT_VALUE else string

if, for, when 브랜치, dowhile 문과 표현식의 경우 본문이 비어 있거나 단일 구문만 포함하는 경우에도 중괄호가 필요합니다.

if (string.isEmpty())
    return  // WRONG!

if (string.isEmpty()) {
    return  // Okay
}

if (string.isEmpty()) return  // WRONG
else doLotsOfProcessingOn(string, otherParametersHere)

if (string.isEmpty()) {
    return  // Okay
} else {
    doLotsOfProcessingOn(string, otherParametersHere)
}

비어 있지 않은 블록

비어 있지 않은 블록과 블록 형식 구문에서는 중괄호가 K&R(Kernighan and Ritchie) 스타일('이집트 대괄호')을 따릅니다.

  • 여는 중괄호 앞에 줄바꿈이 없습니다.
  • 여는 중괄호 뒤에 줄바꿈이 있습니다.
  • 닫는 중괄호 앞에 줄바꿈이 있습니다.
  • 중괄호로 구문이 종료되거나 함수, 생성자 또는 named 클래스의 본문이 종료되는 경우에만 닫는 중괄호 뒤에 줄바꿈이 있습니다. 예를 들어 중괄호 뒤에 else 또는 쉼표가 온다면 중괄호 뒤에 줄바꿈이 없습니다
return Runnable {
    while (condition()) {
        foo()
    }
}

return object : MyClass() {
    override fun foo() {
        if (condition()) {
            try {
                something()
            } catch (e: ProblemException) {
                recover()
            }
        } else if (otherCondition()) {
            somethingElse()
        } else {
            lastThing()
        }
    }
}

빈 블록

빈 블록 또는 블록형식 구문은 K&R스타일

표현식

표현식으로 사용되는 if/else 조건문에서는 전체 표현식이 한 줄에 들어가는 경우에만 중괄호 생략 가능

val value = if (string.isEmpty()) 0 else 1  // Okay

들여쓰기

새 블록 또는 블록 형식 구문이 열릴 때마다 들여쓰기가 4칸씩 증가

한 줄에 한 구문

각 구문 다음에 줄바꿈이 옵니다. 세미콜론은 사용되지 않습니다.

줄바꿈

코드의 열 제한은 100자입니다. 아래 언급된 경우를 제외하고 아래 설명된 대로 이 제한을 초과하는 줄은 줄바꿈되어야 합니다

예외:

  • 열 제한을 준수할 수 없는 줄(예: KDoc의 긴 URL)
  • package 및 import 문
  • 잘라서 셸에 붙여넣을 수 있는 주석의 명령줄

줄바꿈 위치

  • 연산자 또는 중위 함수(at, to등) 이름에서 행을 나누면 연산자 또는 중위 함수 이름 다음에 줄바꿈이 발생
  • '연산자 형식' 기호에서 행을 나누면 기호 앞에서 줄바꿈이 발생
    • 점 구분자(., ?.)
    • 멤버 참조의 두 콜론(::)
  • 메서드 또는 생성자 이름은 뒤에 오는 열린 괄호(()에 연결된 상태로 유지됩니다.
  • 쉼표(,)는 앞에 오는 토큰에 연결된 상태로 유지됩니다.
  • 람다 화살표(->)는 앞에 오는 인수 목록에 연결된 상태로 유지됩니다.

함수

함수 서명이 한 줄에 들어가지 않으면 각 매개변수 선언을 한 줄에 하나씩 표시합니다. 이 형식으로 정의된 매개변수에서는 단일 들여쓰기(+4)를 사용해야 합니다. 닫는 괄호()) 및 반환 유형은 추가 들여쓰기 없이 한 줄에 하나씩 입력

fun <T> Iterable<T>.joinToString(
    separator: CharSequence = ", ",
    prefix: CharSequence = "",
    postfix: CharSequence = ""
): String {
    // …
}

표현식 함수

함수에 표현식이 하나만 포함되는 경우

override fun toString(): String {
    return "Hey"    
}

override fun toString(): String = "Hey"

속성

속성 이니셜라이저가 한 줄에 들어가지 않을 경우 등호(=) 뒤에서 줄바꿈하고 들여쓰기

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

get 또는 set 함수를 선언하는 속성은 일반 들여쓰기(+4)를 적용하여 한 줄에 하나씩 입력해야 합니다. 함수와 동일한 규칙을 사용하여 형식을 지정합니다

var directory: File? = null
    set(value) {
        // …
    }

공백

수직

빈 줄이 하나 표시됩니다.

  • 클래스의 연속하는 멤버 사이: 속성, 생성자, 함수, 중첩 클래스 등
class ExampleClass {
    // 첫 번째 멤버 
    val property1: Int = 10

    // 첫 번째 함수
    fun function1() {
        // 함수 내용
    }

    // 연속된 멤버 간의 빈 줄
    val property2: String = "Hello"

    // 연속된 멤버 간의 빈 줄
    fun function2() {
        // 함수 내용
    }

    // 속성 간의 빈 줄 (선택적 사용)
    val property3: Double = 3.14

    val property4: Boolean = true

    // 속성 간의 빈 줄 (선택적 사용)
    val property5: List<String> = listOf("apple", "banana")

    // 클래스의 마지막 멤버 뒤의 선택적 빈 줄
}

수평

// WRONG!
for(i in 0..1) {
}
// Okay
for (i in 0..1) {
}

// WRONG!
}else {
}
// Okay
} else {
}

// WRONG!
if (list.isEmpty()){
}
// Okay
if (list.isEmpty()) {
}

// WRONG!
val two = 1+1
// Okay
val two = 1 + 1

// WRONG!
ints.map { value->value.toString() }
// Okay
ints.map { value -> value.toString() }

예외

멤버 참조의 두 콜론(::)
// WRONG!
val toString = Any :: toString
// Okay
val toString = Any::toString

. 구분자
// WRONG
it . toString()
// Okay
it.toString()

범위 연산자(..)
// WRONG
for (i in 1 .. 4) {
  print(i)
}
// Okay
for (i in 1..4) {
  print(i)
}

기본 클래스 또는 인터페이스를 지정하기 위해 클래스 선언에 사용된 경우나 일반 제약조건의 where 절에 사용된 경우에만 콜론(:) 앞에 줄바꿈을 넣습니다.

// WRONG!
class Foo: Runnable
// Okay
class Foo : Runnable

// WRONG
fun <T: Comparable> max(a: T, b: T)
// Okay
fun <T : Comparable> max(a: T, b: T)

// WRONG
fun <T> max(a: T, b: T) where T: Comparable<T>
// Okay
fun <T> max(a: T, b: T) where T : Comparable<T>

쉼표(,) 또는 콜론(:) 뒤

// Okay
val oneAndTwo = listOf(1, 2)

// Okay
class Foo : Runnable

줄 끝 주석을 시작하는 이중 슬래시(//)

// Okay
var debugging = false // disabled by default

Enum 클래스
함수와 상수에 대한 문서가 없는 열거형은 필요에 따라 한 줄로 서식 지정할 수 있습니다

enum class Answer { YES, NO, MAYBE }

열거형의 상수가 별도의 줄에 배치되는 경우 상수가 본문을 정의하는 경우를 제외하고 빈 줄이 필요하지 않습니다.

enum class Answer {
    YES,
    NO,

    MAYBE {
        override fun toString() = """¯\_(ツ)_/¯"""
    }
} //본문을 정의하는 경우
enum class Answer { YES, NO, MAYBE } //아닌경우

주석

멤버 또는 유형 주석은 주석 처리된 구문 바로 앞에 별도의 줄로 입력됩니다.

@Retention(SOURCE)
@Target(FUNCTION, PROPERTY_SETTER, FIELD)
annotation class Global

인수가 없는 주석은 한 줄로 입력할 수 있습니다.

@JvmField @Volatile
var disposable: Disposable? = null

인수가 없는 주석이 하나만 있는 경우 선언과 동일한 줄에 입력할 수 있습니다.

@Volatile var disposable: Disposable? = null

@Test fun selectAll() {
    // …
}

@[...] 구문은 명시적 use-site target에만 사용할 수 있으며, 한 줄에 인수가 없는 두 개 이상의 주석을 결합하는 데만 사용할 수 있습니다.

use-site target: 애노테이션을 적용할 때 해당 애노테이션이 적용되는 대상을 지정하는 방법

@field:[JvmStatic Volatile]
var disposable: Disposable? = null

암시적 반환/속성 유형

표현식 함수 본문 또는 속성 이니셜라이저가 스칼라 값이거나 반환 유형을 본문에서 명확히 추론할 수 있는 경우 생략할 수 있습니다.

override fun toString(): String = "Hey"
// becomes
override fun toString() = "Hey"

private val ICON: Icon = IconLoader.getIcon("/icons/kotlin.png")
// becomes
private val ICON = IconLoader.getIcon("/icons/kotlin.png")

이름 지정

식별자에는 ASCII 문자와 숫자만 사용하며, 아래 언급된 소수의 경우에는 밑줄로 표시됩니다. 따라서 각 유효 식별자 이름은 정규 표현식 \w+과 일치합니다

예 name_, mName, s_name 및 kName와 같이 특수 접두사 또는 접미사는 지원 속성의 경우를 제외하고 사용되지 않습니다(지원 속성 참조).

패키지 이름

패키지 이름은 모두 소문자이며 연속 단어는 밑줄 없이 연결

유형 이름

클래스 이름은 PascalCase로 작성되며 일반적으로 명사 또는 명사구

함수 이름

함수 이름은 camelCase로 작성되며 일반적으로 동사 또는 동사구입니다. 예를 들면 sendMessage 또는 stop이 있습니다.

이름의 논리적 구성요소를 구분하기 위해 테스트 함수 이름에 밑줄을 표시할 수 있습니다

@Test fun pop_emptyStack() {
    // …
}

Unit을 반환하는 @Composable로 주석 처리된 함수는 PascalCased이며 명사처럼 이름이 지정되어 있습니다.

Unit : 값이 없음, 값을 반환하지않는 함수에 사용

함수 이름은 일부 플랫폼에서 지원되지 않으므로 공백을 포함하면 안 됩니다

상수 이름

상수 이름에는 UPPER_SNAKE_CASE(모두 대문자)를 사용하며 밑줄로 단어를 구분

상수는 맞춤 get 함수가 없는 val 속성

const val NUMBER = 5
val NAMES = listOf("Alice", "Bob")
val AGES = mapOf("Alice" to 35, "Bob" to 32)
val COMMA_JOINER = Joiner.on(',') // Joiner is immutable
val EMPTY_ARRAY = arrayOf()

이러한 이름은 일반적으로 명사 또는 명사구입니다.

상수 값은 object 내부 또는 최상위 선언으로만 정의할 수 있습니다. 상수의 요구사항을 충족하지만 class의 내부에 정의된 값은 상수가 아닌 이름을 사용해야 합니다.

스칼라 값인 상수는 const 변경자를 사용해야 합니다.

상수가 아닌 이름

상수가 아닌 이름은 camelCase로 작성됩니다. 이는 인스턴스 속성, 로컬 속성, 매개변수 이름에 적용됩니다.

val variable = "var"
val nonConstScalar = "non-const"
val mutableCollection: MutableSet = HashSet()
val mutableElements = listOf(mutableInstance)
val mutableValues = mapOf("Alice" to mutableInstance, "Bob" to mutableInstance2)
val logger = Logger.getLogger(MyClass::class.java.name)
val nonEmptyArray = arrayOf("these", "can", "change")

이러한 이름은 일반적으로 명사 또는 명사구

지원 속성

지원 속성이 필요한 경우 밑줄 처리된 접두사를 제외하고 이름이 실제 속성의 이름과 정확히 일치해야 합니다.

private var _table: Map? = null

val table: Map
    get() {
        if (_table == null) {
            _table = HashMap()
        }
        return _table ?: throw AssertionError()
    }

유형 변수 이름

각 유형 변수의 이름은 다음 두 스타일 중 하나로 지정됩니다.

  • 단일 대문자, 필요에 따라 단일 숫자가 뒤에 옴(예: E, T, X, T2)
  • 클래스에 사용된 형식으로 된 이름, 대문자 T가 뒤에 옴(예: RequestT, FooBarT)
public class Box<T> {
    private T value;

    public T getValue() {
        return value;
    }

public class RequestT<T> {
    private T requestData;

    public T getRequestData() {
        return requestData;
    }

카멜 표기법

  1. 구문을 일반 ASCII로 변환하고 모든 아포스트로피를 삭제합니다. 예를 들어 'Müller’s algorithm'이 'Muellers algorithm'으로 변환될 수 있습니다.

  2. 공백 및 나머지 구두점(일반적으로 하이픈)에서 분할하여 이 결과를 단어로 나눕니다. 권장: 단어에 일반적인 용도의 카멜 표기법이 이미 있는 경우 이 단어를 구성요소로 분할합니다. 예를 들어 'AdWords'는 'ad words'로 분할됩니다. 'iOS'와 같은 단어는 실제로 카멜 표기법이 아니며, 어떤 규칙도 따르고 있지 않으므로 이 권장 사항이 적용되지 않습니다.

  3. 모두 소문자(두문자어 포함)인 경우 다음 중 하나를 실행합니다.

    • 파스칼 표기법을 적용하려면 각 단어의 첫 글자를 대문자로 시작합니다.
    • 카멜 표기법을 적용하려면 첫 단어를 제외한 각 단어의 첫 글자를 대문자로 시작합니다.
  4. 마지막으로 모든 단어를 단일 식별자로 결합합니다.

원래 단어의 대소문자는 거의 완전히 무시됩니다.

도움말

형식지정

KDoc 블록의 기본 형식

/**
 * Multiple lines of KDoc text are written here,
 * wrapped normally…
 */
fun method(arg: String) {
    // …
}

/** An especially short bit of KDoc. */

기본 형식은 항상 허용됩니다. 주석 마커를 포함하여 전체 KDoc 블록이 한 줄에 들어가는 경우 단일 줄 형식이 대체될 수 있습니다. 이는 블록 태그가 없는 경우(예: @return)에만 적용됩니다.

0개의 댓글