Kotlin

(Kotlin) Class 정의

jaesungLeee 2021. 10. 4. 17:37

2021.10.04 - [[Languages]] - 객체 지향 프로그래밍 (OOP) 에서 이어지는 포스팅입니다.

 

Kotlin에는 사용할 수 있는 Class의 유형이 아주 다양하다. 이번 포스팅은 다양한 유형의 Class 중 일반적인 Class를 정의하는 방법에 대한 포스팅이다.

1. 일반적인 Class

Kotlin에서 Class는 class 키워드를 사용하여 정의한다. 

 

class Soldier { 
    /*
    멤버 변수 (클래스 변수, 인스턴스 변수)
    멤버 함수
    */
}
class Message { // 구현 }
class PracticleSystem { // 구현 }

class MainActivity: AppCompatActivity() {
    override fun onCreate(...) {
        // 인스턴스화
        val soldier1 = Soldier()
        val textMessage = Message()
        val system1 = PracticleSystem()
    }
}

 

위의 예시에서는 세 가지 Class (Soldier, Message, PracticleSystem)을 정의하고, MainActivity에서 각 Class의 인스턴스를 생성한다. Class에는 Class내에서 정의되는 멤버 변수, 멤버 함수를 정의한다. 

아래의 예시는 위의 코드에서 멤버 변수와 멤버 함수를 정의한 예시이다.

 

class Soldier {  
    val name = "John"        // 클래스 변수
    val rank = "Private"
    var missing: Boolean     // 인스턴스 변수

    fun getStatus() {        // 멤버 함수
        var status = "$rank"
        if (missing) {
            status = "$status SOLDIER IS MISSING"
        } else {
            status = "$status, READY FOR DUTY"
        }
        Log.i("STATUS", status)
    }
}

class MainActivity: AppCompatActivity() {
		
    override fun onCreate(...) {
        val soldier1 = Soldier()
        println(soldier1.name)

        soldier1.getStatus()
    }
}

 

멤버 변수 중 클래스 변수는 Class내에 명시적으로 선언된 변수이다.

위의 코드에서 val name = "John"과 같이 Immutable 한 변수 name에 "John"이라는 String Type의 값을 명시적으로 선언할 수 있다.

인스턴스 변수는 Class내에 Data Type만 선언하고 인스턴스를 통해 호출하면서 값을 유동적으로 변경할 수 있는 변수이다. 

Kotlin에서는 Class만 생성하면 멤버 변수와 getter, setter가 자동으로 생성되어 위의 MainActivity Class처럼 사용할 수 있다. 위의 예시에서는 MainActivity Class에서 Soldier Class의 인스턴스를 생성하고 이 인스턴스를 이용하여 Soldier Class의 멤버들을 호출한다.


2. 프로퍼티(Property) 와 커스텀 접근자(Getter / Setter)

Kotlin에서 프로퍼티(Property)는 Java의 필드(field)와 접근자를 통칭한다. 아래의 예시 코드를 통해 설명한다.

 

public class Person {
    private final String name;
    private boolean isMarried;
    
    public Person(String name, boolean isMarried) {
        this.name = name;
        this.isMarried = isMarried;
    }
    
    public String getName() {
        return this.name;
    }
    
    public void setIsMarried(boolean isMarried) {
        this.isMarried = isMarried;
    }
    
    public boolean getIsMarried() {
        return this.isMarried;
    }
}

 

Java는 데이터를 필드에 저장한다. 위의 예시에서 name과 isMarried는 Person Class의 필드에 저장한다고 볼 수 있다. 위의 Java 코드를 Kotlin으로 변환하면 놀라운 결과를 볼 수 있다. 

 

class Person(val name: String, var isMarried: Boolean)

 

앞서 보인 Java 코드를 위와 같이 한 줄로 축약할 수 있다. 즉, Kotlin에서는 클래스 내부에서의 변수 선언은 필드뿐만 아니라 접근자까지 자동으로 생성해 준다. 이 뿐만 아니라 Kotlin에서는 public 접근 제한자가 Default로 선언되어 있어 생략할 수 있다. 

위의 예시에서 val로 선언된 name 프로퍼티는 Immutable 즉, 불변 프로퍼티이다. 이 프로퍼티는 private 필드와 getter를 자동으로 갖는다. var로 선언된 isMarried 프로퍼티는 Muttable 즉, 읽고 쓸 수 있는 가변 프로퍼티이다. 이 프로퍼티는 private 필드와 getter, setter 접근자 모두를 자동으로 갖는다.

 

Kotlin에서 접근자를 사용할때 이 접근자가 단순히 값을 반환하는 접근자가 아닌 특정 연산을 수행한 결과를 반환하는 경우라면 개발자가 직접 커스텀 접근자를 작성할 수 있다. 아래의 예시를 통해 확인할 수 있다.

 

class Soldier {
    val name = "JSLee"
        get() {
            Log.i("GETTER CALLED", "NAME : $name")
            return field
        }
        
