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

이 Codelab은 프로그래머를 위한 Kotlin 부트캠프 과정의 일부입니다. Codelab을 순서대로 진행한다면 이 과정을 통해 최대한의 가치를 얻을 수 있을 것입니다. 기존 지식에 따라 일부 섹션을 훑어볼 수도 있습니다. 이 교육 과정에서는 객체 지향 언어를 알고 Kotlin을 배우고자 하는 프로그래머를 대상으로 합니다.

소개

이 Codelab에서는 일반 클래스, 함수, 메서드와 Kotlin의 작동 방식을 소개합니다.

이 과정의 강의는 하나의 샘플 앱을 빌드하는 대신 지식을 쌓을 수 있도록 만들어졌지만 서로 종속되지 않도록 익숙해져 있는 섹션을 훑어볼 수 있습니다. 이러한 사례를 연결하는 데 도움이 되는 예시는 대부분 수족관 테마를 사용합니다. 전체 수족관 이야기를 보려면 프로그래머를 위한 Kotlin 부트캠프 Udacity 과정을 확인하세요.

기본 요건

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

학습할 내용

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

실습할 내용

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

제네릭 소개

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

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

일반 유형을 정의하려면 클래스 이름 뒤에 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. 물 공급 유형의 유형 계층 구조를 만듭니다. 먼저, WaterSupplyopen 클래스로 만들어 서브클래스로 분류할 수 있게 합니다.
  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. main() 함수를 추가하여 genericsExample()를 호출한 다음 프로그램을 실행하고 결과를 관찰합니다.
fun main() {
    genericsExample()
}
⇒ water needs processing: true
water needs processing: false

3단계: 보다 구체적으로 지정

일반적인이란 거의 아무거나 전달할 수 있으며 경우에 따라 문제가 된다는 것을 의미합니다. 이 단계에서는 Aquarium 클래스를 배치할 수 있는 항목을 더 구체적으로 지정합니다.

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

AquariumT.에 제한을 두지 않으므로 String를 비롯한 모든 유형을 전달할 수 있으므로 결과는 전달된 문자열입니다.

  1. genericsExample()에서 waterSupplynull를 전달하는 또 다른 Aquarium를 만듭니다. 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. 정말 원하는 것은 TWaterSupply(또는 서브클래스) 중 하나만 전달되도록 하는 것입니다. 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()에서 LakeWater를 사용하여 Aquarium를 만드는 코드를 추가한 후 물을 추가합니다.
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 유형은 클래스에서만 반환할 수 있는 유형입니다.

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

inout 유형은 Kotlin 유형 시스템의 명령어입니다. 전체 유형 시스템을 설명하는 것은 이 부트캠프의 범위를 벗어나므로 적절하게 설명되어 있습니다. 그러나 컴파일러에서 inout로 표시되지 않은 유형을 적절하게 플래그하므로 이 유형을 알아야 합니다.

1단계: 출력 유형 정의

  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

Kotlin은 addItemTo()이 일반 WaterSupply를 사용하여 안전하지 않은 유형(out 유형으로 선언됨)으로 유형을 획득하지 못하도록 할 수 있습니다.

  1. out 키워드를 삭제하면 Kotlin은 그와 같이 안전하지 않은 작업을 실행하고 있지 않으므로 addItemTo()를 호출할 때 오류를 표시합니다.

2단계: 입력 유형 정의

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() 예시 코드를 업데이트하여 TapWater이 있는 AquariumTapWaterCleaner을 만들고 클리너를 사용하여 물을 추가합니다. 필요한 경우 클리너를 사용합니다.
fun genericsExample() {
    val cleaner = TapWaterCleaner()
    val aquarium = Aquarium(TapWater())
    aquarium.addWater(cleaner)
}

Kotlin은 inout 유형 정보를 사용하여 코드에서 제네릭을 안전하게 사용하도록 합니다. Outin은 기억하기 쉽습니다. out 유형은 반환 값으로 바깥쪽으로 전달되고 in 유형은 인수로 안쪽으로 전달될 수 있습니다.

유형 및 문제 유형의 해결 방법을 자세히 살펴보려면 이 문서를 자세히 살펴보세요.

이 작업에서는 일반 함수와 용도를 알아봅니다. 일반적으로 일반 함수를 만드는 것은 함수가 일반 유형이 있는 클래스의 인수를 사용할 때마다 좋은 생각입니다.

1단계: 일반 함수 만들기

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

하지만 이는 Aquarium가 호출되려면 out 유형 매개변수가 있어야 한다는 것을 의미합니다. 입력 또는 출력 모두에 유형을 사용해야 하기 때문에 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단계: 구체화된 유형으로 일반 메서드 만들기

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

  1. Aquarium 클래스에서 WaterSupply로 제한된 일반 매개변수 R (T가 이미 사용됨)을 사용하고 waterSupplyR 유형인 경우 true를 반환하는 메서드를 선언합니다. 앞서 선언한 함수와 비슷하지만 Aquarium 클래스 내부에 있습니다.
fun <R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R
  1. R에는 빨간색으로 밑줄이 그어져 있습니다. 포인터를 위로 가져가면 어떤 오류인지 확인할 수 있습니다.
  2. is 검사를 하려면 Kotlin에 유형이 구체화되어 있거나 실제이며 함수에 사용될 수 있음을 알려야 합니다. 이렇게 하려면 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)인지 확인하는 isOfType()이라는 확장 함수를 WaterSupply에 정의합니다.
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 또는 다른 서브클래스)은 중요하지 않습니다. star-projection 구문을 사용하면 다양한 일치를 편리하게 지정할 수 있습니다. 별표 프로젝션을 사용하면 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

앞의 예에서 Kotlin은 컴파일 시간뿐만 아니라 런타임에도 알아야 하므로 일반 유형을 reified로 표시하고 함수를 inline로 만들어야 했습니다.

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 부트캠프: 교육 과정에 오신 것을 환영합니다.를 참고하세요.