코틀린(Kotlin) 클래스

Notepad96

·

2020. 9. 18. 19:47

300x250
 
 
 

1. 객체(Object)

서로 관련이 있는 속성(변수)들을 묶어 놓음으로써 고유한 속성들을 갖는 데이터 타입(객체)을 만들어 내는 것.


    val book = object {
        var name = "Horror Stories"
        val price = 32_000
    }
   	book.name = "Horror Stories3"
    // book.price = 25_000  val error
    println(book.name)
    println(book.price)


속성들도 마찬가지로 var로 선언한 name은 값을 변경 할 수 있지만 val로 선언한 price는 값을 변경하고자하면 에러가 발생한다.

객체의 속성들은 '.속성이름' 으로 접근할 수 있다.

 

 

 

 


2. 클래스(Class)

위에서 book이라는 객체(object)를 만들었다.

 

만약 이 book이라는 객체가 여러 개가 필요하다면 그만큼 생성하는 똑같은 코드를 여러 개 선언해야 한다.

 

또한, 속성의 수정 사항이 있을 시 일일이 다 변경해줘야하는 번거로움이 있다.

 

 

이러한 문제들을 해결하기 위하여 클래스(class)를 선언하여 사용한다.

 

클래스는 객체의 껍데기만을 만들어 놓음으로써 더욱 효율적으로 객체를 생성하고 관리할 수 있도록 만든다.

 

클래스 이름은 관례적으로 대문자로 시작한다.

 

 

    class Book {
        var name: String = "Horror Stories"
        var price: Int = 0
        fun info() {
            println("name: ${this.name}, price: $price")
        }
    }
    var book = Book()
    book.name = "Horror Stories2"
    book.price = 32_000
    
    println(book.name)
    println(book.price)
    book.info()

 

 

 

 

2-1. 생성자(constructor)

 

기본 생성자

class Book constructor(var name:String, var price:Int) {}

class Book(var name:String, var price:Int) {
        init {
            println("init 실행: $name, $price")
        }
    }

 

기본 생성자란 클래스 선언 시 같이 선언하는 생성자이다.

 

constructor는 아래처럼 생략하여 작성할 수 있다.

 

기존 Java에서도 생성자의 이름은 클래스의 이름과 같았으며 이점의 착안하여 이를 합침으로서 더욱 코드를 간결하게 만들 수 있다.

 

 

기본 생성자는 따로 코드블럭이 없으므로 무언가 초기화하는 코드를 실행해야한다면 init 블록을 사용해주면 된다.

 

init 블록은 클래스 생성 시 가장 먼저 실행되며 생성자로 받은 인수들의 접근할 수 있다.

 

init 블록은 여러 개 존재할 수 있으며 실행 순서는 선언한 순서대로 실행된다.

 

 

 

보조 생성자

    class Book(var name:String, var price:Int) {
        init {
            println("init 실행: $name, $price")
        }
        
        constructor() : this("empty", 55_000) 
    }
    
    var book = Book()
    var book2 = Book("Horror Stories2", 12_000)

기본 생성자 외에 다른 형태의 생성자가 필요할 경우 보조 생성자를 선언하여 다른 형태로 클래스를 호출하여 객체를 생성할 수 있다.

 

※ 동일한 이름의 다른 매개변수를 같는 것을 오버로딩(overloading) 이라고 한다. 이는 생성자 오버로딩

 

 

위 코드에서는 매개변수가 없는 보조 생성자를 선언하여서 name= "empty" 로 price = 55000으로 초기화되도록 만들었다.

(이 같은 경우에는 디폴트(default) 매개변수를 사용하면 되므로 굳이 보조 생성자를 생성할 필요가 없다.)

 

 

보조 생성자는 기본 생성자와는 달리 코드 블록을 만들 수 있으므로 따로 init블록 없이 초기화 할 수도 있다.

 

물론 위에서는 init블록이 존재하므로 보조 생성자를 사용하여 객체를 생성하여도 init 블록이 먼저 실행된다.

 

 

 

 

 

 

2-2. Getter / Setter

 

프로퍼티마다 정의

    class Book {
        var name = "default"
            get() {
            	println("값 = $price")
                return field
            }
            set(value) {
                field = value
            }
        val price = 0
            get() {
                return field
            }
            /*
            set(value) {
                field = value
            }
            */
    }

 

