Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

Jetpack Compose용 Kotlin

Jetpack Compose는 Kotlin을 중심으로 빌드되었습니다. 일부 경우에 Kotlin은 좋은 Compose 코드를 더 쉽게 작성할 수 있게 하는 특수 관용구를 제공합니다. 다른 프로그래밍 언어로 생각하고 그 언어를 Kotlin으로 마음속으로 번역하면 Compose의 강점 중 일부를 놓칠 수 있으며 관용적으로 작성된 Kotlin 코드를 이해하기 어려울 수 있습니다. Kotlin의 스타일에 더 익숙해지면 이러한 위험을 피하는 데 도움이 될 수 있습니다.

기본값 인수

Kotlin 함수를 작성할 때 함수 인수의 기본값을 지정할 수 있습니다. 이 기본값은 호출자가 명시적으로 값을 전달하지 않는 경우에 사용됩니다. 이 기능은 오버로드된 함수의 필요성을 줄여줍니다.

예를 들어 정사각형을 그리는 함수를 작성한다고 가정해 보겠습니다. 이 함수에는 각 변의 길이를 지정하는 단일 필수 매개변수인 side가 있을 수 있습니다. 그리고 thickness, edgeColor, fill 등과 같은 몇 가지 선택적 매개변수가 있을 수 있습니다. 호출자가 이러한 매개변수를 지정하지 않으면 함수는 기본값을 사용합니다. 다른 언어에서는 다음과 같이 여러 함수를 작성해야 할 것으로 예상할 수 있습니다.

// We don't need to do this in Kotlin!
void drawSquare(int side) {...}
void drawSquare(int side, int thickness) {...}
void drawSquare(int side, int thickness, Color edgeColor ) {...}
// …

Kotlin에서는 다음과 같이 단일 함수를 작성하고 인수의 기본값을 지정할 수 있습니다.

fun drawSquare(
    side: Int, thickness: Int = 2,
    edgeColor: Color = Color.Black,
    fill: Color = Color.White
) { ... }

이 기능을 사용하면 중복 함수를 여러 개 작성하지 않아도 되는 이점 외에도 코드를 훨씬 더 명확하게 읽을 수 있습니다. 호출자가 인수 값을 지정하지 않으면 기본값을 사용할 의사가 있음을 나타냅니다. 또한 이름이 지정된 매개변수를 사용하면 진행 상황을 훨씬 더 쉽게 확인할 수 있습니다. 코드를 검토하고 다음과 같은 함수 호출을 살펴보는 경우 drawSquare() 코드를 확인하지 않고서는 매개변수의 의미를 모를 수 있습니다.

drawSquare(30, 5, Color.Red);

이에 반해, 다음 코드는 자체 문서화합니다.

drawSquare(side = 30, thickness = 5, edgeColor = Color.Red)

대부분의 Compose 라이브러리는 기본값 인수를 사용합니다. 작성하는 구성 가능한 함수에도 똑같이 하는 것이 좋습니다. 이렇게 하면 컴포저블을 맞춤설정할 수 있지만 여전히 기본 동작을 간단하게 호출할 수 있습니다. 예를 들어 다음과 같은 간단한 텍스트 요소를 생성할 수 있습니다.

Text(text = "Hello, Android!")

이 코드는 다음과 같이 더 많은 Text 매개변수가 명시적으로 설정되는 훨씬 더 상세한 코드와 동일한 효과를 냅니다.

Text(text = "Hello, Android!",
    color = Color.Unset,
    fontSize = TextUnit.Inherit,
    letterSpacing = TextUnit.Inherit,
    Overflow = TextOverflow.Clip)

첫 번째 코드 스니펫은 훨씬 더 간단하고 읽기도 쉬울 뿐만 아니라 자체 문서화합니다. text 매개변수만 지정함으로써 다른 모든 매개변수에 기본값을 사용하겠다는 점을 문서화합니다. 이에 반해, 두 번째 스니펫은 설정된 값이 함수의 기본값이 되더라도 다른 매개변수의 값을 명시적으로 설정하겠다는 것을 의미합니다.

고차 함수 및 람다 표현식

Kotlin은 다른 함수를 매개변수로 받는 함수인 고차 함수를 지원합니다. Compose는 이 접근 방식을 기반으로 합니다. 예를 들어 Button 구성 가능한 함수는 onClick 람다 매개변수를 제공합니다. 이 매개변수의 값은 사용자가 버튼을 클릭할 때 버튼이 호출하는 함수입니다.

Button( // …
    onClick = myClickFunction)

고차 함수는 함수로 평가되는 표현식인 람다 표현식과 자연스럽게 쌍을 이룹니다. 함수가 한 번만 필요하면 이 함수를 고차 함수로 전달하기 위해 다른 곳에서 정의할 필요가 없습니다. 대신 람다 표현식을 사용하여 바로 함수를 정의하기만 하면 됩니다. 이전 예에서는 myClickFunction()이 다른 곳에 정의되어 있다고 가정합니다. 그러나 여기에서 해당 함수만 사용한다면 람다 표현식을 사용하여 함수를 인라인으로 정의하는 것이 더 간단합니다.

Button( // ...
    onClick = {
       // do something
       // do something else
    }
)

후행 람다

Kotlin은 마지막 매개변수가 람다인 고차 함수를 호출하기 위한 특수 구문을 제공합니다. 이 매개변수로 람다 표현식을 전달하려면 후행 람다 구문을 사용할 수 있습니다. 람다 표현식을 괄호 안에 넣는 대신 그 뒤에 놓습니다. 이는 Compose에서 일반적인 상황이므로 코드가 어떻게 보이는지 잘 알고 있어야 합니다.