    var bullets = 100
        get() {
            Log.i("GETTER CALLED", "VALUE : $bullets")
            return field
        }
        set(value) {
            field = if (value < 0) 0 else value
            Log.i("SETTER CALLED", "NEW VALUE : $field")
        }
}

class MainActivity: AppCompatActivity() {
    override fun onCreate(...) {
        
        val soldier = Soldier()
        
        Log.i("NAME", "${soldier.name}")
        
        Log.i("BULLETS", "${soldier.bullets}")
        
        soldier.bullets--
        Log.i("BULLETS", "${soldier.bullets}")
    }
}

 

위 예시 코드의 로그 출력 결과는 아래와 같다.

 

// I/GETTER CALLED: NAME : JSLEE
// I/NAME: JSLEE
// I/GETTER CALLED: VALUE : 100
// I/BULLETS: 100
// I/GETTER CALLED: VALUE : 100
// I/SETTER CALLED: NEW VALUE : 99
// I/GETTER CALLED: VALUE : 99
// I/BULLETS: 99

 

MainActivity의 첫 번째 로그에서 name 프로퍼티에 대한 접근 시 커스텀 getter가 한번 호출되어 로그를 출력하고 MainActivity의 로그를 출력한다.

MainActivity의 두 번째 로그에서 bullets 프로퍼티에 대한 접근 시 bullets에 대한 커스텀 getter를 호출하여 로그를 출력한 후 MainActivity의 로그를 출력한다.

MainActivity의 세 번째 로그는 조금 다르다. soldier.bullets-- 코드가 실행될 때 bullets에 대한 커스텀 getter를 호출한 후 연산 수행을 위해 커스텀 setter를 호출하여 로그가 getter와 setter 각각 한 번씩 출력된다. 그다음, MainActivity의 세 번째 로그 출력을 위해 다시 getter를 호출하여 로그를 출력하고 마지막으로 세 번째 로그가 출력된다.

 

위의 코드를 살짝 수정하여 주의해야할 점을 설명한다.

 

class Soldier {
    val name = "JSLee"
        get() {
            Log.i("GETTER CALLED", "NAME : $name")
            return this.name
        }
}

class MainActivity: AppCompatActivity() {
    override fun onCreate(...) {
        
        val soldier = Soldier()
        
        Log.i("NAME", "${soldier.name}")
    }
}

 

위와 같이 name 프로퍼티에 "JSLEE"라고 값을 선언했을 때 this.name을 retrun 하게 되면 아래와 같은 에러를 볼 수 있다.

Initializer is not allowed here because this property has no backing field

프로퍼티에 값을 저장해 놓은 상태에서 getter를 사용하기 위해서는 field 접근자를 통해 return을 해야 한다. 하지만, 값이 저장되어있지 않은 상태에서 getter를 통해 값을 저장한다면 아래처럼 값을 저장할 수 있다. 

 

class Soldier {
    val name: String
    	get() = "JSLEE"
}

class MainActivity: AppCompatActivity() {
    override fun onCreate(...) {
        
        val soldier = Soldier()
        
        Log.i("NAME", "${soldier.name}")
    }
}

 


3. 생성자 (Constructor)

Kotlin의 Class는 하나의 Primary Constructor여러 개의 Secondary Constructor를 가질 수 있다. 여기서, Primary Constructor는 Class 헤더에 위치하며, 접근 제한자나 특별한 Annotation이 없다면 Constructor 키워드를 생략할 수 있다. 아래의 예시를 통해 확인한다.

 

class Person constructor(firstName: String) {   }

class Person(firstName: String){   }

 

또한, Primary Constructor에는 어떠한 코드도 포함될 수 없다. 따라서, 초기화하는 코드는 init 키워드 블록을 이용하여 작성할 수 있다. 아래의 예시는 위의 코드를 재작성한다.

 

class Person(firstName: String) {
    
