-
Notifications
You must be signed in to change notification settings - Fork 0
Android Code Convention
Kotlin 스타일 가이드 | Android Developers
[Coding conventions | Kotlin](https://kotlinlang.org/docs/coding-conventions.html)
✨ 팀프로젝트에서 팀원의 코드 리뷰는 항상 수반되기 때문에 “최종, 찐최종, 찐찐최종” 같은 네이밍보다는 해당 기능을 유추할 수 있을 정도의 적절한 네이밍이 바람직하고 더욱 원활한 협업을 위해 어느 정도의 틀이 필요한데 코드 컨벤션이 그 출발로 적절한 것 같습니다.-
1.1 디렉토리 구조(Directory structure)
- Kotlin 소스 파일은 Java 소스 파일과 동일한 소스 루트에 있어야 하며 동일한 디렉토리 구조를 따라야한다.
- 공통 루트 패키지가 생략된 패키지 구조를 따라야한다.
공통 패키지: com.example.aboutme → root 디렉토리
서브 패키지: com.example.aboutme.agit.data → agit/data 디렉토리
- 모든 소스 파일은 UTF-8로 인코딩 되어야 한다.
-
1.2 소스 파일 이름(Source file names)
- Kotlin 파일에 하나의 클래스만 포함된 경우 해당 파일의 이름은 곧 클래스 이름이다.
- Kotlin 파일에 여러 클래스 또는 최상위 레벨(Top-level) 선언만 포함되어 있으면 파일에 포함된 것들을 설명하는 이름으로 파일 이름을 지정한다.
- 카멜 표기법(CamelCase)
- 파일 이름이 곧 코드가 하는 것들이 설명되어야한다. 의미 없는 단어는 사용을 자제한다.
-
1.3 소스 파일 구성(Source file organization)
- 서로 연관되어있는 여러 선언들을 하나의 Kotlin 소스 파일에 포함하나, 파일 크기가 너무 크지 않고 수백 줄을 초과하면 안된다.
- 클래스에 모든 클라이언트와 관련된 클래스의 확장 함수를 넣을 때 클래스 자체에 정의 된 동일한 파일에 이 함수를 넣는 것이 좋다.
- 특정 클라이언트에 대해서만 의미가 있는 확장 기능을 정의할 때는 해당 클라이언트 코드 옆에 확장 기능을 지정한다.
-
1.4 클래스 레이아웃(Class layout)
- 일반적으로 클래스의 내용은 다음 순서로 정렬한다.
- 속성 선언 및 Initializer 블록
- 보조 생성자(Secondary constructors)
- 메소드 선언(Method declarations)
- 동반자 객체(Companion Object)
- 메소드 선언은 사전 순 또는 가시성에 따라 정렬하지 말고, 일반 메소드와 확장 메소드를 분리하지 않는다.
- 연관된 메소드끼리 함께 위치하도록 구성해야 한다. 다른 사람이 봤을 때 로직을 이해하기 쉽도록 배치한다.
- 중첩 클래스는 해당 클래스를 사용하는 코드 다음에 배치한다.
- 중첩 클래스가 외부에서만 사용하도록 의도되어 있고, 클래스 내부에서 참조되지 않은 경우 Companion Object(동반자 객체) 뒤의 끝에 넣는다.
-
1.5 인터페이스 구현 레이아웃(Interface implementation layout)
- 인터페이스를 구현할 때 구현 멤버들의 순서를 인터페이스 멤버와 동일한 순서로 유지합니다.
- 필요한 경우, 구현에 사용되는 privated method를 배치한다.
-
1.6 오버로드 레이아웃(Overload layout)
- 클래스 내에서 항상 오버로드된 메서드를 함께 둔다.
- Java naming convention을 따름
- Package → 항상 소문자
- Class, Object: 카멜 표기법 사용
object EmptyDeclarationProcessor : DeclarationProcessor() { ... }-
2.1 함수 이름(Function names) ✨
- 함수, 속성,및 지역 변수의 이름은 소문자로 시작하여 카멜 표기법을 사용하며, 밑줄은 사용하지 않는다.
fun processDeclarations() { /*...*/ } var declarationCount = 1
- 클래스의 인스턴스를 만드는데 사용되는 팩토리 함수는 생성되는 클래스 이름과 동일할 수 있다.
interface Foo { /*...*/ } class FooImpl : Foo { /*...*/ } fun Foo(): Foo { return FooImpl() }
-
2.2 테스트 메소드 이름(Names for test methods)
- 테스트의 코드의 경우 메소드에 backtick(’), space, underscore(_)를 사용할 수 있다.
class MyTestCase { @Test fun `ensure everything works`() { /*...*/ } @Test fun ensureEverythingWorks_onAndroid() { /*...*/ } }
-
2.3 속성 이름(Property names) ✨
- 상수: 대문자와 _(underscore) 사용 가능
const val MAX_COUNT = 8 val USER_NAME_FIELD = "UserName"
- 변경 가능한 데이터를 가진 객체: 카멜 표기법
val mutableCollection: MutableSet<String> = HashSet()
- 싱글톤 객체: 객체 선언과 동일하게 사용
val PersonComparator: Comparator = …
- enum 상
enum class Color { RED, GREEN }
-
2.4 (Names for backing properties)
- 클래스가 개념상으로 동일하지만 하나는 public API의 일부이고, 다른 하나는 구현 세부 정보가 있는 private property 이름은 접두사로 _(underscore)를 사용한다.
- private 속성을 반환하는 public 속성을 사용하는 경우, private 속성 앞에 _(underscore)를 붙여 사용한다.
class C { private val _elementList = mutableListOf<Element>() val elementList: List<Element> get() = _elementList }
-
2.5 좋은 이름 선택(Choosing good names) ✨
- 클래스
→ 무엇을 하는 클래스인지 의미를 표현,
→ 명사 또는 명사구의 이름을 사용(List, PersonReader)
- 메소드
→ 어떤 변화 또는 객체의 반환 작업을 하는 메소드인지 표현
→ 동사 또는 동사구의 이름을 사용(close, readPersons)
- 선언 이름의 일부가 약어인 경우
→ 약어가 두 문자인 경우는 대문자로 표기(IOStream)
→ 더 긴 경우는 첫번째 문자만 대문자로 사용(XmlFomatter, HttpInputStream)
- 대부분의 경우 Java code convention을 따름
- 4 spaces 사용, 탭은 사용하지 않는다.
- 중괄호의 경우 구성 시작 부분의 끝줄에 여는 중괄호를 입력하고 열린 구성과 수직적으로 정렬된 별도의 줄을 닫는 중괄호를 입력한다.
- 세미콜론은 선택사항이지만 생략하는 것을 권장
if (elements != null) {
for (element in elements) {
// ...
}
}-
3.1 가로 공백(Horizontal whitespaces) ✨
- 연산자 앞, 뒤에 공백을 추가한다.
- a + b, a / b
- 예외
- “range to” 연산 주위에도 공백을 넣지 말아야 한다. (0..I)
- 단항 연산 주위에도 공백을 넣지 말아야 한다. (a++)
- 중괄호 앞에 공백을 추가한다.
- if, when, for and while
- 괄호 앞에는 공백을 추가하지 않는다.
class A(val x: Int) fun foo(x: Int) { ... } fun bar() { foo(1) }
- (, [ 뒤에 또는 ), ] 전에 공백을 추가하지 않는다.
- . 또는 ?. 주위에 공백을 추가하지 않는다.
foo.bar().filter { it > 2 }.joinToString() foo?.bar()- 주석 // 뒤에 공백을 추가한다.
// 주석을 추가한다. -
3.2 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 { /*...*/ } }
-
3.3 Class header formatting ✨
- 기본 생성자의 매개 변수가 몇 개 있는 클래스는 한 줄로 작성할 수 있다.
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() { /*...*/ } }
- 생성자의 매게 변수에는 일반 들여 쓰기(4 공백)을 사용한다.
- 이유 : 기본 생성자에서 선언 된 속성이 클래스 본문에 선언 된 속성 만큼 들여 쓰기가 되도록 해야한다.
-
3.4 수정자(Modifiers) ✨
- 선언에 여러 수정자가 있는 경우 항상 다음 순서로 입력해야한다.
public / protected / private / internal expect / actual final / open / abstract / sealed / const external override lateinit tailrec vararg suspend inner enum / annotation companion inline infix operator data
- 모든 특수 효과를 수정자 앞에 배치하도록 한다.
@Named("Foo") private val foo: Foo
- 라이브러리에서 작업하지 않는 한 중복 수정자 (예: public) 를 생략한다
-
3.5 Annotation formatting
- 어노테이션은 일반적으로 별개의 줄에 붙는다. 단, 어노테이션이 첨부 된 문장과 같은 들여쓰기가 있어야한다.
@Target(AnnotationTarget.PROPERTY) annotation class JsonExclude
- 인수가 없는 어노테이션은 같은 줄에 배치할 수 있다.
@JsonExclude @JvmField var x: String
- 인수가 없는 단일 어노테이션은 해당 선언과 동일한 줄에 배치할 수 있다.
@Test fun foo() { /*...*/ }
-
3.6 File annotation
- 파일 어노테이션은 package 명령문 앞에 배치되며 빈 줄이 있는 package 와 구분한다. (package가 아닌 파일을 대상으로 한다는 사실을 강조하기 위함)
/** License, copyright and whatever */ @file:JvmName("FooBar") package foo.bar
-
3.7 Functions formatting ✨
- 함수 시그니쳐가 한 줄에 들어가지 않으면 다음 구문을 사용한다.
fun longMethodName( argument: ArgumentType = defaultValue, argument2: AnotherArgumentType, ): ReturnType { // body }
- 함수 매개 변수에 일반 들여 쓰기(4공백)을 사용하도록 한다.
- 생성자와 매개 변수와의 일관성을 두기 위함
- 하나의 표현식으로 구성된 본문이 있는 함수에 표현식 본문을 사용하는 것이 좋다.
fun foo(): Int { // bad return 1 } fun foo() = 1 // good
-
3.8 Expression bodies formatting
- 함수에 선언된 동일한 행에 맞지 않는 표현식 본문이 있다면 첫 행에 =부호를 붙인다.
- 표현 본문을 4칸만큼 들여쓴다.
fun f(x: String, y: String, z: String) = veryLongFunctionCallWithManyWords(andLongParametersToo(), x, y, z)
-
3.9 Property formatting
- 매우 간단한 읽기 전용 속성의 경우 한 줄 형식을 고려하도록 한다.
val isEmpty: Boolean get() = size == 0
- 복잡한 속성의 경우 항상 get 및 set 키워드를 별도의 줄에 넣는다.
val foo: String get() { /*...*/ }
- initializer가 있는 속성의 경우 initializer가 길면 등호 다음에 줄 바꿈을 추가하고 initializer를 4칸만큼 들여 다.
private val defaultCharset: Charset? = EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
-
3.10 Formatting control flow statements ✨
- if 또는 when문의 조건이 여러 행인 경우 항상 명령문 본문 주위에 중괄호를 사용한다.
- 명령문 begin에 상대적인 4칸 공백으로 조건의 각 후속줄을 들여쓰기한다.
- 조건의 닫는 괄호와 여는 중괄호를 별도의 줄에 넣는다.
if (!component.isSyncing && !hasAnyKotlinRuntimeInScope(module) ) { return createKotlinNotConfiguredPanel(module) }
- 이전의 중괄호와 같은 줄에 else, catch, finally 키워드와 do/while 루프의 while 키워드를 넣는다.
if (condition) { // body } else { // else part } try { // body } finally { // cleanup }
- when 문에서 분기가 단일 행 이상인 경우 case 블록과 빈 행을 구분하도록 한다.
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 }
-
3.11 Method call formatting ✨
- 긴 인수 목록에서는 여는 괄호 다음에 줄 바꿈을 넣도록 한다.
- 인수는 4칸 들여쓴다.
- 같은 줄에 여러 가지 밀접하게 관련된 인수를 그룹화 하도록 한다.
- 인수 이름과 값을 구분하는 = 기호 주위에 공백을 넣도록 한다.
drawSquare( x = 10, y = 10, width = 100, height = 100, fill = true ) -
3.12 Chained call wrapping ✨
- 별도로 묶여있는 변수를 선언할 때는 문자 또는 ?. 연산자 다음 행에 하나의 들여쓰기를 하도록 한다. 체인의 첫 번째 호출은 일반적으로 앞에 줄 바꿈이 있어야 하지만 코드를 생략도 무방합니다.
val anchor = owner ?.firstChild!! .siblings(forward = true) .dropWhile { it is PsiComment || it is PsiWhiteSpace }
-
3.13 Lamda formatting
- 람다식에서 매개 변수를 본문에 분리하는 화살표 주위와 중괄호 주위에 공백을 사용한다.
- 호출이 단일 람다로 사용되면 가능할 때 마다 괄호 밖으로 전달이 되어야 한다.
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) } -
3.14 Trailing commas
class Person( val firstName: String, val lastName: String, val age: Int, // trailing comma )
- 버전 컨트롤 diff를 더 깔끔하게 만든다.
→ 모든 초점이 변경된 값에 집중된다.
- 요소를 추가하고 재정렬하는 것이 쉬워진다.
→ 요소를 조작하더라도 콤마를 추가하거나 삭제할 필요가 없다.
- 코드 생성을 단순화한다.
enum class Direction { NORTH, SOUTH, WEST, EAST, // trailing comma }
fun shift(x: Int, y: Int) { /*...*/ } shift( 25, 20, // trailing comma ) val colors = listOf( "red", "green", "blue", // trailing comma )
class Customer( val name: String, val lastName: String, // trailing comma ) class Customer( val name: String, lastName: String, // trailing comma )
- 더 긴 문서 주석을 보려면 별도의 행에 /** 를 입력하고 각 후속 행을 시작하도록 한다.
/**
* This is a documentation comment
* on multiple lines.
*/- 짧은 주석은 한 줄에 입력할 수 있다.
/** This is a short documentation comment. */- 일반적으로 @param, @return 태그를 사용하지 않도록 한다.
- 대신 매개 변수 설명 및 반환 값을 설명서 주석에 직접 통합하고 매개 변수에 대한 링크를 언급한 곳마다 추가하도록 한다.
- 주 텍스트의 흐름이 맞지 않는 긴 설명이 필요한 경우에만 @param, @return을 사용하도록 한다.
// Avoid doing this:
/**
* Returns the absolute value of the given number.
* @param number The number to return the absolute value for.
* @return The absolute value.
*/
fun abs(number: Int): Int { /*...*/ }
// Do this instead:
/**
* Returns the absolute value of the given [number].
*/
fun abs(number: Int): Int { /*...*/ }-
일반적으로 Kotlin의 특정 구문 구조가 선택사항이며 IDE에서 중복으로 강조 표시된 경우 코드에서 생략해야 한다.
-
명확성을 위해 코드에 불필요한 구문 요소를 남겨두지 않는다.
-
5.1 Unit
- 함수가 Unit을 반환하면 반환 유형을 생략해야 한다.
fun foo() { // ": Unit" is omitted here }
-
5.2 Semicolons
- 가능한 경우 세미콜론을 생략하도록 한다.
-
5.3 String templates ✨
- 문자열 템플릿에 간단한 변수를 삽입할 때는 중괄호를 사용하지 않는다.
- 더 긴 표현식에 대해서만 중괄호를 사용한다.
println("$name has ${children.size} children")
-
6.1 Immutability
- 변경할 수 없는 데이터를 가변적으로 사용하는 것이 바람직하다.
- 초기화 후 로컬 변수와 속성이 수정되지 않으면 항상 var 대신 val을 사용하도록 한다.
- 변경되지 않는 컬렉션을 선언하려면 항상 불변 컬렉션 인터페이스를 사용한다. (Collection, List, Set, Map)
- 팩토리 함수를 사용하여 컬렉션 인스턴스를 만들 때는 가능한 불변 컬렉션 유형을 반환하는 함수를 사용한다.
// Bad: use of a mutable collection type for value which will not be mutated fun validateValue(actualValue: String, allowedValues: HashSet<String>) { ... } // Good: immutable collection type used instead fun validateValue(actualValue: String, allowedValues: Set<String>) { ... } // Bad: arrayListOf() returns ArrayList<T>, which is a mutable collection type val allowedValues = arrayListOf("a", "b", "c") // Good: listOf() returns List<T> val allowedValues = listOf("a", "b", "c")
-
6.2 Default parameter values
- Overload 된 함수를 선언할 때는 기본 매개 변수 값을 사용하여 함수를 선언하는 것이 좋다.
// Bad fun foo() = foo("a") fun foo(a: String) { /*...*/ } // Good fun foo(a: String = "a") { /*...*/ }
-
6.3 Type aliases
- 코드베이스에서 여러 번 사용되는 유형의 매개 변수가 있다면 typealias를 사용하는 것이 좋다.
typealias MouseClickHandler = (Any, MouseEvent) -> Unit typealias PersonIndex = Map<String, Person>
-
6.4 Lamda parameters
- 짧고 중첩되지 않은 람다에서는 매게 변수를 명시적으로 선언하는 대신 it 규칙을 사용하는 것이 좋다.
- 매개 변수가 있는 중첩 된 람다에서 매개 변수는 항상 명시적으로 선언되어야 한다.
-
6.5 Returns in a lambda
- 람다에서 라벨이 지정된 여러 개의 리턴을 사용하지 않도록 한다.
- 람다를 재구성하여 단일 리턴을 갖도록 해야 한다.
- 단일 리턴이 불가능한 경우 익명 함수로 변환하는 것을 고려하도록 한다.
- 람다에서 마지막 문장에 레이블이 지정된 리턴 값을 사용하지 않도록 한다.
-
6.6 Named arguments
- 모든 매개 변수의 의미가 문맥에서 절대적으로 명확하지 않다면 메소드가 동일한 기본 유형의 여러 매개 변수 또는 Boolean 유형의 매개 변수를 사용하는 경우 Named arguments로 개수와 상관없이 원하는 위치에 값을 대입할 수 있다.
drawSquare(x = 10, y = 10, width = 100, height = 100, fill = true)
-
6.7 Conditional statements ✨
- if, if 및 when의 조건문을 사용하는 것이 좋다.
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" }
-
6.8 if or when
- binary conditions의 경우 when 대신에 if를 사용하는것이 좋다.
when (x) { null -> ... else -> ... } if (x == null) ... else ...
- 세 가지 이상의 옵션이 있는 경우는 when의 사용을 선호한다.
-
6.9 Nullable Boolean values in conditions
- 조건문에 Nullable Boolean를 사용해야 하는 경우라면 if (value == true) 또는 if (value == false)를 사용한다.
-
6.10 Loops
- higher-order 함수를 루프에 사용하는 것이 좋다.(filter, map 등) 예외 : forEach(forEach 의 수신자가 nullable 이거나 더 긴 체인 호출의 일부로 사용되지 않는 한 일반적으로 for loop를 대신 사용하는 것을 선호한다.
-
6.11 Loops on ranges
- until 함수를 사용해서 열린 범위를 반복하도록 한다.
for (i in 0..n - 1) { /*...*/ } // bad for (i in 0..<n) { /*...*/ } // good
-
6.12 Strings
- 문자열 템플릿을 사용하는 것이 좋다.
- \n escape sequences 을 사용하는 대신에 다중 행을 사용하는 것을 권장한다.
- 여러 줄의 문자열에서 들여쓰기를 유지하려면 trimMargin, trimIndent 를 사용한다.
fun main() { println(""" Not trimmed text """ ) println(""" Trimmed text """.trimIndent() ) println() val a = """Trimmed to margin text: |if(a > 1) { | return a |}""".trimMargin() println(a) } // 결과값 Not trimmed text Trimmed text Trimmed to margin text: if(a > 1) { return a }
-
6.13 Functions vs Properties
- 인수가 없는 함수는 읽기 전용 속성과 호환될 수 있다.
- 의미는 비슷하지만 서로를 선호할 때는 규칙이 있다.
- 기본 알고리즘이 다음과 같은 경우 함수에 대한 속성을 선호한다.
- throw 하지 않는다.
- 쉬운 계산 또는 첫 번째 실행에서 캐싱된다.
- 객체 상태가 변경되지 않은 경우 호출에 대한 동일한 결과를 반환한다.
-
6.14 Extension functions
- 확장 기능을 자유롭게 사용한다.
- 주로 객체에 대해 작동하는 함수가 있을 때 마다 객체를 receiver로 받아들이는 확장 함수를 고려해야한다.
- API 오염을 최소화 하기 위해 확장 함수의 가시성을 가능한 많이 제한해야한다.
- 필요한 경우 로컬 확장 함수, 멤버 확장 함수 또는 비공개 가시성이 있는 최상위 확장 함수를 사용하도록 한다.
-
6.15 Infix functions
- 비슷한 역활을 하는 두 개의 객체에서 작동 할 때만 infix function을 선언한다.
- 좋은 예 : and, to, zip
- 나쁜 예 : add
- 메소드가 receiver object를 변경하면 삽입을 선언하면 안된다.
-
6.16 Factory functions
- 클래스에 대한 팩토리 함수를 선언하는 경우 클래스 자체와 동일한 이름을 지정하면 안된다.
- 고유한 이름을 사용하여 팩토리 함수의 이유를 명확히 해야한다.
class Point(val x: Double, val y: Double) { companion object { fun fromPolar(angle: Double, radius: Double) = Point(...) } }
- 다른 슈퍼 클래스 생성자를 호출하지 않고 기본 인수값을 사용하여 단일 생성자로 축소할 수 없는 여러 Overload 된 생성자가 있는 경우 Overload 된 생성자를 팩토리 함수로 대체하는 것이 좋다.
-
6.17 Platform types
- 플랫폼 유형의 식을 리턴하는 경우 공용 함수/메소드는 Kotlin 유형을 명시적으로 선언해야 한다.
fun apiCall(): String = MyJavaApi.getProperty("name")
- 플랫폼 유형의 표현식으로 초기화 된 모든 패키지(패키지 또는 클래스 수준)는 명시적으로 Kotlin 유형을 선언해야한다.
class Person { val name: String = MyJavaApi.getProperty("name") }
- 플랫폼 형식으로 초기화 된 값은 형태 선언을 가질 수도 있고 가지지 않을 수도 있다.
fun main(args: Array) { val name = MyJavaApi.getProperty("name") println(name) }
-
6.18 Scope functions apply/with/run/also/let)
- Kotlin은 주어진 객체의 Context에서 코드 블록을 실행하는 다양한 함수를 제공합니다.
[Scope functions | Kotlin](https://kotlinlang.org/docs/scope-functions.html)
- 멤버의 가시성을 항상 명시적으로 지정해야한다. (선언을 실수로 공개 API에 노출시키지 않도록 한다.)
- 항상 함수 반환 형식 및 속성 형식을 명시적으로 지정한다. (구현이 변경될 때 실수로 반환 형식을 변경하지 않도록 한다.)
- 새로운 문서를 필요로 하지 않는 재정의를 제외하고는 모든 공용 멤버에 대한 KDoc 주석을 제공한다. (라이브러리에 대한 문서 생성 지원을 위함)