org.example.kotlin
패키지와 해당 하위 패키지에 있는 경우org.example.kotlin
패키지에 있는 파일은 소스 루트(Source Root) 바로 아래에 위치해야 한다.org.example.kotlin.network.socket
에 있는 파일은 소스 루트의 network/socket
하위 디렉터리에 있어야 한다.JVM: Kotlin이 Java와 함께 사용되는 프로젝트에서 Kotlin 소스 파일은 Java 소스 파일과 동일한 소스 루트에 있어야 하며, 같은 디렉터리 구조를 따라야 한다. 각 파일은 각 패키지 문(statement)에 해당하는 디렉터리에 저장되어야 한다. 이렇게 하면 Java와 Kotlin 코드가 함께있어도 체계적으로 관리할 수 있다.
.kt
확장자를 붙여야 한다.// 파일 이름: ProcessDeclarations.kt
// 선언 처리 관련 클래스
class DeclarationProcessor {
fun processDeclaration(declaration: String): Boolean {
// 선언 처리 로직
println("Processing declaration: $declaration")
return true
}
}
// 선언 유효성 검사 관련 클래스
class DeclarationValidator {
fun validateDeclaration(declaration: String): Boolean {
// 선언 유효성 검사 로직
println("Validating declaration: $declaration")
return true
}
}
// 선언 관련 유틸리티 함수들
fun formatDeclaration(declaration: String): String {
// 선언 포맷 로직
return declaration.trim()
}
// 기타 선언 처리와 관련된 클래스, 함수들...
// 파일 이름: FileProcessingOperations.kt
const val DEFAULT_FILE_PATH = "/path/to/default/file"
fun readFromFile(filePath: String): String {
// 파일에서 읽기 로직
return "File content from $filePath"
}
fun writeToFile(filePath: String, content: String) {
// 파일에 쓰기 로직
println("Writing to file at $filePath: $content")
}
fun validateFilePath(filePath: String): Boolean {
// 파일 경로 유효성 검사 로직
return filePath.isNotBlank()
}
// 다른 최상위 수준 선언들...
카멜 표기법(파스칼 표기법)
을 사용한다.open class DeclarationProcessor { /*...*/ }
object EmptyDeclarationProcessor : DeclarationProcessor() { /*...*/ }
fun processDeclarations() { /*...*/ }
var declarationCount = 1
interface Foo { /*...*/ }
class FooImpl : Foo { /*...*/ }
fun Foo(): Foo { return FooImpl() }
class MyTestCase {
@Test fun `ensure everything works`() { /*...*/ }
@Test fun ensureEverythingWorks_onAndroid() { /*...*/ }
}
const val MAX_COUNT = 8
val USER_NAME_FIELD = "UserName"
val mutableCollection: MutableSet<String> = HashSet()
val PersonComparator: Comparator<Person> = /*...*/
enum class Color { RED, GREEN }
) 또는 대문자 카멜 케이스 이름을 사용하는 것이 좋다class C {
private val _elementList = mutableListOf<Element>()
val elementList: List<Element>
get() = _elementList
}
if (elements != null) {
for (element in elements) {
// ...
}
}
class A(val x: Int)
fun foo(x: Int) { ... }
fun bar() {
foo(1)
}
(
, [
뒤에 또는 ]
, )
앞에는 절대로 공백을 넣지 마라..
이나 ?:
주위에는 절대로 공백을 넣지 마라 : foo.bar().filter { it > 2 }.joinToString()
, foo?.bar()
//
뒤에는 공백을 넣어라 : // this is a comment
class Map<K, V> { ... }
::
주위에는 절대로 공백을 넣지 마라 : Foo::class
, String::length
?
앞에는 공백을 넣지 마라 : String?
:
앞에 공백을 넣어라::
앞에 공백을 넣지 않는다.:
뒤에는 항상 공백을 넣는다.// 타입과 상위 타입을 구분할 때
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 { /*...*/ }
}
// 선언과 그 타입을 구분할 때
val foo: Int = 1
// ':' 뒤에는 항상 공백을 넣는다.
val foo: Int = 1
class Foo : Bar {
...
}
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() { /*...*/ }
}
// 여는 중괄호를 별도의 줄에 놓기
class MyFavouriteVeryLongClassHolder :
MyLongHolder<MyFavouriteVeryLongClass>(),
SomeOtherInterface,
AndAnotherOne
{
fun foo() { /*...*/ }
}
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
@Named("Foo")
private val foo: Foo
@Target(AnnotationTarget.PROPERTY)
annotation class JsonExclude
@JsonExclude @JvmField
var x: String
@Test fun foo() { /*...*/ }
/** License, copyright and whatever */
@file:JvmName("FooBar")
package foo.bar
fun longMethodName(
argument: ArgumentType = defaultValue,
argument2: AnotherArgumentType,
): ReturnType {
// body
}
함수 매배견수에는 일반 들여쓰기(4 개의 공백)를 사용하라. 이렇게 함으로써 생성자 매개변수와 일관성을 유지할 수 있다.
함수의 본문이 단일 표현식으로 구성된 경우 표현식 본문을 사용하는 것을 선호하라.
fun foo(): Int { // bad
return 1
}
fun foo() = 1 // good
=
기호를 두고 표현식 본문을 네 칸 들여써라.fun f(x: String, y: String, z: String) =
veryLongFunctionCallWithManyWords(andLongParametersToo(), x, y, z)
val isEmpty: Boolean get() = size == 0
val foo: String
get() { /*...*/ }
=
기호 뒤에 줄 바꿈을 추가하고 초기화를 네 개의 공백으로 들여써라.private val defaultCharset: Charset? =
EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
if (!component.isSyncing &&
!hasAnyKotlinRuntimeInScope(module)
) {
return createKotlinNotConfiguredPanel(module)
}
if (condition) {
// body
} else {
// else part
}
try {
// body
} finally {
// cleanup
}
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
}
drawSquare(
x = 10, y = 10,
width = 100, height = 100,
fill = true
)
.
문자나 ?.
연산자를 다음 줄에 넣고, 한 칸 들여써라.val anchor = owner
?.firstChild!!
.siblings(forward = true)
.dropWhile { it is PsiComment || it is PsiWhiteSpace }
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)
}
class Person(
val firstName: String,
val lastName: String,
val age: Int, // trailing comma
)
-
이것 없이도 코드는 작동한다. 코틀린 스타일 가이드는 선언 위치에서 마지막 콤마 사용을 권장하며, 호출 위치의 경우 사용자의 재량에 따른다./**
* This is a documentation comment
* on multiple lines.
*/
/** This is a short documentation comment. */
@param
및 @return
태그의 사용을 피하라. 대신, 매개변수와 반환 값의 설명을 문서 주석에 직접 포함시키고, 매개변수가 언급될 때마다 링크를 추가하라. 주 텍스트의 흐름에 맞지 않는 긴 설명이 필요할 때만 @param 및 @return을 사용하라.// Avoid doing this:
/**
* 주어진 숫자의 절댓값을 반환한다.
* @param 숫자 절댓값을 반환할 숫자.
* @return 절댓값.
*/
fun abs(number: Int): Int { /*...*/ }
// Do this instead:
/**
* 주어진 [숫자]의 절댓값을 반환한다.
*/
fun abs(number: Int): Int { /*...*/ }
fun foo() { // ": Unit" is omitted here
}
println("$name has ${children.size} children")
// Bad: 변경되지 않는 값에 변경 가능한 컬렉션 유형을 사용한다.
fun validateValue(actualValue: String, allowedValues: HashSet<String>) { ... }
// Good: 대신에 불변 컬렉션 유형를 사용한다.
fun validateValue(actualValue: String, allowedValues: Set<String>) { ... }
// Bad: arrayListOf()는 변경 가능한 컬렉션 유형인 ArrayList<T>를 반환환다.
val allowedValues = arrayListOf("a", "b", "c")
// Good: listOf()는 List<T>를 반환한다.
val allowedValues = listOf("a", "b", "c")
// Bad
fun foo() = foo("a")
fun foo(a: String) { /*...*/ }
// Good
fun foo(a: String = "a") { /*...*/ }
typealias MouseClickHandler = (Any, MouseEvent) -> Unit
typealias PersonIndex = Map<String, Person>
import ... as ...
를 선호하는 것이 좋다.// Bad
val doubled = listOf(1, 2, 3).map { number -> number * 2 }
// Good
val doubled = listOf(1, 2, 3).map { it * 2 }
listOf(1, 2, 3).map { number ->
listOf('a', 'b', 'c').map { char ->
// 이곳에서 'it'을 사용하면 어떤 'it'인지 명확하지 않다.
// 따라서 매개변수를 명시적으로 선언하는 것이 좋다.
}
}
// Bad
val result = run loop@{
listOf(1, 2, 3).forEach {
if (it == 2) return@loop it
}
return@loop null
}
// Good
val result = listOf(1, 2, 3).firstOrNull { it == 2 }
drawSquare(x = 10, y = 10, width = 100, height = 100, fill = true)
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"
}
if (x == null) ... else ...
When
을 사용하는 대신when (x) {
null -> // ...
else -> // ...
}
if (value == true)
또는 if (value == false)
검사를 사용하는 것이 좋다.data class Item(val name: String)
fun processItems(items: List<Item>?) {
// Nullable 체크 후 forEach 사용
if (items != null) {
items.forEach {
println(it.name)
}
}
// 안전한 호출 연산자와 forEach 사용
items?.forEach {
println(it.name)
}
// forEach가 더 긴 호출 체인의 일부로 사용될 때
val result = someFunctionReturningNullableList()?.forEach {
// 이 부분에서 각 아이템을 처리
println(it.name)
}
}
fun someFunctionReturningNullableList(): List<Item>? {
return emptyList()
}
fun main() {
val items: List<Item>? = listOf(Item("A"), Item("B"), Item("C"))
processItems(items)
}
..<
연산자를 사용하라.for (i in 0..n - 1) { /*...*/ } // bad
for (i in 0..<n) { /*...*/ } // good
trimIndent
를 사용하거나, 내부 들여쓰기가 필요한 경우에는 trimMargin
를 사용하라.println("""
Not
trimmed
text
"""
)
//
// Not
// trimmed
// text
//
println("""
Trimmed
text
""".trimIndent()
)
// Trimmed
// text
println()
val a = """Trimmed to margin text:
|if(a > 1) {
| return a
|}""".trimMargin()
println(a)
// Trimmed to margin text:
// if(a > 1) {
// return a
// }
// Before
fun calculateArea(): Double {
return width * height
}
// After
val area: Double
get() = width * height
fun String.isNumeric(): Boolean {
return this.all { it.isDigit() }
}
val str = "12345"
println(str.isNumeric()) // true
class MutablePoint(var x: Int, var y: Int) {
infix fun translate(pair: Pair<Int, Int>) {
x += pair.first
y += pair.second
}
}
fun main() {
val point = MutablePoint(10, 20)
point translate (5 at 5)
println("Translated Point: (${point.x}, ${point.y})")
}
infix fun Int.at(dy: Int) = Pair(this, dy)
class MutablePoint(var x: Int, var y: Int) {
fun translate(dx: Int, dy: Int) {
x += dx
y += dy
}
}
fun main() {
val point = MutablePoint(10, 20)
point.translate(5, 5)
println("Translated Point: (${point.x}, ${point.y})") // Translated Point: (15, 25)
}
class Point(val x: Double, val y: Double) {
companion object {
fun fromPolar(angle: Double, radius: Double) = Point(...)
}
}
// Before
class User {
constructor(name: String) { ... }
constructor(name: String, age: Int) { ... }
constructor(name: String, age: Int, address: String) { ... }
}
// After
class User private constructor(name: String, age: Int, address: String) {
companion object {
fun createWithName(name: String) = User(name, 0, "")
fun createWithNameAndAge(name: String, age: Int) = User(name, age, "")
fun createWithNameAgeAndAddress(name: String, age: Int, address: String) = User(name, age, address)
}
}
fun apiCall(): String = MyJavaApi.getProperty("name")
class Person {
val name: String = MyJavaApi.getProperty("name")
}
fun main() {
val name = MyJavaApi.getProperty("name")
println(name)
}