프로그래머를 위한 Kotlin 부트캠프 5.2: 일반

이 Codelab은 프로그래머를 위한 Kotlin 부트캠프 과정의 일부입니다. Codelab을 순서대로 진행하면 이 과정의 학습 효과를 극대화할 수 있습니다. 기존 지식에 따라 일부 섹션을 훑어볼 수도 있습니다. 이 과정은 객체 지향 언어를 알고 Kotlin을 배우고 싶은 프로그래머를 대상으로 합니다.

소개

이 Codelab에서는 일반 클래스, 함수, 메서드와 Kotlin에서 작동하는 방식을 알아봅니다.

이 과정의 강의는 단일 샘플 앱을 빌드하는 대신 지식을 쌓을 수 있도록 설계되었지만, 서로 반독립적이므로 잘 아는 섹션은 대충 훑어볼 수 있습니다. 이러한 요소를 연결하기 위해 많은 예에서 수족관 테마를 사용합니다. 전체 수족관 스토리를 확인하려면 프로그래머를 위한 Kotlin 부트캠프 Udacity 과정을 확인하세요.

기본 요건

  • Kotlin 함수, 클래스, 메서드의 문법
  • IntelliJ IDEA에서 새 클래스를 만들고 프로그램을 실행하는 방법

학습할 내용

  • 일반 클래스, 메서드, 함수로 작업하는 방법

실습할 내용

  • 일반 클래스를 만들고 제약 조건 추가
  • inout 유형 만들기
  • 일반 함수, 메서드, 확장 함수 만들기

일반 유형 소개

Kotlin에는 많은 프로그래밍 언어와 마찬가지로 일반 유형이 있습니다. 일반 유형을 사용하면 클래스를 일반으로 만들어 클래스를 훨씬 더 유연하게 만들 수 있습니다.

항목 목록을 보유하는 MyList 클래스를 구현한다고 가정해 보겠습니다. 일반 유형이 없으면 각 유형에 대해 MyList의 새 버전을 구현해야 합니다. Double용 하나, String용 하나, Fish용 하나. 일반을 사용하면 목록을 일반으로 만들어 모든 유형의 객체를 포함할 수 있습니다. 이는 여러 유형에 맞는 와일드카드를 유형으로 만드는 것과 같습니다.

일반 유형을 정의하려면 클래스 이름 뒤에 <T> 꺾쇠 괄호 안에 T를 넣습니다. 다른 문자나 더 긴 이름을 사용할 수도 있지만 일반 유형의 규칙은 T입니다.

class MyList<T> {
    fun get(pos: Int): T {
        TODO("implement")
    }
    fun addItem(item: T) {}
}

T을 일반 유형인 것처럼 참조할 수 있습니다. get()의 반환 유형은 T이고 addItem()의 매개변수는 T 유형입니다. 물론 일반 목록은 매우 유용하므로 List 클래스는 Kotlin에 내장되어 있습니다.

1단계: 유형 계층 구조 만들기

이 단계에서는 다음 단계에서 사용할 클래스를 만듭니다. 서브클래싱은 이전 Codelab에서 다루었지만 여기에서 간략하게 복습해 보겠습니다.

  1. 예를 깔끔하게 유지하려면 src 아래에 새 패키지를 만들고 generics라고 이름을 지정합니다.
  2. generics 패키지에서 새 Aquarium.kt 파일을 만듭니다. 이렇게 하면 충돌 없이 동일한 이름을 사용하여 항목을 재정의할 수 있으므로 이 Codelab의 나머지 코드는 이 파일에 들어갑니다.
  3. 물 공급 유형의 유형 계층 구조를 만듭니다. 먼저 WaterSupply을 서브클래스로 분류할 수 있도록 open 클래스로 만듭니다.
  4. 불리언 var 매개변수 needsProcessing 추가 이렇게 하면 getter 및 setter와 함께 변경 가능한 속성이 자동으로 생성됩니다.
  5. 수돗물에는 물고기에 좋지 않은 첨가물이 포함되어 있으므로 WaterSupply를 확장하는 서브클래스 TapWater를 만들고 needsProcessingtrue를 전달합니다.
  6. TapWater에서 물을 정화한 후 needsProcessingfalse로 설정하는 addChemicalCleaners()이라는 함수를 정의합니다. needsProcessing 속성은 기본적으로 public이고 서브클래스에서 액세스할 수 있으므로 TapWater에서 설정할 수 있습니다. 완성된 코드는 다음과 같습니다.
