Android

코틀린 객체 지향 프로그래밍

9tun 2025. 6. 4. 21:45

클래스와 생성자

클래스 선언

 class user{ }

 

클래스 멤버는 생성자, 변수, 함수, 클래스로 구성됩니다.

생성자 키워드: constructor

 class User {
 	var name = "kkang"
    constructor(name: String){
    	this.name = name
    }
    fun someFun(){
    	println("name: $name")
    }
    class SomeClass{ }
 }

 

위 클래스 생성자를 아래와 같이 작성할 수도 있다. 

class User(val name: String)

이러면 본문 없는 클래스라도, 생성자까지 이미 정의된 것이다.

 

클래스는 객체를 생성해서 사용하고 그 객체로 멤버에 접근합니다.

코틀린에서는 객체를 new를 사용하지 않고 아래와 같이 생성 합니다.

val user = User("kim")
user.someFun()

 객체 생성 시 생성자가 자동으로 호추되므로 소괄호 안에 전달한 인자는 클래스 생성자 매개인자와 맞아야 합니다.

 

주 생성자

코틀린 클래스 생성자는 주 생성자보조 생성자로 구분합니다.

 

주 생성자는 constructor 키워드로 클래스 선언부에 선언합니다.

class User contructor(){
}

주 생성자 선언은 필수는 아니고, 한 클래스에 하나만 가능합니다.

주 생성자의 constructor를 생략할 수 있습니다.

주 생성자를 선언하지 않으면 컴파일러가 매개변수 없는 주 생성자를 자동으로 추가합니다.

class User(){
}

 

주 생성자 매개변수

주 생성자 선언 시 매개변수를 선언할 수도 있습니다. 괄호에 바로 매개인자를 선언했습니다.

class User(name: String, count: Int){
}

위와 같이 선언했으면 클래스 생성할 때 매개변수를 넣어줍니다.

val user = User("구턴", 10)

 

주 생성자 본문 - init

코틀린 클래스 안에서 init 키워드로 지정한 영역은 객체를 생성할 때 자동으로 실행됩니다.

class User(name: String, count: Int){
	init{
    	println("I am init...")
    }
}

fun main(){
	val user = User("9tun", 10)
}

I am init...

 

생성자 매개변수를 클래스 멤버 변수로 선언

생성자에서 사용한 매개변수를 클래스 멤버 변수로 쓰려면 아래와 같이 해야 합니다.

class User(name: String, count: Int){
	// 클래스 멤버 변수 선언
    var name: String
    var count: Int
    
    init{
    	// 클래스 멤버 변수에 생성자 매개변수 대입
		this.name = name
        this.count = count
    }
    fun someFun(){
    	println("name: $name, count: $count")
    }
}

fun main(){
	val user = User("9tun", 10)
    user.someFun()
}

 

위 방법보다 더 간단히 하는 법:

class User(val name: String, val count: Int){
	fun someFun(){
    	println("name: $name, count: $count")
    }
}

fun main(){
	val user = User("9tun", 10)
    user.someFun()
}

보시면 애초에 class 매개인자 자리에 "val"을 사용한 것을 볼 수 있습니다.

 

보조 생성자

클래스 본문에  constructor 키워드로 선언하는 생성자 함수입니다.

본문에 선언하므로 여러 번 선언할 수 있습니다.

class User {
	constructor (name: String){
    	println("constructor(name: String) call...")
    }
    
    constructor (name: String, count: Int){
    	println("constructor(name: String, count: Int) call...")
    }    
}

fun main(){
	val user1 = User("9tun")
    val user2 = User("9tun", 10)
}

 

보조 생성자에 주 생성자 연결

클래스에 주 생성자와 보조 생성자를 모두 선언하면, 보조 생성자에서 주 생성자를 호출해 줘야 합니다.

claa User(name: String){
	constructor(name: String, count: Int): this(name){
    
    }
}

fun main(){
	val user = User("9tun", 10)
}

보조 생성자 선언부에 this(name)을 추가했습니다.

이렇게 하면 보조 생성자로 객체를 생성할 때 주 생성자가 함께 호출됩니다.

 

주 생성자 + 여러 개의 보조 생성자를 선언한 경우, 보조 생성자에서 this()로 다른 보조 생성자를 호출할 수 있습니다.

이때에도 어떤 식으로든 주 생성자를 호출해야 합니다.

