이 Codelab은 프로그래머를 위한 Kotlin 부트캠프 과정의 일부입니다. Codelab을 순서대로 진행하면 이 과정의 학습 효과를 극대화할 수 있습니다. 기존 지식에 따라 일부 섹션을 훑어볼 수도 있습니다. 이 과정은 객체 지향 언어를 알고 Kotlin을 배우고 싶은 프로그래머를 대상으로 합니다.
소개
이 Codelab에서는 일반 클래스, 함수, 메서드와 Kotlin에서 작동하는 방식을 알아봅니다.
이 과정의 강의는 단일 샘플 앱을 빌드하는 대신 지식을 쌓을 수 있도록 설계되었지만, 서로 반독립적이므로 잘 아는 섹션은 대충 훑어볼 수 있습니다. 이러한 요소를 연결하기 위해 많은 예에서 수족관 테마를 사용합니다. 전체 수족관 스토리를 확인하려면 프로그래머를 위한 Kotlin 부트캠프 Udacity 과정을 확인하세요.
기본 요건
- Kotlin 함수, 클래스, 메서드의 문법
- IntelliJ IDEA에서 새 클래스를 만들고 프로그램을 실행하는 방법
학습할 내용
- 일반 클래스, 메서드, 함수로 작업하는 방법
실습할 내용
- 일반 클래스를 만들고 제약 조건 추가
in
및out
유형 만들기- 일반 함수, 메서드, 확장 함수 만들기
일반 유형 소개
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에서 다루었지만 여기에서 간략하게 복습해 보겠습니다.
- 예를 깔끔하게 유지하려면 src 아래에 새 패키지를 만들고
generics
라고 이름을 지정합니다. - generics 패키지에서 새
Aquarium.kt
파일을 만듭니다. 이렇게 하면 충돌 없이 동일한 이름을 사용하여 항목을 재정의할 수 있으므로 이 Codelab의 나머지 코드는 이 파일에 들어갑니다. - 물 공급 유형의 유형 계층 구조를 만듭니다. 먼저
WaterSupply
을 서브클래스로 분류할 수 있도록open
클래스로 만듭니다. - 불리언
var
매개변수needsProcessing
추가 이렇게 하면 getter 및 setter와 함께 변경 가능한 속성이 자동으로 생성됩니다. - 수돗물에는 물고기에 좋지 않은 첨가물이 포함되어 있으므로
WaterSupply
를 확장하는 서브클래스TapWater
를 만들고needsProcessing
에true
를 전달합니다. TapWater
에서 물을 정화한 후needsProcessing
을false
로 설정하는addChemicalCleaners()
이라는 함수를 정의합니다.needsProcessing
속성은 기본적으로public
이고 서브클래스에서 액세스할 수 있으므로TapWater
에서 설정할 수 있습니다. 완성된 코드는 다음과 같습니다.
package generics
open class WaterSupply(var needsProcessing: Boolean)
class TapWater : WaterSupply(true) {
fun addChemicalCleaners() {
needsProcessing = false
}
}
WaterSupply
의 서브클래스인FishStoreWater
와LakeWater
를 두 개 더 만듭니다.FishStoreWater
는 처리할 필요가 없지만LakeWater
은filter()
메서드로 필터링해야 합니다. 필터링 후에는 다시 처리할 필요가 없으므로filter()
에서needsProcessing = false
을 설정합니다.
class FishStoreWater : WaterSupply(false)
class LakeWater : WaterSupply(true) {
fun filter() {
needsProcessing = false
}
}
추가 정보가 필요한 경우 Kotlin의 상속에 관한 이전 강의를 검토하세요.
2단계: 일반 클래스 만들기
이 단계에서는 다양한 유형의 물 공급을 지원하도록 Aquarium
클래스를 수정합니다.
- Aquarium.kt에서 클래스 이름 뒤에 대괄호 안에
<T>
이 있는Aquarium
클래스를 정의합니다. T
유형의 변경 불가능한 속성waterSupply
를Aquarium
에 추가합니다.
class Aquarium<T>(val waterSupply: T)
genericsExample()
라는 함수를 작성합니다. 클래스의 일부가 아니므로main()
함수나 클래스 정의와 같이 파일의 최상위 수준에 배치할 수 있습니다. 함수에서Aquarium
를 만들고WaterSupply
를 전달합니다.waterSupply
매개변수는 일반적이므로 꺾쇠 괄호<>
안에 유형을 지정해야 합니다.
fun genericsExample() {
val aquarium = Aquarium<TapWater>(TapWater())
}
genericsExample()
에서 코드는 수족관의waterSupply
에 액세스할 수 있습니다.TapWater
유형이므로 유형 변환 없이addChemicalCleaners()
를 호출할 수 있습니다.
fun genericsExample() {
val aquarium = Aquarium<TapWater>(TapWater())
aquarium.waterSupply.addChemicalCleaners()
}
Aquarium
객체를 만들 때 Kotlin에는 유형 추론이 있으므로 꺾쇠 괄호와 그 사이의 내용을 삭제해도 됩니다. 따라서 인스턴스를 만들 때TapWater
를 두 번 말할 필요가 없습니다. 유형은Aquarium
의 인수로 추론할 수 있으며, 여전히TapWater
유형의Aquarium
를 만듭니다.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
aquarium.waterSupply.addChemicalCleaners()
}
- 상황을 확인하려면
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}")
}
genericsExample()
을 호출하는main()
함수를 추가한 다음 프로그램을 실행하고 결과를 확인합니다.
fun main() {
genericsExample()
}
⇒ water needs processing: true water needs processing: false
3단계: 더 구체적으로 만들기
일반적으로 거의 모든 것을 전달할 수 있지만 때로는 문제가 됩니다. 이 단계에서는 Aquarium
클래스에 넣을 수 있는 항목을 더 구체적으로 지정합니다.
genericsExample()
에서waterSupply
문자열을 전달하여Aquarium
을 만든 다음 수족관의waterSupply
속성을 출력합니다.
fun genericsExample() {
val aquarium2 = Aquarium("string")
println(aquarium2.waterSupply)
}
- 프로그램을 실행하고 결과를 확인합니다.
⇒ string
Aquarium
는 T.
Any 유형에 제한을 두지 않으므로 String
를 포함한 모든 유형을 전달할 수 있습니다. 따라서 전달된 문자열이 결과가 됩니다.
genericsExample()
에서 다른Aquarium
을 만들어waterSupply
에null
을 전달합니다.waterSupply
이 null이면"waterSupply is null"
을 출력합니다.
fun genericsExample() {
val aquarium3 = Aquarium(null)
if (aquarium3.waterSupply == null) {
println("waterSupply is null")
}
}
- 프로그램을 실행하고 결과를 관찰합니다.
⇒ waterSupply is null
Aquarium
를 만들 때 null
를 전달할 수 있는 이유는 무엇인가요? 이는 기본적으로 T
이 null 허용 Any?
유형(유형 계층 구조의 최상위 유형)을 나타내기 때문입니다. 다음은 이전에 입력한 내용과 동일합니다.
class Aquarium<T: Any?>(val waterSupply: T)
null
전달을 허용하지 않으려면Any
뒤의?
를 삭제하여Any
유형의T
을 명시적으로 만드세요.
class Aquarium<T: Any>(val waterSupply: T)
이 컨텍스트에서 Any
를 일반 제약 조건이라고 합니다. 즉, null
이 아닌 한 모든 유형을 T
에 전달할 수 있습니다.
- 실제로 원하는 것은
WaterSupply
(또는 그 서브클래스 중 하나)만T
에 전달될 수 있도록 하는 것입니다.Any
을WaterSupply
로 바꿔 좀 더 구체적인 일반 제약 조건을 정의합니다.
class Aquarium<T: WaterSupply>(val waterSupply: T)
4단계: 추가 확인
이 단계에서는 check()
함수를 알아보고 코드가 예상대로 작동하는지 확인합니다. check()
함수는 Kotlin의 표준 라이브러리 함수입니다. 어설션 역할을 하며 인수가 false
로 평가되면 IllegalStateException
를 발생시킵니다.
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()
에서 예외가 발생합니다.
genericsExample()
에서LakeWater
로Aquarium
를 만드는 코드를 추가한 다음 물을 추가합니다.
fun genericsExample() {
val aquarium4 = Aquarium(LakeWater())
aquarium4.addWater()
}
- 프로그램을 실행하면 물을 먼저 필터링해야 하므로 예외가 발생합니다.
⇒ Exception in thread "main" java.lang.IllegalStateException: water supply needs processing first at Aquarium.generics.Aquarium.addWater(Aquarium.kt:21)
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은 코드 안전성을 추가로 확인할 수 있습니다.
in
및 out
유형은 Kotlin의 유형 시스템을 위한 지시어입니다. 전체 유형 시스템을 설명하는 것은 이 부트캠프의 범위를 벗어납니다 (꽤 복잡함). 하지만 컴파일러는 in
및 out
로 적절하게 표시되지 않은 유형을 플래그로 지정하므로 이에 대해 알아야 합니다.
1단계: out 유형 정의
Aquarium
클래스에서T: WaterSupply
를out
유형으로 변경합니다.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
...
}
- 동일한 파일에서 클래스 외부에서
WaterSupply
의Aquarium
을 예상하는 함수addItemTo()
를 선언합니다.
fun addItemTo(aquarium: Aquarium<WaterSupply>) = println("item added")
genericsExample()
에서addItemTo()
를 호출하고 프로그램을 실행합니다.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
addItemTo(aquarium)
}
⇒ item added
addItemTo()
이 out
유형으로 선언되어 있으므로 Kotlin은 addItemTo()
이 일반 WaterSupply
으로 형식에 안전하지 않은 작업을 하지 않도록 할 수 있습니다.
out
키워드를 삭제하면 Kotlin에서 유형을 사용하여 안전하지 않은 작업을 하지 않는다고 보장할 수 없으므로addItemTo()
를 호출할 때 컴파일러에서 오류가 발생합니다.
2단계: 'in' 유형 정의
in
유형은 out
유형과 비슷하지만 함수에 전달되기만 하고 반환되지 않는 일반 유형에 사용됩니다. in
유형을 반환하려고 하면 컴파일러 오류가 발생합니다. 이 예에서는 인터페이스의 일부로 in
유형을 정의합니다.
- Aquarium.kt에서
WaterSupply
로 제한된 일반T
를 사용하는 인터페이스Cleaner
를 정의합니다.clean()
의 인수로만 사용되므로in
매개변수로 만들 수 있습니다.
interface Cleaner<in T: WaterSupply> {
fun clean(waterSupply: T)
}
Cleaner
인터페이스를 사용하려면 화학 물질을 추가하여TapWater
를 청소하는Cleaner
를 구현하는 클래스TapWaterCleaner
를 만듭니다.
class TapWaterCleaner : Cleaner<TapWater> {
override fun clean(waterSupply: TapWater) = waterSupply.addChemicalCleaners()
}
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")
}
}
genericsExample()
예시 코드를 업데이트하여TapWaterCleaner
,TapWater
이 있는Aquarium
를 만든 다음 클리너를 사용하여 물을 추가합니다. 필요에 따라 클리너를 사용합니다.
fun genericsExample() {
val cleaner = TapWaterCleaner()
val aquarium = Aquarium(TapWater())
aquarium.addWater(cleaner)
}
Kotlin은 in
및 out
유형 정보를 사용하여 코드가 제네릭을 안전하게 사용하도록 합니다. Out
와 in
는 기억하기 쉽습니다. out
유형은 반환 값으로 외부로 전달할 수 있고 in
유형은 인수로 내부로 전달할 수 있습니다.
유형 및 out 유형이 해결하는 문제의 종류에 대해 자세히 알아보려면 문서를 참고하세요.
이 작업에서는 일반 함수와 일반 함수를 사용하는 시기를 알아봅니다. 일반적으로 함수가 일반 유형이 있는 클래스의 인수를 취하는 경우 일반 함수를 만드는 것이 좋습니다.
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
요구사항을 삭제할 수 있습니다.
- 함수를 일반화하려면 키워드
fun
뒤에 일반 유형T
과 제약 조건(이 경우WaterSupply
)을 사용하여 꺾쇠 괄호를 넣습니다.WaterSupply
대신T
에 의해 제한되도록Aquarium
를 변경합니다.
fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) {
println("aquarium water is clean: ${!aquarium.waterSupply.needsProcessing}")
}
T
는 수족관의 일반 유형을 지정하는 데 사용되는 isWaterClean()
의 유형 매개변수입니다. 이 패턴은 매우 일반적이며 잠시 시간을 내어 이 패턴을 살펴보는 것이 좋습니다.
- 함수 이름 바로 뒤, 괄호 앞에 꺾쇠괄호로 유형을 지정하여
isWaterClean()
함수를 호출합니다.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
isWaterClean<TapWater>(aquarium)
}
aquarium
인수에서 유형이 추론되므로 유형이 필요하지 않습니다. 따라서 유형을 삭제합니다. 프로그램을 실행하고 출력을 살펴봅니다.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
isWaterClean(aquarium)
}
⇒ aquarium water is clean: false
2단계: 구체화된 유형으로 일반 메서드 만들기
자체 일반 유형이 있는 클래스에서도 메서드에 일반 함수를 사용할 수 있습니다. 이 단계에서는 Aquarium
유형이 WaterSupply
인지 확인하는 일반 메서드를 추가합니다.
Aquarium
클래스에서WaterSupply
로 제한되고waterSupply
이R
유형인 경우true
을 반환하는 일반 매개변수R
(T
은 이미 사용됨)를 사용하는 메서드hasWaterSupplyOfType()
을 선언합니다. 이는 이전에 선언한 함수와 유사하지만Aquarium
클래스 내에 있습니다.
fun <R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R
- 마지막
R
에 빨간색 밑줄이 그어져 있습니다. 마우스 포인터를 오류 위에 올려 놓으면 오류가 무엇인지 확인할 수 있습니다. is
검사를 실행하려면 Kotlin에 유형이 reified(실제)이며 함수에서 사용할 수 있다고 알려야 합니다. 이렇게 하려면fun
키워드 앞에inline
를 넣고 일반 유형R
앞에reified
를 넣습니다.
inline fun <reified R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R
유형이 구체화되면 인라인 처리 후 실제 유형이므로 일반 유형처럼 사용할 수 있습니다. 즉, 이 유형을 사용하여 is
검사를 실행할 수 있습니다.
여기에서 reified
를 사용하지 않으면 Kotlin에서 is
검사를 허용할 만큼 유형이 '실제'가 되지 않습니다. 비구체화 유형은 컴파일 시간에만 사용할 수 있으며 프로그램에서 런타임에 사용할 수 없기 때문입니다. 이에 대해서는 다음 섹션에서 자세히 설명합니다.
TapWater
을 유형으로 전달합니다. 일반 함수를 호출하는 것과 마찬가지로 함수 이름 뒤에 유형과 함께 꺾쇠 괄호를 사용하여 일반 메서드를 호출합니다. 프로그램을 실행하고 결과를 관찰합니다.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
println(aquarium.hasWaterSupplyOfType<TapWater>()) // true
}
⇒ true
3단계: 확장 프로그램 함수 만들기
일반 함수와 확장 함수에도 구체화된 유형을 사용할 수 있습니다.
Aquarium
클래스 외부에서 전달된WaterSupply
이 특정 유형(예:TapWater
)인지 확인하는WaterSupply
의 확장 함수isOfType()
를 정의합니다.
inline fun <reified T: WaterSupply> WaterSupply.isOfType() = this is T
- 메서드와 마찬가지로 확장 함수를 호출합니다.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
println(aquarium.waterSupply.isOfType<TapWater>())
}
⇒ true
이러한 확장 함수를 사용하면 Aquarium
이기만 하면 Aquarium
유형 (Aquarium
또는 TowerTank
또는 기타 하위 클래스)은 중요하지 않습니다. 별 프로젝션 구문을 사용하면 다양한 일치 항목을 편리하게 지정할 수 있습니다. 스타 프로젝션을 사용하는 경우 Kotlin은 안전하지 않은 작업을 하지 않도록 합니다.
- 별 투영을 사용하려면
Aquarium
뒤에<*>
를 넣습니다.hasWaterSupplyOfType()
는Aquarium
의 핵심 API에 속하지 않으므로 확장 함수로 이동합니다.
inline fun <reified R: WaterSupply> Aquarium<*>.hasWaterSupplyOfType() = waterSupply is R
hasWaterSupplyOfType()
호출로 변경하고 프로그램을 실행합니다.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
println(aquarium.hasWaterSupplyOfType<TapWater>())
}
⇒ true
이전 예에서는 일반 유형을 reified
로 표시하고 함수를 inline
로 만들어야 했습니다. Kotlin은 컴파일 시간뿐만 아니라 런타임에도 이러한 유형을 알아야 하기 때문입니다.
모든 일반 유형은 컴파일 시간에만 Kotlin에서 사용됩니다. 이렇게 하면 컴파일러가 모든 작업을 안전하게 수행하고 있는지 확인할 수 있습니다. 런타임에는 모든 일반 유형이 삭제되므로 삭제된 유형을 확인하는 것에 관한 이전 오류 메시지가 표시됩니다.
컴파일러는 런타임까지 일반 유형을 유지하지 않고도 올바른 코드를 만들 수 있습니다. 하지만 컴파일러가 지원할 수 없는 일반 유형에 대한 is
검사와 같은 작업을 수행하는 경우가 있습니다. 이러한 이유로 Kotlin에서는 구체화된 유형(실제 유형)을 추가했습니다.
Kotlin 문서에서 구체화된 유형 및 유형 삭제에 대해 자세히 알아볼 수 있습니다.
이 강의에서는 코드를 더 유연하게 만들고 재사용하기 쉽게 하는 데 중요한 일반 유형에 대해 중점적으로 다루었습니다.
- 코드를 더 유연하게 만들 수 있도록 일반 클래스를 만듭니다.
- 일반 제약 조건을 추가하여 제네릭과 함께 사용되는 유형을 제한합니다.
- 일반과 함께
in
및out
유형을 사용하여 클래스에 전달되거나 클래스에서 반환되는 유형을 제한하는 더 나은 유형 검사를 제공합니다. - 일반 유형과 함께 작동하는 일반 함수와 메서드를 만듭니다. 예를 들면 다음과 같습니다.
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
구체화는 다음을 의미합니다.
▢ 객체의 실제 실행 영향이 계산되었습니다.
▢ 클래스에 제한된 항목 색인이 설정되었습니다.
▢ 일반 유형 매개변수가 실제 유형으로 변환되었습니다.
▢ 원격 오류 표시기가 트리거되었습니다.
다음 강의로 진행합니다.
다른 Codelab 링크를 비롯한 과정 개요는 프로그래머를 위한 Kotlin 부트캠프: 과정에 오신 것을 환영합니다를 참고하세요.