오늘은 구글에서 제시하는 표준 스타일 가이드를 공부했다
Google의 Android 코딩 표준
모든 소스 파일은 UTF-8
로 인코딩되어야 합니다.
UTF-8 -> 문자 인코딩하는 방식
최상위 클래스가 하나 뿐인 경우 파일 이름에 대소문자를 구분하는 이름과 .kt
확장자
최상위 수준 선언이 여러 개 포함되어 있으면 파일의 콘텐츠를 설명하는 이름을 선택하고 PascalCase(파일 이름이 복수형인 경우 camelCase가 허용됨)를 적용하고 .kt
확장자를 추가
문자열과 문자 리터럴의 다른 공백 문자: ASCII 수평 공백 문자를 제외한 다른 공백 문자는 이스케이프 처리됩니다. 이는 예를 들어 줄 바꿈 문자, 탭 문자 등이 포함됩니다
특수 이스케이프 문자 시퀀스 처리: 특수 이스케이프 문자 시퀀스 (예: \b, \n, \r, \t, ', ", \, $) 이러한 특수 이스케이프 문자 시퀀스는 유니코드 이스케이프 문자 대신 해당 시퀀스로 대체됩니다. 예를 들어, \n은 줄 바꿈을 나타내며, \u000a 대신 사용됩니다
.kt 파일은 다음과 같은 순서로 구성됩니다.
파일에 저작권 또는 라이선스 헤더가 포함된 경우 맨 위에 여러 줄로 주석을 넣기
/*
* Copyright 2017 Google, Inc.
*
* ...
*/
KDoc 스타일 또는 한 줄 주석을 사용하지 말기
/** <--
* Copyright 2017 Google, Inc.
*
* ...
*/
// Copyright 2017 Google, Inc.
//
// ...
사용하지 말기
use-site target '파일'을 포함하는 주석은 헤더 주석과 패키지 선언 사이에 배치
package 문은 열 제한을 받지 않으며 줄바꿈되지 않습니다.
클래스, 함수 및 속성의 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
, whe
n 브랜치, do
및 while
문과 표현식의 경우 본문이 비어 있거나 단일 구문만 포함하는 경우에도 중괄호가 필요합니다.
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) 스타일('이집트 대괄호')을 따릅니다.
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자입니다. 아래 언급된 경우를 제외하고 아래 설명된 대로 이 제한을 초과하는 줄은 줄바꿈되어야 합니다
예외:
함수 서명이 한 줄에 들어가지 않으면 각 매개변수 선언을 한 줄에 하나씩 표시합니다. 이 형식으로 정의된 매개변수에서는 단일 들여쓰기(+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()
}
각 유형 변수의 이름은 다음 두 스타일 중 하나로 지정됩니다.
public class Box<T> {
private T value;
public T getValue() {
return value;
}
public class RequestT<T> {
private T requestData;
public T getRequestData() {
return requestData;
}
구문을 일반 ASCII로 변환하고 모든 아포스트로피를 삭제합니다. 예를 들어 'Müller’s algorithm'이 'Muellers algorithm'으로 변환될 수 있습니다.
공백 및 나머지 구두점(일반적으로 하이픈)에서 분할하여 이 결과를 단어로 나눕니다. 권장: 단어에 일반적인 용도의 카멜 표기법이 이미 있는 경우 이 단어를 구성요소로 분할합니다. 예를 들어 'AdWords'는 'ad words'로 분할됩니다. 'iOS'와 같은 단어는 실제로 카멜 표기법이 아니며, 어떤 규칙도 따르고 있지 않으므로 이 권장 사항이 적용되지 않습니다.
모두 소문자(두문자어 포함)인 경우 다음 중 하나를 실행합니다.
마지막으로 모든 단어를 단일 식별자로 결합합니다.
원래 단어의 대소문자는 거의 완전히 무시됩니다.
KDoc 블록의 기본 형식
/**
* Multiple lines of KDoc text are written here,
* wrapped normally…
*/
fun method(arg: String) {
// …
}
/** An especially short bit of KDoc. */
기본 형식은 항상 허용됩니다. 주석 마커를 포함하여 전체 KDoc 블록이 한 줄에 들어가는 경우 단일 줄 형식이 대체될 수 있습니다. 이는 블록 태그가 없는 경우(예: @return
)에만 적용됩니다.