claa User(name: String){
	
    constructor(name: String, count: Int): this(name){
    
    }
    
    constructor(name: String, count: Int, email: String): this(name, count){
    
    }  
    
}

fun main(){
	val user = User("9tun", 10, "9tun@daum.net")
}

매개인자가 3개인 보조 생성자를 호출하면,

→ this(name, count)를 통해서 매개인자가 2개인 보조 생성자가 호출되고, 

매개인자 2개인 보조 생성자가 호출되면,

  다시 this(name)을 통해서 주 생성자가 호출된다.

 

클래스 상속

상속과 생성자

open class Super{	// 상속할 수 있게 open 키워드 사용
}
class Sub: Super(){	// Super를 상속받아 Sub 클래스 선언
}

 

✅ 상위 클래스

상속 대상이 되는 클래스

 

✅ 하위 클래스

상속 받는 클래스

 

✅ 상속 가능 키워드

코틀린 클래스는 기본적으로 다른 클래스를 상속할 수 없습니다.

다른 클래스에서 상속할 수 있게 선언하려면 open 키워드를 사용합니다.

open class Super(name: String){
}
class Sub(name: String): Super(name){
}

 

오버라이딩

open class Super{
	var superData = 10
    fun superFun(){
    	println("I am superFun: $superData")
    }
}

class Sub:Super()
fun main(){
	val obj = Sub()
    obj.superData = 20
    obj.superFun()
}

 

때로 상위 클래스에 정의된 멤버를 하위 클래스에서 재정의해야 할 수도 잇습니다.

 

✅ 오버라이딩

상위 클래스에 선언된 변수나 함수를 같은 이름으로 하위 클래스에서 다시 선언하는 것

같은 함수명으로 하위 클래스에서 새로운 로직을 추가하고 싶을 때 오버라이딩을 사용합니다.

open class Super{
	open var someData = 10
    open fun someFun(){
    	println("I am super class function: $someData")
    }
}

class Sub: Super(){
	override var someData = 20
    override fun someFun(){
    	println("I am sub class function : $someData")
    }
}

fun main(){
	val obj = Sub()
    obj.someFun()
}

 

👨‍🏫 코틀린의 오버라이딩 규칙

오버라이딩 허용할 변수나 함수 선언 앞에 open 키워드를 추가

📌 C++에서는 Virtual이란 키워드가 해당 역할을 했었음

접근 제한자

클래스 멤버를 외부 어느 범위까지 이용하게 할지 결정하는 키워드

 

접근 제한자 최상위에서 이용 클래스 멤버에서 이용
public 모든 파일에서 가능 모든 클래스에서 가능
internal 같은 모듈 내에서 가능 같은 모듈 내에서 가능
protected 사용 불가 상속 관계의 하위 클래스에서만 가능
private 파일 내부에서만 이용 클래스 내부에서만 이용

 

open class Super{
	var publicData = 10
    protected var protectedData = 20
    private var privateData = 30
}

class Sub: Super()}
	fun subFun(){
    	publicData++	// 성공
        protectedData++ // 성공
        privateData++	// 실패
    }
}

fun main(){
	val obj = Super()
    obj.publicData++	// 성공
    obj.protectedData++	// 실패
    obj.privatedData++	// 실패
}

 

코틀린 클래스 종류

데이터 클래스

키워드 data로 선언, 자주 사용하는 데이터를 객체로 묶어줍니다.

// 일반 클래스
class NonDataClass(val name:String, val email:String, val age:Int)

// 데이터 클래스
data class DataClass(val name:String, val email:String, val age: Int)

// 일반 클래스와 데이터 클래스를 비교하귀 위해 같은 인자를 전달해 객체를 2개씩 생성
fun main(){
	val non1 = NonDataClass("9tun", "9tun@daum.net", 45)
    val non2 = NonDataClass("9tun", "9tun@daum.net", 45)
    
    val data1 = DataClass("9tun", "9tun@daum.net", 45)
    val data2 = DataClass("9tun", "9tun@daum.net", 45)
}

 

객체 데이터 비교: equals()

println("non data class equals: ${non1.equals(non2)}")
println("data class equals: ${data1.equals(data2)}")

non data class equals: false
data class equals: true

 

일반 클래스 객체 non1은 equals로 data를 비교할 수 없다.

데이터 클래스 객체 data1은 equals로 data 비교가 가능하다.

 

