이 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 RhasWaterSupplyOfType()호출로 변경하고 프로그램을 실행합니다.
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 부트캠프: 과정에 오신 것을 환영합니다를 참고하세요.