Kotlin

[Kotlin] 코틀린 문법 정리 - 04

728x90

 

 

1. 제네릭 (Generic)

fun <T> toFunction(value: T): () -> T = { value }

fun main(args: Array<String>) {
    val func: () -> Int = toFunction<Int>(1170)
    println(func())
}

 

 

2. 여러 타입을 인수로 받기

fun <T, R> toFunction(value1: T, value2: R): () -> T = { value1 }

fun main(args: Array<String>) {
    val func: () -> Int = toFunction<Int, String>(1170, "test")
    println(func())
}

 

 

3. 구체화된(Reified) 타입 매개변수

 타입 매개변수는 is 연산자의 피연산자로 사용할 수 없다.

fun <T> check() {
    val number = 0
    
    if (number is T) // 오류
        println("T는 Int 타입입니다.")
}

타입 매개변수를 is 연산자의 피연산자로 사용하고 싶으면 함수는 inline으로 선언 한 뒤, 타입 매개변수 앞에 reified를 붙여주면 된다.

inline fun <reified T> check() {
    val number = 0
    
    if (number is T)
        println("T는 Int 타입입니다.")
}

 

 

4. 제네릭이 적용된 클래스 / 인터페이스 상속, 구현하기

interface Plusable<T> {
    operator fun plus(other: T): T
}

class Rectangle(val width: Int, val height: Int) : Plusable<Rectangle> {
    
    override fun plus(other: Rectangle): Rectangle {
        return Rectangle(width + other.width, height + other.height)
    }
}

 

 

5. 특정 타입을 상속, 구현하는 타입만 인수로 받기

 특정 타입을 구현하는 타입만 인수로 받으려면, 상속을 할 때처럼 타입 매개변수 뒤에 :타입이름 을 적어준다.

 : 타입1, 타입2 와 같이 적으면 여러 개를 지정할 수도 있다.

interface ValueContainer {
    fun getValue(): Int
}

class AAA : ValueContainer {
    override fun getValue(): Int {
        return 1102
    }
}

fun <T : ValueContainer> T.printValue() {
    println(this.getValue())
}

fun main(args: Array<String>) {
    AAA().printValue()
}

 

 

 

6. in / out 키워드

 out T는 자바의 ? extends T와 같고, in T는 자바의 ? Super T와 같다. 타입 인수를 *로 지정하면, 타입 인수가 무엇이든 상관없이 AAA 타입을 대입할 수 있다.

class AAA<out T>
class BBB<in T>

fun main(args: Array<String>) {
    val aaaSub = AAA<Int>()
    val aaaSup: AAA<Any> = aaaSub

    val bbbSub = BBB<Any>()
    val bbbSup: BBB<Int> = bbbSub
  
  	val star: AAA<*> = aaaSub
}

 

 

 

7. .. 연산자와 범위 표현식

fun main(args: Array<String>) {
    val intRange: IntRange = 1..10
    intRange.forEach { print("$it ") }

    val charRange: CharRange = 'A'..'Z'
		if ('B' in charRange) {
        println("대문자입니다.")
    }  
}

 

 

8. 배열 (Array)

fun main(args: Array<String>) {
    val integers: Array<Int> = arrayOf(10, 20, 30) // 배열 초기화

    println(integers.size)
    println(integers[2])

    for (i in integers) {
        print("$i ")
    }
}
val size = 10
var array = Array(size) { 0 } // 0으로 초기화

 

 

9. 배열을 가변 인수로 활용하기

 vararg 키워드를 사용하여 배열 속에 들어있는 원소들을 가변 인수로 활용할 수 있다. vararg 키워드는 java의 ... 키워드와 동일한 역할을 한다.

fun printAll(vararg tokens: String) {
    for (token in tokens) {
        print("$token ")
    }
}

fun main(args: Array<String>) {
    val numbers = arrayOf("what's", "your", "name?")
    printAll(*numbers)

    printAll("hello", "kotlin")
}
void printAll(String... tokens){
}

 

 

10. 열거 클래스 (Enum)

열거 클래스는 정해진 집합 내의 값을 표현하기 위해 사용한다. 열거 클래스에 들어가는 식별자를 열거 상수(Enum Constant)라고 한다.

enum class Mode {
    SELECTION, PEN, SHAPE, ERASER
}

fun main(args: Array<String>) {
    val mode: Mode = Mode.PEN

    when (mode) {
        Mode.SELECTION -> println("선택 모드")
        Mode.PEN -> println("펜 모드")
        Mode.SHAPE -> println("도형 모드")
        Mode.ERASER -> println("지우개 모드")
    }
}

 

 

11. 열거 클래스에 프로퍼티와 멤버 함수 선언하기

enum class Mode(val number: Int) {
    SELECTION(0),
    PEN(1),
    SHAPE(2),
    ERASER(3);

    fun printNumber() {
        println("모드 $number")
    }
}

fun main(args: Array<String>){
    val mode: Mode = Mode.ERASER

    println(mode.number) // 3
    mode.printNumber() // 모드 3
}

 

 

12. 열거 클래스 활용하기

enum class Mode {
    SELECTION, PEN, SHAPE, ERASER
}

