For Programmer

10. 코틀린 - Class(클래스) 본문

Programming language/Kotlin

10. 코틀린 - Class(클래스)

유지광이 2021. 8. 24. 20:50

객체 지향 프로그래밍이란 무엇일까? 그렇다면 우선 객체란 무엇일까? 정답은 이름이 있는 모든 것이다. 

이렇게 말로 들으면 생소하지만 객체지향형 공부를 조금이라도 해보면 어떤 느낌인지 대강 알 수 있다. 절차지향프로그래밍은 간단히 얘기해서 코드를 위에서 아래로 실행하면 문제가 해결된다고 생각해보자. 하지만 축구경기를 하기위해 축구경기1,축구경기2 ... 등등을 만들어야 한다고 생각해보자. 절차지향형 프로그래밍에서는 축구경기1,축구경기2 마다 일일이 장황한 코드를 작성해야 한다.(선수생성,심판생성,경기장생성 등등..)

그렇다면 객체지향형 프로그래밍은 이걸 어떻게 해결할 수 있을 까?

축구경기1을 위해서 선수,심판,경기장,관중을 생성해놨다고 하자. 그러면 우리는 축구경기2를 만들기위해 또 장황한 코드를 작성해야 할까? 답은 아니다. 만들어놓은 선수,심판,경기장,관중을 가져다 쓰면 된다. 이것이 아주아주쉽게 설명하는 객체지향형 프로그래밍의 이점이다. 이처럼 심판,선수,경기장,관중을 만들기 위해 설명서를 작성해야 하는데 이 설명서를 Class에 작성해 놓는다고 생각하면 된다. 이 클래스들을 끌어다 쓴다고 생각하면 된다.

 

클래스를 만드는 방법에는 여러가지 방법이있다.

방법1(주요 생성자 이용)

class Car(var engine:String,var body:String) {

}

-> 주요 생성자에서는 프로퍼티를 선언 및 초기화를 간소화 할수 있게 해준다.(val,var를 선언하였을 시)
주요 생성자에서 val로 생성시 읽기전용(only get)이며 var로 선언시 읽기 쓰기(read, write)가 가능

이렇게 할 경우 자동으로 생성자와 engine,body라는 멤버변수 까지 생성해주기 때문에 engine,body를 클래스내부에서 가져다 쓸 수 있다.

 

방법2(보조 생성자 이용)

//클래스 만드는 방법2(보조 생성자이용(constructor))
class SuperCar{

    var engine:String =""
    var body:String =""
    var door:String =""

    //생성자(멤버 변수는 타입추론이 되지 않으니 타입을 명시해 주어야한다. )
    constructor(endgine:String, body:String, door : String){
        this.body = body
        this.engine = endgine
        this.door  = door
    }
}

-> 보조생성자를 이용하는 방법이다. Java언어와 아주 비슷하며 생성자의 멤버변수는 반드시 타입을 명시해주어야 한다.

또한 보조생성자 내부에서 멤버변수의 값 초기화를 해줄 수 있다.

 

방법3(주 + 보조 이용자 둘다이용)

//클래스 만드는 방법3 -> 1번 방법의 확장(주 + 보조생성자 이용) (생성자가 2개라고 이해하기)
// 주요생성자에 없는것(door)은 임의로 보조생성자에서 초기화를 시켜줘야한다.
class Car1(var engine: String,var body: String){
    var door : String = ""

    //생성자
    constructor(engine: String,body: String,door: String):this(engine, body){
        this.door = door
    }

    fun print2(){
        print(engine+door+body) //모두 정상적으로 출력
    }
}

-> 다음 코드를 보면 engine,body는 var가 붙어있기 때문에 자동으로 값의 초기화가 된다. 또한 멤버변수도 설정이 된다. 단, door도 생성자에 매개변수로 추가하고 싶다면 보조생성자에 this(engine,body)를 이용하여 초기화하고 door변수를 추가적으로 매개변수로 설정하면 된다. 또한 내부에서 this.door = door 로 초기화를 수동으로 반드시 해주어야 한다. 그렇게 되면 print2()함수에서 3가지 변수에 다 접근할 수 있다.

 