📌 equals() 함수는 주 생성자에 선언한 멤버 변수의 데이터만 비교 대상으로 삼는다.

data class DataClass(val name:String, var email:String, val age:Int){
	lateinit var address: String
    constructor(name: String, email: String, age: Int, address: String):
    	this(name, email, age){
        	this.address = address
        }
}

fun main(){
	val obj1 = DataClass("9tun", "9tun@daum.net", 45, "Seoul")
    val obj2 = DataClass("9tun", "9tun@daum.net", 45, "Busan")
    println("obj1.equals(obj2): ${obj1.equals(obj2)}")
}

obj1.equals(obj2): true

 

👨‍🏫 주 생성자의 멤버 변수값이 같기 때문에 true 라고 나옵니다.

 

객체 데이터 반환: toString()

 

fun main(){
	class NonDataClass(val name: String, val email:String, val age:Int)
    data class DataClass(val name: String, val email: String, val age: Int)
    val non = NonDataClass("9tun", "9tun@daum.net", 45)
    val data = DataClass("9tun", "9tun@daum.net", 45)
    println("non data class toString: ${non.toString()}")
    println("data class toString: ${data.toString()}")
}

non data class toString: com.example.androidlab.P122Kt$main$NonDataClass@37a71e93
data class toString: DataClass(name=9tun, email=9tun@daum.net, age=45)

 

일반 클래스의 toString 출력값은 의미있는 데이터가 아닙니다.

데이터 클래스의 toString 출력값은 객체가 포함하는 멤버 변수의 데이터 출력합니다.

 

📌 toString() 함수도 주 생성자의 매개변수에 선언된 데이터만 출력 대상입니다.

 

오브젝트 클래스

이름없는 클래스, 즉 익명 클래스 생성에 사용합니다.

클래스 선언과 동시에 객체를 생성한다는 의미에서 object 키워드를 사용합니다.

val obj = object{
	var data = 10
    fun some() {
    	println("data: ${data})	
    }
}

fun main() {
	obj.data = 20	// 오류
    obj.some()		// 오류
}

 

object 키워드로 클래스를 선언했지만, 타입을 명시하지 않았으므로, 최상위 타입인 any로 취급됐다.

하지만 any 객체에는 data나 some()이라는 멤버가 없기 때문에 오류가 발생한 것이다.

 

따라서 클래스 타입 object 뒤에 클래스의 상위 클래스/인터페이스를 입력해야 합니다.

그럼 그 상위 클래스 타입으로 이용할 수 있는 것이다.

open class Super{
	open var data = 10
    open fun some(){
    	println("I am super some(): $data")
    }
}

val obj = object: Super(){
	override var data = 20
    override fun some(){
    	println("I am object some(): $data")
    }
}

fun main(){
	obj.data = 30		// 성공
    obj.some()			// 성공
}

 

컴패니언 클래스

멤버 변수나 함수를 클래스 이름으로 접근할 때 사용합니다.

일반적으로 클래스 멤버는 객체를 통해 접근해야 합니다. 하지만 컴패니언 클래스는 객체 생성 없이 접근 가능합니다.

class MyClass{
	var data = 10
    fun some(){
    	println(data)
    }
}

fun main(){
	val obj = MyClass()
    obj.data = 20			// 성공, 객체를 통해 접근
    obj.some()				// 성공, 객체를 통해 접근
    MyClass.data = 20		// 오류, 클래스 이름으로 접근할 수 없음
    MyClass.some()			// 오류, 클래스 이름으로 접근할 수 없음
}

 

위와 같이 클래스 이름으로 접근이 불가한 것을 컴패니언 클래스로 접근해 보자.

class MyClass{
	companion object{
        var data = 10
        fun some(){
            println(data)
        }
    }
}

fun main(){
    MyClass.data = 20		// 성공, 클래스 이름으로 접근
    MyClass.some()			// 성공, 클래스 이름으로 접근
}

📌 C++의 정적(static) 멤버처럼 동작하는 객체입니다.

 

'Android' 카테고리의 다른 글

뷰를 이용한 화면 구성  (0) 2025.06.13
코틀린 유용한 기법  (0) 2025.06.09
고틀린  (0) 2025.06.01
안드로이드 앱 기본 구조  (0) 2025.05.30
개발 환경 준비  (0) 2025.05.27