예를 들어, 구성 가능한 함수인 Column()과 같이 모든 레이아웃의 마지막 매개변수는 하위 UI 요소를 내보내는 함수인 children입니다. 세 개의 텍스트 요소가 포함된 열을 만든 후 몇 가지 서식을 적용해야 한다고 가정해 보겠습니다. 이 코드는 작동하지만 매우 번거롭습니다.

Column(
    modifier = Modifier.padding(16.dp), children = {
        Text("Some text")
        Text("Some more text")
        Text("Last text")
    }
)

children 매개변수는 함수 서명의 마지막 매개변수이며 값을 람다 표현식으로 전달하기 때문에 이 매개변수를 괄호에서 꺼낼 수 있습니다.

Column(modifier = Modifier.padding(16.dp)) {
    Text("Some text")
    Text("Some more text")
    Text("Last text")
}

두 예의 의미는 정확히 동일합니다. 중괄호는 content 매개변수에 전달되는 람다 표현식을 정의합니다.

실제로 전달하는 유일한 매개변수가 후행 람다인 경우(즉, 마지막 매개변수가 람다이고 다른 매개변수를 전달하지 않는다면) 괄호를 모두 생략할 수 있습니다. 예를 들어 Column에 수정자를 전달할 필요가 없다고 가정해 보겠습니다. 그러면 다음과 같이 코드를 작성할 수 있습니다.

Column {
    Text("Some text")
    Text("Some more text")
    Text("Last text")
}

이 구문은 Compose에서 특히 Column과 같은 레이아웃 요소에서 매우 일반적입니다. 마지막 매개변수는 요소의 하위 요소를 정의하는 람다 표현식이며 이러한 하위 요소는 함수 호출 이후 중괄호로 지정됩니다.

범위 및 수신자

일부 메서드와 속성은 특정 범위에서만 사용할 수 있습니다. 제한된 범위를 사용하면 필요한 곳에 기능을 제공하고 적절하지 않은 곳에서 실수로 기능을 사용하는 것을 방지할 수 있습니다.

Compose에서 사용된 예를 살펴보겠습니다. Row 레이아웃 컴포저블을 호출하면 콘텐츠 람다가 RowScope 내에서 자동으로 호출됩니다. 이를 통해 RowRow 내에서만 유효한 기능을 노출할 수 있습니다. 아래 예는 Rowgravity 수정자의 행별 값을 어떻게 노출했는지 보여줍니다.

Row {
    Text(
        text = "Hello world",
        // This Text is inside a RowScope so it has access to
        // Alignment.CenterVertically but not to
        // Alignment.CenterHorizontally, which would be available
        // in a ColumnScope.
        modifier = Modifier.gravity(Alignment.CenterVertically)
    )
}

일부 API는 수신자 범위에서 호출되는 람다를 허용합니다. 이러한 람다는 매개변수 선언을 기반으로 다른 곳에 정의된 속성 및 함수에 액세스할 수 있습니다.

Box(
    modifier = Modifier.drawBehind {
        // This method accepts a lambda of type DrawScope.() -> Unit
        // therefore in this lambda we can access properties and functions
        // available from DrawScope, such as the `drawRectangle` function.
        drawRectangle(...)
    }
)

자세한 내용은 Kotlin 문서의 수신자가 있는 함수 리터럴을 참조하세요.

위임된 속성

Kotlin은 위임된 속성을 지원합니다. 이러한 속성은 필드인 것처럼 호출되지만 속성의 값은 표현식을 평가하여 동적으로 결정됩니다. by 구문의 사용을 통해 이러한 속성을 인식할 수 있습니다.

class delegatingClass {
    var name: String by nameGetterFunction()
}

다른 코드는 다음과 같은 코드로 속성에 액세스할 수 있습니다.

myDC = delegatingClass()
println("The name property is: " + myDC.name)

println()이 실행되면 nameGetterFunction()이 호출되어 문자열 값을 반환합니다.

이러한 위임된 속성은 상태 지원 속성 사용 시 특히 유용합니다.

var showDialog by remember { mutableStateOf(false) }

// Updating the var automatically triggers a state change
showDialog = true

디스트럭처링 데이터 클래스

데이터 클래스를 정의하면 디스트럭처링 선언을 통해 데이터에 쉽게 액세스할 수 있습니다. 예를 들어 Person 클래스를 정의한다고 가정해 보겠습니다.

data class Person(val name: String, val age: Int)

이 유형의 객체가 있다면 다음과 같은 코드를 사용하여 값에 액세스할 수 있습니다.

val mary = Person(name = "Mary", age = 35)

// ...

val (name, age) = mary

Compose 함수에서는 이러한 종류의 코드를 흔히 보게 됩니다.

ConstraintLayout {

    val (image, title, subtitle) = createRefs()

    // The `createRefs` function returns a data object;
    // the first three components are extracted into the
    // image, title, and subtitle variables.

    // ...

}

데이터 클래스는 다른 많은 유용한 기능을 제공합니다. 예를 들어 데이터 클래스 정의 시 컴파일러는 equals()copy()와 같은 유용한 함수를 자동으로 정의합니다. 자세한 내용은 데이터 클래스 문서를 참조하세요.

싱글톤 객체

Kotlin을 사용하면 인스턴스가 항상 한 개만 있는 클래스인 싱글톤을 쉽게 선언할 수 있습니다. 이러한 싱글톤은 object 키워드를 사용하여 선언됩니다. Compose는 이러한 객체를 자주 사용합니다. 예를 들어 MaterialTheme은 싱글톤 객체로 정의됩니다. MaterialTheme.colors, shapestypography 속성에는 모두 현재 테마의 값이 포함되어 있습니다.