package generics

open class WaterSupply(var needsProcessing: Boolean)

class TapWater : WaterSupply(true) {
   fun addChemicalCleaners() {
       needsProcessing = false
   }
}
  1. WaterSupply의 서브클래스인 FishStoreWaterLakeWater를 두 개 더 만듭니다. FishStoreWater는 처리할 필요가 없지만 LakeWaterfilter() 메서드로 필터링해야 합니다. 필터링 후에는 다시 처리할 필요가 없으므로 filter()에서 needsProcessing = false을 설정합니다.
class FishStoreWater : WaterSupply(false)

class LakeWater : WaterSupply(true) {
   fun filter() {
       needsProcessing = false
   }
}

추가 정보가 필요한 경우 Kotlin의 상속에 관한 이전 강의를 검토하세요.

2단계: 일반 클래스 만들기

이 단계에서는 다양한 유형의 물 공급을 지원하도록 Aquarium 클래스를 수정합니다.

  1. Aquarium.kt에서 클래스 이름 뒤에 대괄호 안에 <T>이 있는 Aquarium 클래스를 정의합니다.
  2. T 유형의 변경 불가능한 속성 waterSupplyAquarium에 추가합니다.
class Aquarium<T>(val waterSupply: T)
  1. genericsExample()라는 함수를 작성합니다. 클래스의 일부가 아니므로 main() 함수나 클래스 정의와 같이 파일의 최상위 수준에 배치할 수 있습니다. 함수에서 Aquarium를 만들고 WaterSupply를 전달합니다. waterSupply 매개변수는 일반적이므로 꺾쇠 괄호 <> 안에 유형을 지정해야 합니다.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
}
  1. genericsExample()에서 코드는 수족관의 waterSupply에 액세스할 수 있습니다. TapWater 유형이므로 유형 변환 없이 addChemicalCleaners()를 호출할 수 있습니다.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
    aquarium.waterSupply.addChemicalCleaners()
}
  1. Aquarium 객체를 만들 때 Kotlin에는 유형 추론이 있으므로 꺾쇠 괄호와 그 사이의 내용을 삭제해도 됩니다. 따라서 인스턴스를 만들 때 TapWater를 두 번 말할 필요가 없습니다. 유형은 Aquarium의 인수로 추론할 수 있으며, 여전히 TapWater 유형의 Aquarium를 만듭니다.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    aquarium.waterSupply.addChemicalCleaners()
}
  1. 상황을 확인하려면 addChemicalCleaners()을 호출하기 전후에 needsProcessing을 출력하세요. 완성된 함수는 다음과 같습니다.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
    println("water needs processing: ${aquarium.waterSupply.needsProcessing}")
    aquarium.waterSupply.addChemicalCleaners()
    println("water needs processing: ${aquarium.waterSupply.needsProcessing}")
}
  1. genericsExample()을 호출하는 main() 함수를 추가한 다음 프로그램을 실행하고 결과를 확인합니다.
fun main() {
    genericsExample()
}
⇒ water needs processing: true
water needs processing: false

3단계: 더 구체적으로 만들기

일반적으로 거의 모든 것을 전달할 수 있지만 때로는 문제가 됩니다. 이 단계에서는 Aquarium 클래스에 넣을 수 있는 항목을 더 구체적으로 지정합니다.

  1. genericsExample()에서 waterSupply 문자열을 전달하여 Aquarium을 만든 다음 수족관의 waterSupply 속성을 출력합니다.
fun genericsExample() {
    val aquarium2 = Aquarium("string")
    println(aquarium2.waterSupply)
}
  1. 프로그램을 실행하고 결과를 확인합니다.
⇒ string

AquariumT.Any 유형에 제한을 두지 않으므로 String를 포함한 모든 유형을 전달할 수 있습니다. 따라서 전달된 문자열이 결과가 됩니다.

  1. genericsExample()에서 다른 Aquarium을 만들어 waterSupplynull을 전달합니다. waterSupply이 null이면 "waterSupply is null"을 출력합니다.
fun genericsExample() {
    val aquarium3 = Aquarium(null)
    if (aquarium3.waterSupply == null) {
        println("waterSupply is null")
    }
}
  1. 프로그램을 실행하고 결과를 관찰합니다.
⇒ waterSupply is null