방법4(주 + 보조 생성자 이용) <- var,val 사용안할 시 

//클래스 만드는 방법4
// 주요생성자에 없는것(door)은 임의로 보조생성자에서 초기화를 시켜줘야한다.
// engine,body 둘다 class 내부에서 사용하기 위해서는 변수에 초기화 시켜주어야 한다.
class Car3(engine: String,body: String){
    var door : String = ""
    var engine :String = engine
    //생성자
    constructor(engine: String,body: String,door: String):this(engine, body){
        this.door = door
    }

    fun print2(){
        print(engine+door+body) //body는 초기화가 되지 않았기 때문에 출력불가
    }
}

-> 주 생성자에 var,val를 사용하지 않았기 때문에 수동으로 멤버변수를 초기화해주어야한다. 그러나 body는 초기화를 해주지 않았기 때문에 print2()에서 해당 변수를 출력하려고 하면 오류가 발생한다.(접근 불가)

 

방법5(보조 생성자만 여러개 이용) <- 기본 자바 방법과 비슷

//클래스 만드는 방법5 -> 2번 방법의 확장(보조 생성자 2개 이용)
class Car2{
    var engine: String =""
    var body: String =""
    var door: String =""

    constructor(engine: String,body: String){
        this.engine = engine
        this.body = body
    }

    constructor(engine: String,body: String, door:String){
        this.engine = engine
        this.body = body
        this.door = door
    }
}

-> 기본 자바와 비슷한 생성자 선언 방식이다.

 

init사용하기

class RunableCar2{
    var engine : String =""
    var body: String =""

    constructor(engine: String,body: String){
        this.body= body
        this.engine = engine
        println("RunableCar2가 생성자에서 만들어 졌습니다.")
    }

    init { //인스턴스화 될때 가장 먼저 호출 된다.(초기값 설정시 유용)
        println("RunableCar2가 만들어 졌습니다.")
    }

    fun ride(){
        println("탑승 하였습니다.")
    }

    fun drive(){
        println("달립니다 !")
    }

    fun navi(destination:String){
        println("$destination 으로 목적지가 설정되었습니다.")
    }

}

-> init은 보조 생성자보다 먼저 실행되기 때문에 주생성자에서 만약 var,val를 선언하지 않았을 경우 멤버변수를 초기화해야 한다거나 멤버변수의 값 초기화가 필요한경우 init을 사용하면 된다.

RunableCar2가 만들어 졌습니다.
RunableCar2가 생성자에서 만들어 졌습니다.

 

*함수 오버로딩

//오버로딩(overloading) : 함수의 이름은 같으나 매개변수가 다른 경우
class TestClass(){
    val a: Int = 10

    fun test(){
        println("Unit형 fun")
    }
    fun test(a:Int){
        println("Int형 fun")
    }

    fun test(a:Int,b:String){
        println("Int형,String형 fun")
    }
}
val testClass = TestClass()

    testClass.test() //Unit형 fun 출력
    testClass.test(1) //Int형 fun 출력
    testClass.test(2,"hello") //Int형,String형 fun 출력

-> 함수의 이름은 같으나 함수의 매개변수가 다른 경우 이를 오버로딩이라고 표현한다. 이는 생성자도 적용될 수 있다. 생성자의 이름은 다 같으나 매개변수가 다를 경우 문법오류가 나지 않는데 이는 오버로딩을 지원하기 때문이다. 후에 상속에서 나올 오버라이딩과 구분해야 한다. 오버라이딩은 부모 클래스에서 정의해 놓은 함수를 자식이 상속받아 그것을 수정하여 사용할 수 있는 것을 말하는데 오버라이딩은 매개변수를 수정할 수 없기 때문에 함수의 내용만 수정할 수 있다. 따라서 오버로딩과는 약간의 차이가 있다. 이 둘의 개념을 잘 정리해 놓을 필요가 있다.

Comments