    init {
        println("Primary Constructor Initialized With $firstName")
    }
}

fun main(args: Array<String>) {
    val person = Person("JSLEE")
}

// Primary Constructor Initialized with JSLEE

 

위 코드에서 person 인스턴스 초기화 시, Class에 선언된 프로퍼티와 init 블록은 동등한 우선순위를 가져 선언된 순서대로 수행된다. 하지만, Primary Constructor를 통해 프로퍼티에 대한 선언과 초기화를 아래 예시처럼 동시에 진행할 수도 있다.

 

class Person(val firstName: String, val lastName: String, var age: Int) {   }

 

앞서 설명한 것과 같이 kotlin은 여러 개의 Secondary Constructor도 가질 수 있다. Secondary Constructor는 Primary Constructor와는 달리 생략할 수 없다. 따라서, 아래처럼 constructor라는 키워드 블록을 이용하여 생성할 수 있다.

 

class Person {
    var children = mutableListOf<Person>()
    constructor(parent: Person) {
        parent.childern.add(this)
    }
}

 

위의 코드는 Primary Constructor가 없고 Secondary Constructor만 가지는 경우의 예시이다. 만약, Primary Constructor와 Secondary Constructor가 공존하는 Class를 생성한다면 Secondary Constructor는 this() 생성자를 이용하여 Primary Constructor에 위임할 수 있다.

 

class Meeting(val day: String, val person: String) {
    var time = "To Be Decided"
    
    val text = "For meeting, meeting2 Instance"
    
    constructor(day: String, person: String, time: String): this(day, person) {
        this.time = time
        println("For meeting2 Instance")
    }
}

fun main(args: Array<String>) {
    println("[meeting]")
    val meeting = Meeting("Thursday", "Bob")
    println("${meeting.text}")
    
    println("")
    
    println("[meeting2]")
    val meeting2 = Meeting("Sunday", "JSLEE", "3PM")
    println("${meeting2.text}")
}

 

위 코드에서는 Secondary Constructor 생성과 함께 this() 생성자를 이용하여 Primary Constructor에 위임하고 있다. this(day, person)을 생략한다면 컴파일 에러가 나타난다. 위 코드의 출력 결과는 아래와 같다.

 

// [meeting]
// For meeting, meeting2 Instance
// 
// [meeting2]
// For meeting2 Instance
// For meeting, meeting2 Instance

References

https://www.udemy.com/course/android-app-development-bootcamp-with-kotlin-masterclass/

 

Android App Development Bootcamp with Kotlin - Masterclass

Learn Android 11 App Development From Beginner to Advanced Developer. Master Android Studio and build your first app now

www.udemy.com

https://iosroid.tistory.com/61

 

코틀린(Kotlin) 클래스(Class) : 프로퍼티 와 필드 (Properties and Fields)

코틀린 클래스의 프로퍼티(property) 와 필드(field) 에 대해 살펴보자. 원문 https://kotlinlang.org/docs/reference/properties.html 을 보며 정리. Declaring Properties property 는 var 또는 var ..

iosroid.tistory.com

https://umbum.dev/591

 

[Kotlin] 프로퍼티, 커스텀 접근자(getter, setter)

프로퍼티 = 필드 + 접근자 클래스 내부의 변수 선언은 자바에서는 필드 선언을 의미하지만 코틀린에서는 프로퍼티 선언을 의미한다. 즉, 필드 뿐만 아니라 접근자 메서드도 알아서 생성해준다

umbum.dev

https://wooooooak.github.io/kotlin/2019/05/24/property/

 

코틀린 프로퍼티(kotlin property) · 쾌락코딩

코틀린 프로퍼티(kotlin property) 24 May 2019 | lambda 프로퍼티 접근자 코틀린은 프로퍼티를 언어 기본 기능으로 제공한다. 프로퍼티란 무엇일까? 프로퍼티란 필드와 접근자를 통칭하는 것이다. 일반적

wooooooak.github.io

https://kotlinlang.org/docs/classes.html#constructors

 

Classes | Kotlin

 

kotlinlang.org