Aquarium를 만들 때 null를 전달할 수 있는 이유는 무엇인가요? 이는 기본적으로 T이 null 허용 Any? 유형(유형 계층 구조의 최상위 유형)을 나타내기 때문입니다. 다음은 이전에 입력한 내용과 동일합니다.

class Aquarium<T: Any?>(val waterSupply: T)
  1. null 전달을 허용하지 않으려면 Any 뒤의 ?를 삭제하여 Any 유형의 T을 명시적으로 만드세요.
class Aquarium<T: Any>(val waterSupply: T)

이 컨텍스트에서 Any일반 제약 조건이라고 합니다. 즉, null이 아닌 한 모든 유형을 T에 전달할 수 있습니다.

  1. 실제로 원하는 것은 WaterSupply (또는 그 서브클래스 중 하나)만 T에 전달될 수 있도록 하는 것입니다. AnyWaterSupply로 바꿔 좀 더 구체적인 일반 제약 조건을 정의합니다.
class Aquarium<T: WaterSupply>(val waterSupply: T)

4단계: 추가 확인

이 단계에서는 check() 함수를 알아보고 코드가 예상대로 작동하는지 확인합니다. check() 함수는 Kotlin의 표준 라이브러리 함수입니다. 어설션 역할을 하며 인수가 false로 평가되면 IllegalStateException를 발생시킵니다.

  1. Aquarium 클래스에 addWater() 메서드를 추가하여 물을 추가합니다. check()를 사용하면 물을 먼저 처리하지 않아도 됩니다.
class Aquarium<T: WaterSupply>(val waterSupply: T) {
    fun addWater() {
        check(!waterSupply.needsProcessing) { "water supply needs processing first" }
        println("adding water from $waterSupply")
    }    
}

이 경우 needsProcessing가 true이면 check()에서 예외가 발생합니다.

  1. genericsExample()에서 LakeWaterAquarium를 만드는 코드를 추가한 다음 물을 추가합니다.
fun genericsExample() {
    val aquarium4 = Aquarium(LakeWater())
    aquarium4.addWater()
}
  1. 프로그램을 실행하면 물을 먼저 필터링해야 하므로 예외가 발생합니다.
⇒ Exception in thread "main" java.lang.IllegalStateException: water supply needs processing first
        at Aquarium.generics.Aquarium.addWater(Aquarium.kt:21)
  1. Aquarium에 추가하기 전에 물을 필터링하는 호출을 추가합니다. 이제 프로그램을 실행하면 예외가 발생하지 않습니다.
fun genericsExample() {
    val aquarium4 = Aquarium(LakeWater())
    aquarium4.waterSupply.filter()
    aquarium4.addWater()
}
⇒ adding water from generics.LakeWater@880ec60

위에서는 일반 유형의 기본사항을 다루었습니다. 다음 작업에서는 더 많은 내용을 다루지만 중요한 개념은 일반 제약 조건이 있는 일반 클래스를 선언하고 사용하는 방법입니다.

이 작업에서는 일반적인 사항을 사용하여 in 및 out 유형에 대해 알아봅니다. in 유형은 클래스에만 전달할 수 있고 반환할 수는 없는 유형입니다. out 유형은 클래스에서만 반환될 수 있는 유형입니다.

Aquarium 클래스를 살펴보면 속성 waterSupply을 가져올 때만 일반 유형이 반환되는 것을 알 수 있습니다. 생성자에서 정의하는 경우를 제외하고 T 유형의 값을 매개변수로 사용하는 메서드가 없습니다. Kotlin을 사용하면 정확히 이 경우에 out 유형을 정의할 수 있으며, 유형을 안전하게 사용할 수 있는 위치에 관한 추가 정보를 추론할 수 있습니다. 마찬가지로 메서드에 전달되기만 하고 반환되지 않는 일반 유형에 in 유형을 정의할 수 있습니다. 이를 통해 Kotlin은 코드 안전성을 추가로 확인할 수 있습니다.

inout 유형은 Kotlin의 유형 시스템을 위한 지시어입니다. 전체 유형 시스템을 설명하는 것은 이 부트캠프의 범위를 벗어납니다 (꽤 복잡함). 하지만 컴파일러는 inout로 적절하게 표시되지 않은 유형을 플래그로 지정하므로 이에 대해 알아야 합니다.

1단계: out 유형 정의

  1. Aquarium 클래스에서 T: WaterSupplyout 유형으로 변경합니다.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    ...
}
  1. 동일한 파일에서 클래스 외부에서 WaterSupplyAquarium을 예상하는 함수 addItemTo()를 선언합니다.
