이 Codelab은 프로그래머를 위한 Kotlin 부트캠프 과정의 일부입니다. Codelab을 순서대로 진행한다면 이 과정을 통해 최대한의 가치를 얻을 수 있을 것입니다. 기존 지식에 따라 일부 섹션을 훑어볼 수도 있습니다. 이 교육 과정에서는 객체 지향 언어를 알고 Kotlin을 배우고자 하는 프로그래머를 대상으로 합니다.
소개
이 Codelab에서는 일반 클래스, 함수, 메서드와 Kotlin의 작동 방식을 소개합니다.
이 과정의 강의는 하나의 샘플 앱을 빌드하는 대신 지식을 쌓을 수 있도록 만들어졌지만 서로 종속되지 않도록 익숙해져 있는 섹션을 훑어볼 수 있습니다. 이러한 사례를 연결하는 데 도움이 되는 예시는 대부분 수족관 테마를 사용합니다. 전체 수족관 이야기를 보려면 프로그래머를 위한 Kotlin 부트캠프 Udacity 과정을 확인하세요.
기본 요건
- Kotlin 함수, 클래스, 메서드의 구문
- IntelliJ IDEA에서 새 클래스를 만들고 프로그램을 실행하는 방법
학습할 내용
- 일반 클래스, 메서드, 함수로 작업하는 방법
실습할 내용
- 일반 클래스 만들기 및 제약 조건 추가
in
및out
유형 만들기- 일반 함수, 메서드, 확장 함수 만들기
제네릭 소개
많은 프로그래밍 언어와 마찬가지로 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에서 다뤘지만 간단한 리뷰입니다.
- 예시를 깔끔하게 유지하려면 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}")
}
main()
함수를 추가하여genericsExample()
를 호출한 다음 프로그램을 실행하고 결과를 관찰합니다.
fun main() {
genericsExample()
}
⇒ water needs processing: true water needs processing: false
3단계: 보다 구체적으로 지정
일반적인이란 거의 아무거나 전달할 수 있으며 경우에 따라 문제가 된다는 것을 의미합니다. 이 단계에서는 Aquarium
클래스를 배치할 수 있는 항목을 더 구체적으로 지정합니다.
genericsExample()
에서Aquarium
를 만들어waterSupply
의 문자열을 전달한 다음 수족관의waterSupply
속성을 출력합니다.
fun genericsExample() {
val aquarium2 = Aquarium("string")
println(aquarium2.waterSupply)
}
- 프로그램을 실행하고 결과를 관찰합니다.
⇒ string
Aquarium
은 T.
에 제한을 두지 않으므로 String
를 비롯한 모든 유형을 전달할 수 있으므로 결과는 전달된 문자열입니다.
genericsExample()
에서waterSupply
에null
를 전달하는 또 다른Aquarium
를 만듭니다.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
에 전달할 수 있습니다.
- 정말 원하는 것은
T
에WaterSupply
(또는 서브클래스) 중 하나만 전달되도록 하는 것입니다.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
유형은 클래스에서만 반환할 수 있는 유형입니다.
Aquarium
클래스를 살펴보면 waterSupply
속성을 가져올 때만 일반 유형이 반환되는 것을 확인할 수 있습니다. T
유형의 값을 매개변수로 사용하는 메서드는 없습니다 (생성자에서 정의하는 경우를 제외하고). Kotlin을 사용하면 이 경우에 정확하게 out
유형을 정의할 수 있으며 유형이 안전한 위치에 관한 추가 정보를 추론할 수 있습니다. 마찬가지로 메서드로만 전달되며 반환되지 않는 일반 유형에는 in
유형을 정의할 수 있습니다. 이를 통해 Kotlin은 코드 안전성을 추가로 확인할 수 있습니다.
in
및 out
유형은 Kotlin 유형 시스템의 명령어입니다. 전체 유형 시스템을 설명하는 것은 이 부트캠프의 범위를 벗어나므로 적절하게 설명되어 있습니다. 그러나 컴파일러에서 in
및 out
로 표시되지 않은 유형을 적절하게 플래그하므로 이 유형을 알아야 합니다.
1단계: 출력 유형 정의
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
Kotlin은 addItemTo()
이 일반 WaterSupply
를 사용하여 안전하지 않은 유형(out
유형으로 선언됨)으로 유형을 획득하지 못하도록 할 수 있습니다.
out
키워드를 삭제하면 Kotlin은 그와 같이 안전하지 않은 작업을 실행하고 있지 않으므로addItemTo()
를 호출할 때 오류를 표시합니다.
2단계: 입력 유형 정의
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()
예시 코드를 업데이트하여TapWater
이 있는Aquarium
인TapWaterCleaner
을 만들고 클리너를 사용하여 물을 추가합니다. 필요한 경우 클리너를 사용합니다.
fun genericsExample() {
val cleaner = TapWaterCleaner()
val aquarium = Aquarium(TapWater())
aquarium.addWater(cleaner)
}
Kotlin은 in
및 out
유형 정보를 사용하여 코드에서 제네릭을 안전하게 사용하도록 합니다. Out
및 in
은 기억하기 쉽습니다. out
유형은 반환 값으로 바깥쪽으로 전달되고 in
유형은 인수로 안쪽으로 전달될 수 있습니다.
유형 및 문제 유형의 해결 방법을 자세히 살펴보려면 이 문서를 자세히 살펴보세요.
이 작업에서는 일반 함수와 용도를 알아봅니다. 일반적으로 일반 함수를 만드는 것은 함수가 일반 유형이 있는 클래스의 인수를 사용할 때마다 좋은 생각입니다.
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
로 제한된 일반 매개변수R
(T
가 이미 사용됨)을 사용하고waterSupply
가R
유형인 경우true
를 반환하는 메서드를 선언합니다. 앞서 선언한 함수와 비슷하지만Aquarium
클래스 내부에 있습니다.
fun <R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R
R
에는 빨간색으로 밑줄이 그어져 있습니다. 포인터를 위로 가져가면 어떤 오류인지 확인할 수 있습니다.is
검사를 하려면 Kotlin에 유형이 구체화되어 있거나 실제이며 함수에 사용될 수 있음을 알려야 합니다. 이렇게 하려면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
)인지 확인하는isOfType()
이라는 확장 함수를WaterSupply
에 정의합니다.
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
또는 다른 서브클래스)은 중요하지 않습니다. star-projection 구문을 사용하면 다양한 일치를 편리하게 지정할 수 있습니다. 별표 프로젝션을 사용하면 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
앞의 예에서 Kotlin은 컴파일 시간뿐만 아니라 런타임에도 알아야 하므로 일반 유형을 reified
로 표시하고 함수를 inline
로 만들어야 했습니다.
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 부트캠프: 교육 과정에 오신 것을 환영합니다.를 참고하세요.