안녕하세요 이번 시간에는 코틀린에서 클래스 정의하는 방법에 대하여 개꿀떡 해보겠습니다.
1. 클래스 내부구조
- 자바와 마찬가지로 "class" 키워드 다음에 클래스명이 오고 그다음에 클래스 본문이 오는 형태로 정의.
- 클래스 본문은 멤버 정의가 들어있는 블록.
- 코틀린에서는 멤버변수라는 말 대신 "프로퍼티"라고 하고 기본 접근자 메서드는 자동으로 구현.
- "val 프로퍼티"는 불변의 값으로 getter가 자동으로 구현.
- "var 프로퍼티"는 가변 값으로 getter와 setter가 자동으로 구현.
- 클래스를 접근하기위해서 생성된 인스턴스를 "객체"라고 함.
- 클래스 내부에서는 "this" 키워드를 사용해서 수신 객체를 참조 할 수 있다.
- this 생락도 가능하지만 반드시 명시 해야할 경우도 있다. (어떤 클래스의 프로퍼티와 메서드 파라미터 이름이 같은 경우)
- 클래스 인스턴스의 프로퍼티나 메서드를 사용하려면 우선 인스턴스를 명시적으로 생성해야 한다.
- 생성자 호출을 사용하면 프로그램이 새 인스턴스에 대한 힙 메모리를 할당한 다음, 인스턴스의 상태를 초기화해주는 생성자 코드를 호출해준다.
- 코틀린 클래스는 디폴트로 public이다.(internal이나 private 설정 가능)
-
① vs Java
- 코틀린에서 클라이언트(호출하는쪽) 코드를 바꾸지 않아도 원하는대로 프로퍼트의 구현을 바꿀 수 있기 때문에 코틀린 프로퍼티는 캡슐화에 위배되지 않는다.
- 자바에서는 클래스를 어느곳에서 쓸 수 있게 하려면 명시적으로 public 변경자를 붙여야한다.
② 예시
< 코드 - class.kt >
package com.example.kotlinbase.DefClass
class Person {
var sFirstName: String = "" // 프로퍼티
var sFamilyName: String = ""
var iAge: Int = 0 // var 프로퍼티
val sGender: String = "남성" // val 프로퍼티
fun fullName() = "$sFirstName $sFamilyName"
fun fullName2() = "${this.sFirstName} ${this.sFamilyName}" // this 키워드 사용하여 수신 객체 참조
fun showMe() {
println("저의 이름은 ${fullName()} 이며 나이는 $iAge 입니다.")
}
// 메서드의 파라미터와 클래스의 프로퍼티의 이름이 같은경우 this 명시
fun setName(sFirstName: String, sFamilyName: String) {
this.sFirstName = sFirstName
this.sFamilyName = sFamilyName
}
}
< 코드 - main.kt >
package com.example.kotlinbase.DefClass
fun showAge(p: Person) = println(p.iAge) // 'p'는 수신객체, 프로퍼티 쓰기
// 프로퍼티 읽기
fun readAge(p: Person) {
p.iAge = readLine()!!.toInt()
}
fun main() {
val p = Person() // Person 클래스 인스턴스 생성
p.sFirstName = "꿀떡"
p.sFamilyName = "개"
p.iAge = 32
p.showMe()
}
< 결과 >
2. 생성자
- 클래스 인스턴스를 초기화해주고 인스턴스 생성 시 호출되는 특별한 함수다.
- 클래스 헤더의 파라미터 목록을 "주생성자"라고 한다.
- 초기화 블록이란 "init" 키워드 앞에 붙은 블록이다.
- 초기화 블록은 return문에 들어가지 못한다.
- 보통 프로퍼티 값을 정의 시 초기화 해주지만 하나의 식으로 표현하기 어려울 때 초기화 블록을 사용한다.
- 모든 프로퍼티는 확실 초기화 되는지 확인해야 한다.
- 주생성자는 프로퍼티 초기화나 init블록 밖에서 사용 하지 못한다.
- 코틀린에서는 "val / var"를 붙여 간단히 주생성자 파라미터의 값을 멤버 프로퍼티로 만들수있다.
- 함수와 마찬가지로 주생성자 파라미터에 "디폴트 값"과 "vararg" 키워드 사용이 가능하다.
- "부생성자" 키워드인 "constructor"를 사용하면 클래스의 인스턴스를 서로 다른 방법으로 초기화가 가능하다.
- 부생성자는 return문을 사용할 수 있지만 반환타입 지정이 불가능하다.
- 부생성자는 기본적으로 Unit 타입 값을 반환한다.
- 주생성자 없이 부생성자만 있을 경우 부생성자 본문을 실행하기 전 프로퍼티 초기화와 init블록을 먼저 실행시킨다.
- 부생성자는 블록 대신 콜론( : )을 넣어 코드 작성이 가능하다.(함수 처럼)
- 부생성자 파라미터는 "val / var"를 붙일 수 없다.
① vs Java
- 코틀린에서 생성자를 호출할 때 Java의 new 같은 특별한 키워드를 사용하지 않는다.
② 예시
< 코드 - class1.kt >
package com.example.kotlinbase.DefClass
// 클래스의 주생성자 표현
class Person2(sFullName: String) {
val sFirstName: String
val sFamilyName: String
// 주생성자는 프로퍼티 초기화나 init블록 밖에서 사용 불가
// sFullName = ""
// fun readFullName() = println("주생성자는 $sFullName 입니다.")
// 초기화 블록
init {
val sName = sFullName.split(" ")
if(sName.size != 2) {
// 초기화 블록에서는 return 사용불가
// return
throw IllegalArgumentException("유효하지 않는 성명입니다.")
} else {
sFirstName = sName[0]
sFamilyName = sName[1]
}
}
}
< 코드 - class2.kt >
package com.example.kotlinbase.DefClass
// 클래스의 주생성자 표현2(주생성자를 프로퍼티로 사용 가능), 주생성자 디폴트 값 사용
class Person3(val sFirstName: String, val sFamilyName: String = "") {
fun fullName() = "$sFirstName $sFamilyName"
fun printFirstName() {
// 주생성자는 프로퍼티 초기화나 init블록 밖에서 사용 가능
println(sFirstName)
}
}
< 코드 - class3.kt >
package com.example.kotlinbase.DefClass
// 클래스의 주생성자에 vararg 사용
class Room(vararg val p3: Person3) {
fun showNames() {
for (person in p3) println(person.fullName())
}
}
< 코드 - class4.kt >
package com.example.kotlinbase.DefClass
// 부생성자 예시
class Person4 {
val sFirstName: String
val sFamilyName: String
// 부생성자 형태
constructor(sFirstName: String, sFamilyName: String) {
this.sFirstName = sFirstName
this.sFamilyName = sFamilyName
}
// 부생성자에는 val/var 사용시 오류
// constructor(val sFullName: String) {
constructor(sFullName: String) {
val sName = sFullName.split(" ")
if(sName.size != 2) {
throw IllegalArgumentException("유효하지 않는 성명입니다 : $sFullName")
} else {
sFirstName = sName[0]
sFamilyName = sName[1]
}
}
}
< 코드 - main.kt >
package com.example.kotlinbase.DefClass
fun main() {
println("----------결과 1------------")
val p2 = Person2("꿀떡 개") // 새 인스턴스 생성
println(p2.sFirstName)
println("----------결과 2------------")
val p3 = Person3("꿀떡", "개") // 새 인스턴스 생성
p3.printFirstName()
println("----------결과 3------------")
val r = Room(Person3("꿀떡"), Person3("꿀떡", "개")) // 주생성자에 vararg 있을 경우
r.showNames()
println("----------결과 4------------")
val p4_1 = Person4("꿀떡", "개")
val p4_2 = Person4("꿀떡 개")
println(p4_1.sFamilyName)
println(p4_2.sFamilyName)
}
< 결과 >
3. 멤버 가시성
- 클래스 멤버마다 다른 가시성으로 지정 가능하다.
- "public"은 멤버를 어디서나 볼 수 있다.
- "internal"은 멤버를 멤버가 속한 클래스가 포함된 모듈 내부에서만 볼 수 있다.
- "protected"는 멤버를 멤버가 속한 클래스와 멤버가 속한 클래스의 모든 하위 클래서에서 볼 수 있다.
- "private"는 멤버를 멤버가 속한 클래스 내부에서만 볼 수 있다.
- 주생성자의 가시성을 지정하려면 "constructor" 키워드를 꼭 명시 해야한다.
① 예시
< 코드 - class1.kt >
package com.example.kotlinbase.DefClass
// 멤버변수의 가시성 예시
class Person5(private val sFirstName: String,
private val sFamilyName: String) {
fun sFullName() = "$sFirstName $sFamilyName"
}
< 코드 - class2.kt >
package com.example.kotlinbase.DefClass
// 기본 주생성자 형태
class Person6 constructor(val iAge: Int) {
fun showAge() = println("내 나이는 $iAge 입니다.")
}
// 기본 주생성자 형태에서 일반적으로 constructor 생략 해서 사용
class Person7(val iAge: Int) {
fun showAge() = println("내 나이는 $iAge 입니다.")
}
// 주생성자의 가시성을 지정하게 되면 "constructor" 생략 불가능
class Person8 private constructor(val iAge: Int) {
fun showAge() = println("내 나이는 $iAge 입니다.")
}
< 코드 - main.kt >
package com.example.kotlinbase.DefClass
fun main(){
println("----------결과 1------------")
val p5 = Person5("꿀떡", "개")
// println(p5.sFirstName) // 오류
println(p5.sFullName()) // 사용가능
println("----------결과 1------------")
val p6 = Person6(32)
val p7 = Person7(32)
// val p8 = Person8(32) // 주생성자를 접근 못하게 private 사용했으므로 다른곳에서 사용시 오류 발생!
p6.showAge()
p7.showAge()
}
< 결과 >
4. 내포된 클래스
- 코틀린 클래스는 함수, 프로퍼티, 생성자 외에 다른 클래스도 멤버로 가질 수 있다.
- 내포된 클래스도 여러 가시성을 지정할 수 있다.
- 내포된 클래스에 "inner"키워드를 붙이면 외부 클래스의 현재 인스턴스에 접근 가능하다.
- inner 클래스 생성자 호출 시 this 생략 가능
- inner 클래스 본문에서 외부클래스 인스턴스를 가리켜야 한다면 this 사용해야한다.(this@외부클래스이름)
① vs Java
- 코틀린에서 바깥쪽 클래스는 자신에게 내포된 클래스의 비공개 멤버에 접근할 수 없다.
- 코틀린에서는 내포된 클래스가 내부클래스가 되려면 inner를 붙여야하지만 Java는 디폴트이고 연관되길 원하지 않으면 명시적으로 "static"을 붙여야한다.
② 예시
< 코드 - class1.kt >
package com.example.kotlinbase.DefClass
class Person9 (val id: Id, val id2: Id2, val iAge: Int) {
// 내포된 클래스
class Id(val sFirstName: String, val sFamilyName: String)
fun showMe() = println("내 이름은 ${id.sFamilyName} ${id.sFirstName}이고 나이는 $iAge 입니다.")
class Id2(private val sFullName: String)
// fun showFullName() = println("내 이름은 ${id2.sFullName}입니다.") // 멤버변수 가시성 지정 가능(sFullName 접근 불가)
}
< 코드 - class2.kt >
package com.example.kotlinbase.DefClass
class Person10(val sFirstName: String, val sFamilyName: String) {
// "inner" 키워드가 없다면 "fullName()" 접근 불가
// class MyName(val sDescription: String) {
// fun showOwner() = println("내 이름은 ${fullName()} 입니다.")
// }
// "inner" 키워드로 "fullName()" 접근 가능
inner class MyName(val sDescription: String) {
fun showOwner() = println("내 이름은 ${fullName()} 입니다.")
}
private fun fullName() = "$sFamilyName $sFirstName"
}
< 코드 - main.kt >
package com.example.kotlinbase.DefClass
fun main() {
println("----------결과 1------------")
val id = Person9.Id("꿀떡", "개")
val id2 = Person9.Id2("개꿀떡")
val p9 = Person9(id, id2,32)
p9.showMe()
println("----------결과 2------------")
val p10 = Person10("꿀떡", "개")
val myname = p10.MyName("내 이름")
myname.showOwner()
}
< 결과 >
5. 지역 클래스
- Java와 동일하게 함수 본문에서 클래스를 정의할 수 있다.
- 지역클래스는 자신을 둘러싼 코드 블록 안에서만 쓰일 수 있다.
- 지역클래스 본문안에 접근할 수 있는 값을 포획 및 변경 가능.
- 가시성 변경자를 붙일 수 없다.
① vs Java
- Java에서는 포획한 변수의 값을 변경 불가능, 포획한 변수 사용 시 "final" 키워드 사용
② 예시
< 코드 >
package com.example.kotlinbase.DefClass
fun main() {
// 지역 클래스
class Person11(val sFirstName: String, val sFamilyName: String) {
fun fullName() = "$sFamilyName $sFirstName"
}
val p11 = Person11("꿀떡", "개")
println("내 이름은 ${p11.fullName()} 입니다.")
}
// 지역 클래스를 다른 곳에 호출 시 에러
fun subMain() {
// val p11 = Person11("꿀떡", "개")
// println("내 이름은 ${p11.fullName()} 입니다.")
}
< 결과 >
이번 시간에는 클래스 정의와 멤버, 생성자, 부생성자, 멤버가시성, 내포된 클래스와 지역 클래스 관련하여 개꿀떡 해보았습니다. 다음 시간에는 널 가능성 관련해서 개꿀떡 해보겠습니다. 감사합니다 :)
'조재연의 Kotlin 개꿀떡 > 조재연의 Kotlin 기초 개꿀떡' 카테고리의 다른 글
Kotlin 기초 - 널 가능성 (0) | 2023.04.03 |
---|---|
Kotlin 기초 - 기초 함수 (0) | 2023.03.13 |
Kotlin 기초 - 기초 언어 (1) | 2023.03.08 |
Kotlin 기초 - 안드로이드 스튜디오(Android Studio)로 문법 연습하기 (0) | 2023.01.28 |
Kotlin 기초 - 코틀린(Kotlin)이란? (0) | 2023.01.17 |