fun addItemTo(aquarium: Aquarium<WaterSupply>) = println("item added")
  1. genericsExample()에서 addItemTo()를 호출하고 프로그램을 실행합니다.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    addItemTo(aquarium)
}
⇒ item added

addItemTo()out 유형으로 선언되어 있으므로 Kotlin은 addItemTo()이 일반 WaterSupply으로 형식에 안전하지 않은 작업을 하지 않도록 할 수 있습니다.

  1. out 키워드를 삭제하면 Kotlin에서 유형을 사용하여 안전하지 않은 작업을 하지 않는다고 보장할 수 없으므로 addItemTo()를 호출할 때 컴파일러에서 오류가 발생합니다.

2단계: 'in' 유형 정의

in 유형은 out 유형과 비슷하지만 함수에 전달되기만 하고 반환되지 않는 일반 유형에 사용됩니다. in 유형을 반환하려고 하면 컴파일러 오류가 발생합니다. 이 예에서는 인터페이스의 일부로 in 유형을 정의합니다.

  1. Aquarium.kt에서 WaterSupply로 제한된 일반 T를 사용하는 인터페이스 Cleaner를 정의합니다. clean()의 인수로만 사용되므로 in 매개변수로 만들 수 있습니다.
interface Cleaner<in T: WaterSupply> {
    fun clean(waterSupply: T)
}
  1. Cleaner 인터페이스를 사용하려면 화학 물질을 추가하여 TapWater를 청소하는 Cleaner를 구현하는 클래스 TapWaterCleaner를 만듭니다.
class TapWaterCleaner : Cleaner<TapWater> {
    override fun clean(waterSupply: TapWater) =   waterSupply.addChemicalCleaners()
}
  1. Aquarium 클래스에서 T 유형의 Cleaner를 사용하도록 addWater()를 업데이트하고 물을 정화한 후 추가합니다.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    fun addWater(cleaner: Cleaner<T>) {
        if (waterSupply.needsProcessing) {
            cleaner.clean(waterSupply)
        }
        println("water added")
    }
}
  1. genericsExample() 예시 코드를 업데이트하여 TapWaterCleaner, TapWater이 있는 Aquarium를 만든 다음 클리너를 사용하여 물을 추가합니다. 필요에 따라 클리너를 사용합니다.
fun genericsExample() {
    val cleaner = TapWaterCleaner()
    val aquarium = Aquarium(TapWater())
    aquarium.addWater(cleaner)
}

Kotlin은 inout 유형 정보를 사용하여 코드가 제네릭을 안전하게 사용하도록 합니다. Outin는 기억하기 쉽습니다. out 유형은 반환 값으로 외부로 전달할 수 있고 in 유형은 인수로 내부로 전달할 수 있습니다.

유형 및 out 유형이 해결하는 문제의 종류에 대해 자세히 알아보려면 문서를 참고하세요.

이 작업에서는 일반 함수와 일반 함수를 사용하는 시기를 알아봅니다. 일반적으로 함수가 일반 유형이 있는 클래스의 인수를 취하는 경우 일반 함수를 만드는 것이 좋습니다.

1단계: 일반 함수 만들기

  1. generics/Aquarium.kt에서 Aquarium를 사용하는 함수 isWaterClean()를 만듭니다. 매개변수의 일반 유형을 지정해야 합니다. WaterSupply를 사용하는 방법도 있습니다.
fun isWaterClean(aquarium: Aquarium<WaterSupply>) {
   println("aquarium water is clean: ${aquarium.waterSupply.needsProcessing}")
}

하지만 이렇게 하려면 Aquariumout 유형 매개변수가 있어야 호출할 수 있습니다. 입력과 출력 모두에 유형을 사용해야 하므로 out 또는 in가 너무 제한적인 경우가 있습니다. 함수를 일반화하여 out 요구사항을 삭제할 수 있습니다.

  1. 함수를 일반화하려면 키워드 fun 뒤에 일반 유형 T과 제약 조건(이 경우 WaterSupply)을 사용하여 꺾쇠 괄호를 넣습니다. WaterSupply 대신 T에 의해 제한되도록 Aquarium를 변경합니다.
fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) {
   println("aquarium water is clean: ${!aquarium.waterSupply.needsProcessing}")
}