name과 price 프로퍼티 아래 get()과 set(value)을 각각 선언해준 것을 볼 수 있다.

 

여기서 price는 val을 사용하여 선언하였으므로 불변의 값 즉, 읽기전용의 값이 되므로 set(value)을 작성하면 에러가 발생한다.

 

get과 set 안에서는 각각 해당하는 필드에 접근하고자 하면 field를 사용하면 접근할 수 있으며 다른 프로퍼티는 변수명을 사용하여 접근할 수 있다.

 

 

단, default getter/setter이 존재하여 getter/setter를 선언하지 않을경우 자동으로 정의되므로 추가적인 동작이 필요한 것이 아니라면 작성할 필요없다.

 

 

 

※ 필드(field)와 프로퍼티(property)

 

자바에서 필드라하면 클래스가 갖는 멤버변수를 가리킨다.

 

코틀린에서는 이를 프로퍼티라고 정의하는데 그 이유는 프로퍼티는 단순히 필드뿐 아니라 getter/setter의 개념을 포함하고 있기 때문이다.

 

따라서 위 코드를보면 각 get/set 내부에서는 해당 값에 접근하기 위해서 필드(field)로 접근하는 것을 볼 수 있다.

 

 

 

 

 

오버로딩하여 get/set 정의

    class Book(var name: String, var price: Int) {
        operator fun get(pos:Int):Any {
            return when(pos) {
                0 -> name
                1 -> price
                else -> 0
            }
        }
        operator fun set(pos:Int, value:String) {
            when(pos) {
                0 -> name = value
                1 -> price = value.toInt()
            }
        }
    }

 

이 방법은 '[ ]' 연산자를 오버로딩함으로써 getter/setter 처럼 사용하는 방법이다.

 

접근할 때는 인덱스를 사용하여서 접근한다. 예를들어 Book book이 있을 경우

 

'book[1]' -> price get

'book[0] = "new_name"' -> name set

 

이같이 동작한다.

 

 

 

 

 


3. 상속(Inheritance)

상속은 기존 클래스를 확장하여 다른 고유한 것, 클래스를 정의하는 방법이다.

 

    open class Book(var name: String, var price: Int)
    class FavBook(name: String, price: Int, var reason:String) : Book(name, price)
    
    var book = Book("Horror Stories", 24000)
    var fbook = FavBook("Horror Stories2", 12000, "fun")

 

상속은 기본적으로 못하도록되어 있기 때문에 class 앞에 open 키워드를 붙여 열어줌으로써 상속을 허용할 수 있다.

 

해당 코드는 Book 클래스를 상속받는 FavBook 클래스를 정의하는 것을 볼 수 있다.

 

 

상속을 사용함으로써 중복되는 코드를 줄일 수 있으며 클래스간 연관성을 직관적으로 확인할 수 있다.

 

다중 상속은 불가능하여 상속은 한개의 클래스만 가능하다.

 

 

 

3-1. 오버라이딩(overriding)

상속 관계에 있는 클래스에서 다른 동작을하여 메소드를 재정의 할 필요가 있을 때 사용할 수 있다.

 

(직접 수정해보며 실행해볼 수 있다. 로드가 안된다면 새로고침 or play.kotlinlang.org/)

 

메소드를 재정의 하기 위해서는 부모 클래스에서 재정의할 메소드의 open 키워드를 붙여 열어주어야 한다.

 

코틀린에서는 재정의 할 메소드의 앞에는 override 키워드를 붙이게함으로써 더욱 안정성을 높이었다.

 

 

자식 클래스에서 super 키워드를 사용하여 부모클래스의 접근할 수 있다.

 

 

 

 

 

3-2. 클래스 타입 비교 is

클래스 타입을 비교하기 위해서 is 연산자를 사용한다. 이는 자바의 instanceof 같은 것이다.

    var book = Book("Horror Stories", 25000)
    var fbook = FavBook("Horror Stories2", 12000, "fun")
    
    println(book is Book)
    println(fbook is Book)
    println(book is Any)

 

 

결과는

 

book은 Book 클래스로 생성한 것이므로 => true

fbook은 Book 클래스를 상속받은 FavBook으로 생성한 것이므로 => ture

Any는 모든 클래스의 최상위 클래스이므로 => true

 

이다.

 

 

 

 


4. 참 조

 

 

Classes and Inheritance - Kotlin Programming Language

 

kotlinlang.org

 

300x250