fun main(args: Array<String>) {
    val shapeMode: Mode = Mode.SHAPE
    println(shapeMode.name) // 열거 상수의 이름
    println(shapeMode.ordinal) // 열거 상수의 순서

    val modes: Array<Mode> = Mode.values() // 모든 열거 상수들을 배열로 반환
    for(mode in modes){
        println(mode)
    }

    println(Mode.valueOf("PEN").ordinal)
}

 

 

13. sealed 클래스

 sealed 클래스는 자신의 중첩 클래스에만 상속을 허용하는 클래스이다.

sealed class Outer {
    class One : Outer()
    class Two : Outer()
}

class Three : Outer() // 1.1 버전 이후로는 같은 파일 내에서 가능

fun main(args: Array<String>) {
    val instance: Outer = Outer.One()

    val text: String = when (instance) {
        is Outer.One -> "첫 번째"
        is Outer.Two -> "두 번째"
        is Three -> "세 번째"
    }
}

 

 

14. 위임된 프로퍼티 (Delegated Property)

 코틀린에서는 Getter / Setter 구현을 다른 객체에 맡길 수 있는 문법을 제공한다.

class Sample {
    var number: Int by OnlyPositive()
}

class OnlyPositive {
    private var realValue = 0

    operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
        return realValue
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
        realValue = if (value > 0) value else 0
    }
}

fun main(args: Array<String>){
    val sample = Sample()

    sample.number = -50
    println(sample.number)

    sample.number = 100
    println(sample.number)
}

 프로퍼티를 대리하는 객체는 아래의 두 함수를 멤버 함수로 갖고 있어야 한다.

operator fun getValue(thisRef: Any?, property: KProperty<*>): T
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T)

 

 

15. 클래스 위임 (Class Delegation)

 코틀린에서는 인터페이스의 구현을 다른 클래스에 맡길 수 있는 문법도 제공한다.

interface Interface {
    fun abc()
}

class ClassDelegator : Interface {
    override fun abc() {
        println("기본 구현")
    }
}

class Sample : Interface by ClassDelegator()

fun main(args: Array<String>) {
    Sample().abc()
}

 

 

16. Pair 클래스 : 두 변수를 하나로 묶기

 Pair 클래스를 이용하면 두 변수를 하나로 묶을 수 있다.

fun divide(a: Int, b: Int): Pair<Int, Int> = Pair(a / b, a % b)

fun main(args: Array<String>) {
    val (q, r) = divide(10,3)
    println("몫: $q, 나머지: $r")
}

 

 

17. to 확장 함수 : 두 값을 간단히 Pair로 묶기

 to는 모든 타입에 적용되는 확장 함수이다. to 확장 함수를 이용하여 Pair 객체를 간단히 생성할 수 있다.

fun main(args: Array<String>) {
    val test: Pair<Int, Double> = 10 to 3.14
  
  	println(test) // (10, 3.14)
}

 

 

18. Triple 클래스 : 세 변수를 하나로 묶기

 Triple 클래스는 제네릭을 이용하여 세 가지 타입의 값을 보관한다.

fun calculateCircle(r: Int): Triple<Int, Double, Double> =
    Triple(2 * r, 2 * r * 3.14, r * r * 3.14)

fun main(args: Array<String>) {
    val (diameter, _, area) = calculateCircle(5)

    println("지름 : $diameter")
    println("넓이 : $area")
}

 

 

19. Comparable 인터페이스 : 클래스를 비교 가능하게 만들기

class Node(var value: Int) : Comparable<Node> {
    override fun compareTo(other: Node): Int =
        when {
            this.value < other.value -> -1
            this.value > other.value -> 1
            else -> 0
        }
}

fun main(args: Array<String>) {
    val node1 = Node(10)
    val node2 = Node(20)

    println(node1 < node2) // true
}

 

 

20. ClosedRange 인터페이스 : 닫힌 구간을 표현하는 인터페이스

fun main(args: Array<String>) {
    val intRange = 1..10
    val longRange = 1L..100L
    val floatRange = 1.0..2.0
    val charRange = 'A'..'Z'

    println(intRange.start) // 1
    println(longRange.endInclusive) // 100
    println(floatRange.contains(1.1)) // true
    println(charRange.isEmpty()) // false
}

 ClosedFloatRange와 ClosedDoubleRange 클래스는 in 연산자를 사용할 수 없다.

 

 

21. Iterable 인터페이스 : 클래스가 반복자를 지원하도록 하기

fun main(args: Array<String>) {
    val prog: IntProgression = 3..7

    println(prog.first) // 3
    println(prog.last) // 7
    println(prog.step) // 1

  	prog.forEach { print("$it ") } // 3 4 5 6 7
}

 

 

22. Progression과 관련된 함수

fun main(args: Array<String>) {
    val prog1 = 7 downTo 3 // 7 6 5 4 3
    val prog2 = (3..7).reversed() // 7 6 5 4 3
    println(prog1 == prog2) // true
  
    val prog3 = (1..10) step 3 // 1 4 7 10
    val prog4 = 10 downTo 2 step 3 // 10 7 4
    val prog5 = 2 until 5 // 2 3 4
}

 

 

출처 : 초보자를 위한 코틀린 200제 / 엄민석

 

728x90