References: Do it! 코틀린 프로그래밍

코틀린에서 연관(Association)관계, 의존(Dependency)관계, 집합(Aggregation)관계, 구성(Composition)관계 에 대해 알아봅니다.

 

 

1. 클래스간의 관계

 

클래스간의 관계는 다음과 같이 표시할 수 있습니다.

 

또한 클래스간의 관계는 다음과 같이 판별할 수 있습니다.

 

 

  • 연관관계: 클래스간의 참조를 유지하는 경우
  • 의존관계: 클래스간의 참조를 유지하지 않는 경우
  • 집합관계: 연관관계에 있으면서 객체의 생명주기가 서로 유지되는 경우
  • 구성관계: 연관관계에 있으면서 객체의 생명주기가 서로 유지되지 않는 경우

 

 

2. 연관(Association)관계

 

연관관계란 두개의 서로 분리된 클래스가 연결을 갖고 있는것 입니다.

단방향 양방향 연결 모두를 포함하며 서로 다른 생명주기를 가집니다.

다음 예시를 통해 연관 관계에 대해 살펴보겠습니다.

 

class Doctor(val name: String){
    fun patientList(p: Patient){
        println("Doctor: $name, Patient: ${p.name}")
    }
}
class Patient(val name: String){
    fun doctorList(d: Doctor){
         println("Patient: $name, Doctor: ${d.name} ")
    }
}
fun main() {
    val doc = Doctor("DoctorA")
    val pat = Patient("PatientA")
    doc.patientList(pat)
    pat.doctorList(doc)
}

 

  • fun patientList(p: Patient){ / fun doctorList(d: Doctor){: 각 클래스에서 서로 인자를 사용해 참조합니다.
  • val doc = Doctor("DoctorA") / val pat = Patient("PatientA"): 객체는 따로 생성함을 보여줍니다.

위의 코드를 실행시키면 다음과 같은 결과를 확인할 수 있습니다.

 

Doctor와 Patient 클래스는 각각 따로 생성되 독립적인 생명주기를 갖게됩니다. 또한 클래스가 서로 참조하고 있으므로 양방향 참조를 가집니다.

서로 참조는 하고 있지만 객체의 생명주기에 영향을 주지않을 때 연관관계라고 합니다.

 

 

3. 의존(Dependency)관계

 

한 클래스가 다른 클래스에 의존되어 영향을 줄 때 의존관계라 합니다. 예를들어 Doctor 클래스를 생성하려고 할 때 먼저 Patient 클래스가 필요하다면 Doctor는 Patient에 의존하는 관계라고 합니다.

다음 예시를 통해 확인해 봅시다.

 

class Patient(val name: String, var id: Int){
    fun doctorList(d: Doctor){
        println("Patient: $name, Doctor: ${d.name} ")
    }
}
class Doctor(val name: String, val p: Patient){
    val customerId: Int = p.id
    fun patientList(){
        println("Doctor: $name, Patient: ${p.name}")
        println("PatientId: $customerId")
    }
}
fun main() {
    val pat = Patient("PatientA", 1)
    val doc = Doctor("DoctorA", pat)
    doc.patientList()
}

 

  • class Doctor(val name: String, val p: Patient): 이전과 달리 Patient 클래스를 주 생성자에서 받고 있습니다.
  • val doc = Doctor("DoctorA", pat): Doctor 클래스를 생성하기 위해선 이미 생성된 Patient 클래스가 필요합니다.

위 코드의 실행결과는 다음과 같습니다.

 

위의 설명과 같이 Doctor 클래스는 Patient 클래스에 의존하게 됩니다.

 

 

4. 집합(Aggregation)관계

 

집합관계는 연관관계와 유사하지만 한 개체가 특정 개체를 소유한 다는 개념이 추가된 것 입니다.

다음 예제를 통해 살펴보겠습니다.

 

class Pond(_name: String, _members: MutableList<Duck>){
    val name: String = _name
    val members: MutableList<Duck> = _members
    constructor(_name: String): this(_name, mutableListOf<Duck>())
}
class Duck(val name: String){ }
fun main(){
    val pond = Pond("myPond")
    val duck1 = Duck("Duck1")
    val duck2 = Duck("Duck2")

    pond.members.add(duck1)
    pond.members.add(duck2)
    for(duck in pond.members){ println(duck.name) }
}

 

  • _members: MutableList<Duck>: Pond 클래스는 여러 Duck 클래스를 가질 수 있습니다.
  • Pond / Duck: 두 클래스는 서로 생명주기에 영향을 미치지 않습니다.
  • add: Pond 클래스에 Duck를 추가합니다.

위 코드의 실행결과는 다음과 같습니다

연못에는 여러마리 오리가 존재할 수 있습니다. 한 오리는 한 연못에만 존재할 수 있습니다.

결국 Pond는 객체 Duck를 소유하게 됩니다.

 

 

5. 구성(Composition)관계

 

구성관계는 집합관계와 유사하지만 특정 클래스가 어느 한 클래스의 부분이 되는 것 입니다.

부분이 된 하위 클래스는 생명주기가 상위 클래스에 종속됩니다. 따라서 상위 클래스가 삭제되면 하위 클래스도 같이 삭제 되게 됩니다.

다음 예제를 통해 살펴보겠습니다.

 

class Engine(power:String){
    fun start() = println("Engine has ben started.")
    fun stop() = println("Engine has ben stopped.")
}
class Car(val name: String, val power: String){
    private var engine = Engine(power)
    fun startEngine() = engine.start();
    fun stopEngine() = engine.stop();
}
fun main() {
    var car = Car("Tico", "100hp")
    car.startEngine()
    car.stopEngine()
}

 

  •  private var engine = Engine(power): 클래스 내부에서 다른 클래스를 생성했습니다.

위의 코드를 실행한 결과는 다음과 같습니다.

 

위 처럼 프로그래밍을 하면 Engine 클래스는 Car클래스와 함께 생성되며 Car클래스에 생명주기도 종속됩니다. Car 클래스가 삭제되면 Engine 객체도 삭제됩니다.

 

이 관계를 구성 관계라고 합니다.

 

 

 

 

반응형

'Programming' 카테고리의 다른 글

[Kotlin] 26. 지연초기화: lateinit  (0) 2019.08.27
[Kotlin] 25. Custom Getter & Setter  (0) 2019.08.27
[Kotlin] 23. 다형성(Polymorphism)  (0) 2019.08.24
[Kotlin] 22. 상속(Inheritance)  (0) 2019.08.24
[Kotlin] 21. 생성자  (0) 2019.08.24

References: Do it! 코틀린 프로그래밍

코틀린에서의 오버로딩(Overloading)과 오버라이딩(Overriding)에 대해 알아봅니다.

 

 

1. 다형성(Polymorphism)

 

클래스를 상속하다보면 하위 클래스에서 상위 클래스와 똑같은 이름의 프로퍼티나 메서드를 지정할 일이 생깁니다.

하위 클래스에서 이름은 같지만 호출 매개변수가 다르거나 전혀 다른 동작의 메서드를 정의할 필요가 있습니다.

이렇게 이름은 같지만 매개변수가 다르거나 다른 동작을 하도록 하는 것을 다형성(Polymorphism)이라 합니다.

 

 

2. 오버로딩(Overloading)

 

오버로딩은 같은 이름의 메서드가 매개 변수만 달리하여 여러번 재정의 되는것을 말합니다.

덧셈에 대한 오버로딩을 다음 예시를 통해 알아봅시다.

 

class Calc{
    fun add(a: Int, b: Int): Int = a + b
    fun add(a: Double, b: Double): Double = a + b
    fun add(a: Int, b: Int, c: Int): Int = a + b + c
    fun add(a: String, b: String): String = a + b
}
fun main() {
    val calc = Calc()
    println(calc.add(1, 2))
    println(calc.add(1.1, 2.2))
    println(calc.add(1, 2, 3))
    println(calc.add("Hello", "Kotlin"))
}

 

Calc 클래스 내에 add라는 동일한 이름을 가진 메서드가 4개 있습니다. 이 메서드는 모두 다른 매개변수를 갖고 있으며 이를 오버로딩이라 합니다.

위 예시의 실행 결과는 다음과 같습니다.

 

3. 오버라이딩(Overriding)

 

오버라이드는 자식 클래스가 상속 받은 부모 클래스에 있는 메서드를 재정의 하는것을 말합니다.

상속을 하는 부모클래스에는 open 키워드가, 자식 클래스에서 오버라이드 하는 함수에는 override 키워드가 사용됩니다.

 

앞서 구현한 Bird 클래스를 사용하여 오버라이드를 사용해보도록 하겠습니다.

 

open class Bird(var name: String, var wing: Int, var beak: String, var color: String){
    fun fly() = println("Fly wing: $wing")
    open fun sing(vol: Int) = println("Sing vol: $vol")
}
class Parrot: Bird{
    var language: String
    constructor(name: String, wing: Int = 2, beak: String, color: String, language: String = "natural"): super(name, wing, beak, color){
    this.language = language
    }
    fun speak() = println("Speak! : $language")
    override fun sing(vol: Int){
        println("I'm a parrot!")
        speak()
    }
}
fun main() {
    val parrot = Parrot(name="myParrot", beak="short", color="multiple")
    parrot.language = "English"
    parrot.sing(5)
}

 

  • 부모 클래스에서 자식클래스에 override 될 함수임을 나타내기 위해 open 키워드를 입력합니다.
  • 자식 클래스에서 오버라이드 된 함수임을 나타내기 위해 override 키워드를 입력합니다.

Parrot는 Bird에서 상속 된 클래스이며 Parrot클래스 내의 sing은 Bird클래스의 sing을 오버라이드 한 메서드 입니다.

 

위 예시의 결과값은 다음과 같습니다.

 

 

 

반응형

'Programming' 카테고리의 다른 글

[Kotlin] 25. Custom Getter & Setter  (0) 2019.08.27
[Kotlin] 24. 클래스 혹은 객체간의 관계  (0) 2019.08.27
[Kotlin] 22. 상속(Inheritance)  (0) 2019.08.24
[Kotlin] 21. 생성자  (0) 2019.08.24
[Kotlin] 20. 예외 처리  (0) 2019.08.24

References: Do it! 코틀린 프로그래밍

코틀린에서 상속을 알아봅니다.

 

 

1. 상속

 

선언한 클래스가 너무 추상적이라 명확한 한 개체를 정의하기에 모호할 경우에 하위에 새로운 클래스를 추가 해 좀 더 명확한 클래스로 만들 수 있습니다.

이럴 때 상속을 사용하게 되며 추상적인 클래스를 좀 더 명확화 시키기 위해 클래스를 상속받아 좀 더 구체적인 클래스를 만듭니다.

 

아래의 예시를 통해 이전에 만든 Bird 클래스를 이용해 파생 클래스를 만들어 보도록 하겠습니다.

 

open class Bird(var name: String, var wing: Int, var beak: String, var color: String){
    fun fly() = println("Fly wing: $wing")
    fun sing(vol: Int) = println("Sing vol: $vol")
}
class Lark(name: String, wing: Int, beak: String, color: String): Bird(name, wing, beak, color){
    fun singHappy() = println("Happy song!")
}
class Parrot: Bird{
    val language: String
    constructor(name: String, wing: Int, beak: String, color: String, language: String): super(name, wing, beak, color){
        this.language = language
    }
    fun speak() = println("Speak! : $language")
}
fun main() {
    val coco = Bird("myBird", 2, "short", "blue")
    val lark = Lark("myLark", 2, "long", "brown")
    val parrot = Parrot("myParrot", 2, "short", "multiple", "Korean")
    lark.singHappy()
    parrot.speak()
    lark.fly()
}

 

  • 클래스를 상속할 수 있도록 open 키워드를 사용합니다.
  • 상속받은 값을 사용할 것이므로 var키워드는 생략할 수 있습니다. 
  • :Bird(프로퍼티)로 Bird 클래스를 상속받습니다. 자식 클래스의 프로퍼티가 부모 클래스와 동일하면 추가적인 초기화 작업은 필요없습니다.
  • :Bird로 Bird 클래스를 상속 받습니다. 프로퍼티를 별도로 명시하지 않았으므로 부 생성자를 통해 프로퍼티를 초기화 해줘야 합니다. language 프로퍼티가 추가되었으므로 부 생성자 내에서 초기화 시켜줍니다.
  • 클래스내의 메서드를 실행시킵니다. lark의 부모 클래스인 Bird에 fly가 정의되어 있으므로 사용할 수 있습니다.

위의 코드를 실행한 결과는 다음과 같습니다.

 

 

 

 

 

반응형

'Programming' 카테고리의 다른 글

[Kotlin] 24. 클래스 혹은 객체간의 관계  (0) 2019.08.27
[Kotlin] 23. 다형성(Polymorphism)  (0) 2019.08.24
[Kotlin] 21. 생성자  (0) 2019.08.24
[Kotlin] 20. 예외 처리  (0) 2019.08.24
[Kotlin] 19. 라벨  (0) 2019.08.23

References: Do it! 코틀린 프로그래밍

코틀린에서의 생성자에 대해 알아봅니다.

 

 

1. 생성자

 

생성자란 클래스를 통해 객체가 만들어 질 떄 기본적으로 호출되는 함수를 말합니다.

생성자를 선언하는 위치는 다음과 같습니다.

 

class 클래스이름 constructor(매개변수){ //주 생성자
    ...
    constructor(매개변수){ ... } //부 생성자
    [constructor(매개변수){ ... }] //추가 부 생성자
    ... 
}

 

코틀린에서는 위의 예시와 같이 여러 생성자를 선언할 수 있습니다.

이에 대해 좀 더 자세히 알아봅시다.

 

 

2. 부 생성자

 

부 생성자는 클래스 본문에 함수처럼 선언하는 생성자를 말합니다.

Bird 클래스를 예시로 부 생성자에 대해 알아보겠습니다.

 

class Bird{
    var name: String
    var wing: Int
    var beak: String
    var color: String
    constructor(name: String, wing: Int, beak: String, color: String){
        this.name = name
        this.wing = wing
        this.beak = beak
        this.color = color
    }
    fun fly() = println("Fly wing: $wing")
    fun sing(vol: Int) = println("Sing vol: $vol")
}
fun main() {
    val coco = Bird("myBird", 2, "short", "blue")
    coco.color = "yellow"
    println("coco.color: ${coco.color}")
    coco.fly()
    coco.sing(3)
}

 

Bird 클래스를 선언하고 내부에 부 생성자를 통해 프로퍼티를 초기화 해주었습니다.

fly와 sing는 메서드입니다.

 

위의 예시를 실행하면 결과는 다음과 같습니다.

입력 인자값을 달리한 채 부 생성자를 여러개 추가할 수 있습니다.

다음 예시를 통해 여러 부 생성자를 선언한 코드를 확인할 수 있습니다.

 

class Bird{
    var name: String
    var wing: Int
    var beak: String
    var color: String
    constructor(name: String, wing: Int, beak: String, color: String){
        this.name = name
        this.wing = wing
        this.beak = beak
        this.color = color
    }
    constructor(name: String, color: String){
        this.name = name
        this.wing = 2
        this.beak = "short"
        this.color = color

    }
    fun fly() = println("Fly wing: $wing")
    fun sing(vol: Int) = println("Sing vol: $vol")
}

 

주의할 점은 선언한 프로퍼티를 모두를 초기화 시켜줘야 한다는 것 입니다.

 

 

 

3. 주 생성자.

 

주 생성자는 클래스 이름과 함께 생성자 정의를 사용하는 방법입니다.

주 생성자는 클래스 이름과 본문 블록 사이에 선언합니다. 다음 예시를 통해 알아보도록 합시다.

 

class Bird constructor(_name: String, _wing:Int, _beak: String, _color: String){
    var name: String = _name
    var wing: Int = _wing
    var beak: String = _beak
    var color: String = _color
    fun fly() = println("Fly wing: $wing")
    fun sing(vol: Int) = println("Sing vol: $vol")
}

 

위와 같이 주 생성자를 정의할 수 있습니다. 이 경우 constructor 키워드는 생략할 수 도 있습니다.

** 단 가시성 지시자 혹은 어노테이션이 있다면 constructor 키워드를 생략할 수 없습니다.

 

 

내부의 프로퍼티를 주 생성자에서 선언 및 초기화를 실행하면 클래스 내부의 내용을 좀 더 간단하게 만들 수 있습니다.

다음 예시를 통해 프로퍼티 선언을 주 생성자 내부에서 진행하도록 하겠습니다.

 

class Bird(var name: String, var wing: Int, var beak: String, var color: String){
    fun fly() = println("Fly wing: $wing")
    fun sing(vol: Int) = println("Sing vol: $vol")
}

 

클래스 본문 내에 메서드만 존재해 더 간략한 클래스를 선언할 수 있습니다.

 

클래스를 생성할 때 필요한 초기화 작업을 클래스 내에 정의해 사용할 수도 있습니다.

다음 예시코드를 통해 클래스가 생성되며 수행되는 초기화 작업에 대해 알아봅시다.

 

class Bird(var name: String, var wing: Int, var beak: String, var color: String){
    init{
        println("###Start init block.###")
        println("Name: $name, Beak: $beak")
        this.sing(3)
        println("###End init block.###")
    }
    fun fly() = println("Fly wing: $wing")
    fun sing(vol: Int) = println("Sing vol: $vol")
}

fun main() {
    val coco = Bird("myBird", 2, "short", "blue")
}

 

클래스 내의 init을 통해 초기화 블럭을 선언할 수 있습니다.

실행 결과는 다음과 같습니다.

객채를 생성하면 초기화 블록 내의 코드가 자동으로 실행되는 것을 확인할 수 있습니다.

 

 

 

 

 

 

반응형

'Programming' 카테고리의 다른 글

[Kotlin] 23. 다형성(Polymorphism)  (0) 2019.08.24
[Kotlin] 22. 상속(Inheritance)  (0) 2019.08.24
[Kotlin] 20. 예외 처리  (0) 2019.08.24
[Kotlin] 19. 라벨  (0) 2019.08.23
[Kotlin] 18. 반복문  (0) 2019.08.23

References: Do it! 코틀린 프로그래밍

코틀린에서의 예외처리 방법에 대해 알아봅니다.

 

 

1. 예외처리.

 

코틀린에서의 예외처리는 try ... catch(e: Exception) ... 으로 처리합니다.

DividedByZero예시를 통해 확인해 보겠습니다.

 

fun main() {
    val a = 1
    val b = 0
    val c: Int
    try{ c = a/b }
    catch(e: Exception){ println("Divided by 0 exception.") }
    finallyprintln("Finally block. ") }
}

 

위의 코드를 실행하면 다음과 같은 결과를 확인할 수 있습니다.

오류가 발생했다는 것을 확인할 수 있습니다.

 

지금은 코드가 짧으니 발생한 위치를 바로 찾을 수 있지만 대규포 프로그램에선 위치를 찾기가 힘들 수 도 있습니다.

이럴떄 스택 추적 기능을 이용하면 예외가 발생한 위치를 찾을 수 있습니다.

코드를 다음과 같이 수정해 봅시다.

 

fun main() {
    val a = 1
    val b = 0
    val c: Int
    try{ c = a/b }
    catch(e: Exception){
        println("Divided by 0 exception.")
        e.printStackTrace()
    }
    finally{ println("Finally block. ") }
}

 

예외 인자에 printStackTrace()를 실행하면 어디서 예외가 발생했는지 추적을 할 수 있습니다.

실행 결과는 다음과 같습니다.

 

 

2. 사용자 예외 클래스

 

코틀린은 여러 예외 클래스를 미리 지정해 둡니다. 미리 지정된 예외 클래스에 유저가 필요한게 없으면 어떻게 할까요?

유저가 원하는 예외클래스를 직접 정의하는 기능을 코틀린은 제공합니다.

 

다음과 같은 예시를 통해 확인해 보겠습니다

 

class InvalidNameException(message: String): Exception(message)
fun validateName(name: String){
    if(name.matches(Regex(".*\\d+.*"))) { throw InvalidNameException("Your name: $name : contains numerals.") }
}
fun main() {
    var name = "smoh92"
    try{ validateName(name) }
    catch(e: InvalidNameException){ println(e.message) }
    catch(e: Exception){ println(e.message) }
}

 

우선 InvalidNameException클래스를 정의합니다.

이후 위에서 정의한 예외 클래스를 사용하는 함수를 정의합니다. 이 함수는 이름에 숫자가 포함되면 InvalidNameException예외를 발생시킵니다.

 

실행 결과는 다음과 같습니다.

 

 

 

 

반응형

'Programming' 카테고리의 다른 글

[Kotlin] 22. 상속(Inheritance)  (0) 2019.08.24
[Kotlin] 21. 생성자  (0) 2019.08.24
[Kotlin] 19. 라벨  (0) 2019.08.23
[Kotlin] 18. 반복문  (0) 2019.08.23
[Kotlin] 17. 조건문  (0) 2019.08.23

References: Do it! 코틀린 프로그래밍

코틀린에서의 라벨에 대해 알아봅니다.

 

 

1. 라벨

 

라벨이란 코드에서 특정 위치를 임의로 표기한것을 말합니다. @를 통해 사용할 수 있습니다.

@position

 

 

2. 람다식에서 라벨의 사용.

 

람다식을 포함한 함수에서 람다식에서의 리턴은 비지역 반환을 초래한다고 지난 글에서 다뤘습니다.

라벨을 이용하면 람다함수의 비지역 반환을 방지할 수 있습니다.

 

다음 예시코드는 라벨을 사용하여 람다함수의 비지역 반환을 방지하는 코드입니다.

 

fun lambdaFun(a: Int, b: Int, out: (Int, Int)->Unit) = out(a, b)
fun returnFun() {
    println("Start of returnFun")
    lambdaFun(10, 20)/** Start of lbl label. */lbl@{ a, b ->
        var result = a+b
        if(result >10) { return@lbl }
        println("result: $result")
    } /** End of lbl label. */
}
fun main() {
    returnFun()
}

 

lbl@ 를 통해 라벨을 지정하였습니다. 라벨이 유효한 범위는 람다식 한 블럭입니다.

실행결과는 다음과 같습니다.

 

 

3. 암묵적 라벨

 

람다 함수에는 위 처럼 명시적으로 라벨을 지정해 줄 수도 있지만 기본적으로 암묵적 라벨이라는것이 존재합니다.

암묵적라벨은 람다함수의 이름으로 자동 설정됩니다.

 

예를 들어 위의 코드를 암묵적 라벨을 사용하는 코드로 바꾸면 다음과 같습니다.

 

fun lambdaFun(a: Int, b: Int, out: (Int, Int)->Unit) = out(a, b)
fun returnFun() {
    println("Start of returnFun")
    lambdaFun(10, 20){ a, b ->
        var result = a+b
        if(result >10) { return@lambdaFun }
        println("result: $result")
    } 
}
fun main() {
    returnFun()
}

 

람다함수의 이름이 자동으로 암묵적 라벨로 설정되어 별도의 라벨 지정 작업 없이 lambdaFun라벨을 사용할 수 있습니다.

 

 

4. 라벨의 응용

 

break/continue에 대해서도 라벨을 사용할 수 있습니다.

 

fun labelBreak (){
    println("labelBreak")
    first@for(i in 1..5){
        second@for(j in 1..5){
            if(j == 3){ break@first }
            println("i: $i, j: $j")
        }
        println("After for j")
    }
    println("After for i")
}

 

위와 같이 이중 루프문을 탈출하는데 쓸 수 있으며

 

fun labelContinue() {
    println("labelContinue")
    first@for(i in 1..5){
        second@for(j in 1..5){
            if(j == 3){ continue@first }
            println("i: $i, j: $j")
        }
        println("After for j")
    }
    println("After for i")
}

 

위와 같이 뒤의 작업을 스킵할 떄 사용할 수도 있습니다.

 

 

 

 

반응형

'Programming' 카테고리의 다른 글

[Kotlin] 21. 생성자  (0) 2019.08.24
[Kotlin] 20. 예외 처리  (0) 2019.08.24
[Kotlin] 18. 반복문  (0) 2019.08.23
[Kotlin] 17. 조건문  (0) 2019.08.23
[Kotlin] 16. 꼬리 재귀 함수  (0) 2019.08.23

References: Do it! 코틀린 프로그래밍

코틀린에서의 반복문에 대해 알아봅니다.

1. for

 

코틀린에서의 for 조건 내부에서는 세미콜론을 사용하지 않습니다.

반복문 for는 다음과 같이 사용합니다

//for(var i = 0; i < 10; i ++)
for(i in 0..9)
//for(var i = 10; i > 0; i --)f
for(i in 10 downTo 1)
//for(var i = 0; i < 10; i = i +2)
for(i in 0..9 step 2)
//for(var i = 10; i > 0; i = i -2)
for(i in 10 downTo 1 step 2)

 

 

 

 

 

반응형

'Programming' 카테고리의 다른 글

[Kotlin] 20. 예외 처리  (0) 2019.08.24
[Kotlin] 19. 라벨  (0) 2019.08.23
[Kotlin] 17. 조건문  (0) 2019.08.23
[Kotlin] 16. 꼬리 재귀 함수  (0) 2019.08.23
[Kotlin] 15. 확장함수와 중위함수  (0) 2019.08.23

References: Do it! 코틀린 프로그래밍

코틀린에서의 조건문에 대해 알아봅니다.

 

 

1. 범위 연산자: in ..

 

조건문에서 ~이상 ~이하 이런조건은 빈번히 사용됩니다. 아래의 예시를 보겠습니다.

else if(score >= 80.0 && score <= 89.9) { ... ]

80점에서 89.9점까지를 찾는 조건문입니다.

코틀린에선 이러한 포함 여부를 확인할 수 있는 in 연산자와 범위를 지정할 수 있는 .. 연산자를 제공합니다.

 

위의 코드는 다음과 같이 다시 쓸 수 있습니다.

else if(score in 80.0 .. 89.9) { ... }

 

 

 

2. 조건문 when

 

if ... else ... 대신 when 조건문을 사용할 수 있습니다. 다른 언어에서의 switch ... case ... 같은 조건문으로 보입니다.

다음 예시를 통해 확인해 봅시다.

when(x) {
    0, 1, 2, 3 -> println("x는 0~3 사이.")
    5 -> println("x는 5.")
    parseInt(str) -> println("함수의 반환값이 일치함.")
    in 6..10 -> println("x는 6~10 사이.")
    !in 1..10 -> println("x는 1~10 사이에 존재하지 않음.")
    else -> println("x는 다른 무언가")
}

 

parseInt(str)처럼 함수의 반환값이 일치하면 when문에 함수의 반환값을 조건으로 사용할수 도 있습니다.

in 6..10 와 같이 앞서 알아본 범위연산자도 사용할 수 있습니다.

 

when조건문엔 인자를 뺴고 사용할 수도 있습니다. 

이 경우엔 내부 조건에 일치조건 뿐 아니라 다른 조건도 사용할 수 있습니다.

다음 예시를 통해 확인하도록 하겠습니다.

when{
    score >= 90.0 -> grade = 'A'
    score in 80.0..89.9 -> grade = 'B'
    score in 70.0..79.9 -> grade = 'C'
    score < 70.0 -> grade = 'F'
}

인자가 있는 when문과 달리 >=와 같은 조건식을 구성할 수 있습니다.

 

당연하지만 when에 Any클래스도 넣을 수 있습니다.

다양한 자료형의 인자를 받아 각각 다른 결과값을 리턴할 수 도 있습니다.

fun cases(obj: Any){
    when(obj){
        1 -> println("Int($obj)")
        "Hello" -> println("String($obj)")
        is Long -> println("Long($obj)")
        !is String -> println("Not String")
        else -> println("Unknown")
    }
}

 

 

 

 

 

반응형

'Programming' 카테고리의 다른 글

[Kotlin] 19. 라벨  (0) 2019.08.23
[Kotlin] 18. 반복문  (0) 2019.08.23
[Kotlin] 16. 꼬리 재귀 함수  (0) 2019.08.23
[Kotlin] 15. 확장함수와 중위함수  (0) 2019.08.23
[Kotlin] 14. 인라인 함수  (0) 2019.08.23

References: Do it! 코틀린 프로그래밍

코틀린에서 꼬리재귀함수에 대해 알아봅니다.

 

 

1. 재귀함수

 

꼬리재귀함수를 알아보기전에 재귀함수를 구현해봅시다.

아래는 팩토리얼의 예시입니다.

fun factorial(n: Int): Long = if(n == 1) n.toLong() else n * factorial(n-1)
fun main() {
    val num = 10
    val result: Long = factorial(num)
    println("Factorial: $num -> $result")
}

위의 factorial함수는 10번 호출되며 이 함수의 문맥을 유지하기위해 factorial()함수 스택 메모리의 10배만큼 메모리를 사용하게 됩니다.

만약 num이 큰값으로 설정된다면 실행 환경에 따라 스택 메모리가 부족해지는 문제를 겪을수 도 있습니다.

 

 

2. 꼬리 재귀 함수

 

코틀린에선 이러한 스택 메모리 문제를 방지하기 위해 꼬리 재귀 함수를 도입했습니다.

일반적인 재귀함수는 함수가 호출되고 계산이 이루어지지만 꼬리 재귀 함수에서는 계산을 먼저 하고 함수 호출이 이루어집니다.

따라서 꼬리 재귀 함수를 이용하기 위해선 계산이 먼저 이루어 지도록 함수를 작성해야 합니다.

코드를 다음과 같이 수정합니다.

tailrec fun factorial(n: Int, run: Int =1): Long = if(n==1) run.toLong() else factorial(n-1, run*n)
fun main() {
    val num = 10
    println("Factorial: $num -> ${factorial(num)}")
}

tailrec 키워드를 이용해 꼬리 재귀 함수를 만들었습니다.

factorial(n-1, run*n)는 인자 안에서 팩토리얼이 중간값을 계산하고 호출합니다.

꼬리 재귀 함수는 값을 그때그때 계산하므로 스택 메모리를 낭비하지 않습니다.

 

아래는 피보나치 수열을 일반 재귀 함수로 만든 예시입니다.

import java.math.BigInteger
fun recFibonacci(n: Int, a: BigInteger, b: BigInteger): BigInteger = if(n==0) a else recFibonacci(n-1, a+b, b)
fun main() {
    val num = 25000
    val first: BigInteger = BigInteger("0")
    val second: BigInteger = BigInteger("1")
    println(recFibonacci(num, first, second))
}

실행을 해보면 다음과 같은 결과값을 확인할 수 있습니다.

스택에 계속 쌓아 계산하므로 오버플로우 에러가 발생합니다.

이번엔 꼬리재귀함수로 수정해보겠습니다.

import java.math.BigInteger
tailrec fun fibonacci(n: Int, a: BigInteger, b: BigInteger): BigInteger = if(n==0) a else fibonacci(n-1, b, a+b)
fun main() {
    val num = 25000
    val first: BigInteger = BigInteger("0")
    val second: BigInteger = BigInteger("1")
    println(fibonacci(num, first, second))
}

실행을 해보면 다음과 같은 결과값을 확인할 수 있습니다.

굉장히 긴 값이지만 정상적으로 동작하는것을 확인할 수 있습니다.

 

 

 

 

반응형

'Programming' 카테고리의 다른 글

[Kotlin] 18. 반복문  (0) 2019.08.23
[Kotlin] 17. 조건문  (0) 2019.08.23
[Kotlin] 15. 확장함수와 중위함수  (0) 2019.08.23
[Kotlin] 14. 인라인 함수  (0) 2019.08.23
[Kotlin] 13. 익명함수  (0) 2019.08.23

jqGrid를 사용하고 있는데 패치 이후부터 컬럼 순서를 변경하면 데이터 표시가 이상하게 된다는 이슈가 왔다.

 

이상하게 꼬였다.

 

위와 같이 헤더 컬럼 이동 후 실제 데이터의 위치가 잘 안맞는 경우 다음과 같은 코드로 확인해보자.

//(grid 선언부의 옵션설정 코드)
....
sortable: function (permutation) { console.log(permutation.join(',')); },
...

sortable에 위와같은 함수를 입력하고 컬럼을 드래그 해보자

 

디버깅 창에 찍힌 결과를 보면 

뭔가 이상한점을 발견할 수 있었다.

colModel에서 문제를 수정후 정상 작동한다.

 

반응형

+ Recent posts