T는 수족관의 일반 유형을 지정하는 데 사용되는 isWaterClean()의 유형 매개변수입니다. 이 패턴은 매우 일반적이며 잠시 시간을 내어 이 패턴을 살펴보는 것이 좋습니다.

  1. 함수 이름 바로 뒤, 괄호 앞에 꺾쇠괄호로 유형을 지정하여 isWaterClean() 함수를 호출합니다.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    isWaterClean<TapWater>(aquarium)
}
  1. aquarium 인수에서 유형이 추론되므로 유형이 필요하지 않습니다. 따라서 유형을 삭제합니다. 프로그램을 실행하고 출력을 살펴봅니다.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    isWaterClean(aquarium)
}
⇒ aquarium water is clean: false

2단계: 구체화된 유형으로 일반 메서드 만들기

자체 일반 유형이 있는 클래스에서도 메서드에 일반 함수를 사용할 수 있습니다. 이 단계에서는 Aquarium 유형이 WaterSupply인지 확인하는 일반 메서드를 추가합니다.

  1. Aquarium 클래스에서 WaterSupply로 제한되고 waterSupplyR 유형인 경우 true을 반환하는 일반 매개변수 R (T은 이미 사용됨)를 사용하는 메서드 hasWaterSupplyOfType()을 선언합니다. 이는 이전에 선언한 함수와 유사하지만 Aquarium 클래스 내에 있습니다.
fun <R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R
  1. 마지막 R에 빨간색 밑줄이 그어져 있습니다. 마우스 포인터를 오류 위에 올려 놓으면 오류가 무엇인지 확인할 수 있습니다.
  2. is 검사를 실행하려면 Kotlin에 유형이 reified(실제)이며 함수에서 사용할 수 있다고 알려야 합니다. 이렇게 하려면 fun 키워드 앞에 inline를 넣고 일반 유형 R 앞에 reified를 넣습니다.
inline fun <reified R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R

유형이 구체화되면 인라인 처리 후 실제 유형이므로 일반 유형처럼 사용할 수 있습니다. 즉, 이 유형을 사용하여 is 검사를 실행할 수 있습니다.

여기에서 reified를 사용하지 않으면 Kotlin에서 is 검사를 허용할 만큼 유형이 '실제'가 되지 않습니다. 비구체화 유형은 컴파일 시간에만 사용할 수 있으며 프로그램에서 런타임에 사용할 수 없기 때문입니다. 이에 대해서는 다음 섹션에서 자세히 설명합니다.

  1. TapWater을 유형으로 전달합니다. 일반 함수를 호출하는 것과 마찬가지로 함수 이름 뒤에 유형과 함께 꺾쇠 괄호를 사용하여 일반 메서드를 호출합니다. 프로그램을 실행하고 결과를 관찰합니다.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.hasWaterSupplyOfType<TapWater>())   // true
}
⇒ true

3단계: 확장 프로그램 함수 만들기

일반 함수와 확장 함수에도 구체화된 유형을 사용할 수 있습니다.

  1. Aquarium 클래스 외부에서 전달된 WaterSupply이 특정 유형(예: TapWater)인지 확인하는 WaterSupply의 확장 함수 isOfType()를 정의합니다.
inline fun <reified T: WaterSupply> WaterSupply.isOfType() = this is T
  1. 메서드와 마찬가지로 확장 함수를 호출합니다.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.waterSupply.isOfType<TapWater>())  
}
⇒ true

이러한 확장 함수를 사용하면 Aquarium이기만 하면 Aquarium 유형 (Aquarium 또는 TowerTank 또는 기타 하위 클래스)은 중요하지 않습니다. 별 프로젝션 구문을 사용하면 다양한 일치 항목을 편리하게 지정할 수 있습니다. 스타 프로젝션을 사용하는 경우 Kotlin은 안전하지 않은 작업을 하지 않도록 합니다.

  1. 별 투영을 사용하려면 Aquarium 뒤에 <*>를 넣습니다. hasWaterSupplyOfType()Aquarium의 핵심 API에 속하지 않으므로 확장 함수로 이동합니다.
inline fun <reified R: WaterSupply> Aquarium<*>.hasWaterSupplyOfType() = waterSupply is R
  1. hasWaterSupplyOfType() 호출로 변경하고 프로그램을 실행합니다.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.hasWaterSupplyOfType<TapWater>())
}
⇒ true

이전 예에서는 일반 유형을 reified로 표시하고 함수를 inline로 만들어야 했습니다. Kotlin은 컴파일 시간뿐만 아니라 런타임에도 이러한 유형을 알아야 하기 때문입니다.

모든 일반 유형은 컴파일 시간에만 Kotlin에서 사용됩니다. 이렇게 하면 컴파일러가 모든 작업을 안전하게 수행하고 있는지 확인할 수 있습니다. 런타임에는 모든 일반 유형이 삭제되므로 삭제된 유형을 확인하는 것에 관한 이전 오류 메시지가 표시됩니다.

컴파일러는 런타임까지 일반 유형을 유지하지 않고도 올바른 코드를 만들 수 있습니다. 하지만 컴파일러가 지원할 수 없는 일반 유형에 대한 is 검사와 같은 작업을 수행하는 경우가 있습니다. 이러한 이유로 Kotlin에서는 구체화된 유형(실제 유형)을 추가했습니다.

Kotlin 문서에서 구체화된 유형 및 유형 삭제에 대해 자세히 알아볼 수 있습니다.

이 강의에서는 코드를 더 유연하게 만들고 재사용하기 쉽게 하는 데 중요한 일반 유형에 대해 중점적으로 다루었습니다.

  • 코드를 더 유연하게 만들 수 있도록 일반 클래스를 만듭니다.
  • 일반 제약 조건을 추가하여 제네릭과 함께 사용되는 유형을 제한합니다.
  • 일반과 함께 inout 유형을 사용하여 클래스에 전달되거나 클래스에서 반환되는 유형을 제한하는 더 나은 유형 검사를 제공합니다.
  • 일반 유형과 함께 작동하는 일반 함수와 메서드를 만듭니다. 예를 들면 다음과 같습니다.
    fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) { ... }
  • 일반 확장 함수를 사용하여 클래스에 비코어 기능을 추가합니다.
  • 구체화된 유형은 유형 삭제로 인해 필요한 경우가 있습니다. 구체화된 유형은 일반 유형과 달리 런타임까지 유지됩니다.
  • check() 함수를 사용하여 코드가 예상대로 실행되는지 확인합니다. 예:
    check(!waterSupply.needsProcessing) { "water supply needs processing first" }

Kotlin 문서

이 과정의 주제에 관해 자세히 알아보거나 막히는 부분이 있다면 https://kotlinlang.org에서 시작하는 것이 좋습니다.

Kotlin 튜토리얼

https://try.kotlinlang.org 웹사이트에는 Kotlin Koans라는 풍부한 튜토리얼, 웹 기반 인터프리터, 예시가 포함된 전체 참고 문서 세트가 포함되어 있습니다.

Udacity 과정

이 주제에 관한 Udacity 과정을 보려면 프로그래머를 위한 Kotlin 부트캠프를 참고하세요.

IntelliJ IDEA

IntelliJ IDEA 문서는 JetBrains 웹사이트에서 확인할 수 있습니다.

이 섹션에는 강사가 진행하는 과정의 일부로 이 Codelab을 진행하는 학생에게 출제할 수 있는 과제가 나열되어 있습니다. 다음 작업은 강사가 결정합니다.

  • 필요한 경우 과제를 할당합니다.
  • 과제 제출 방법을 학생에게 알립니다.
  • 과제를 채점합니다.

강사는 이러한 추천을 원하는 만큼 사용할 수 있으며 적절하다고 생각되는 다른 과제를 출제해도 됩니다.

이 Codelab을 직접 진행하는 경우 이러한 과제를 자유롭게 사용하여 배운 내용을 테스트해 보세요.

다음 질문에 답하세요

질문 1

다음 중 일반 유형의 이름을 지정하는 규칙은 무엇인가요?

<Gen>

<Generic>

<T>

<X>

질문 2

일반 유형에 허용되는 유형에 대한 제한을 무엇이라고 하나요?

▢ 일반적인 제한

▢ 일반 제약 조건

▢ 명확성

▢ 일반 유형 제한

질문 3

구체화는 다음을 의미합니다.

▢ 객체의 실제 실행 영향이 계산되었습니다.

▢ 클래스에 제한된 항목 색인이 설정되었습니다.

▢ 일반 유형 매개변수가 실제 유형으로 변환되었습니다.

▢ 원격 오류 표시기가 트리거되었습니다.

다음 강의로 진행합니다. 6. 기능 조작

다른 Codelab 링크를 비롯한 과정 개요는 프로그래머를 위한 Kotlin 부트캠프: 과정에 오신 것을 환영합니다를 참고하세요.