모든 내용은 Do it! 코틀린 프로그래밍을 바탕으로 정리한 것입니다.
super과 this의 참조
상위 클래스는 super, 현재 클래스는 this로 참조
super로 상위 객체 참조
open class Bird(var name: String, var wing: Int, var beak: String, var color: String) {
fun fly() = println("Fly wing: $wing")
open fun sing(vol: Int) = println("Sing vol: $vol")
}
class Parrot : Bird {
val language: String
constructor(name: String, wing: Int, beak: String, color: String, language: String) : super(name, wing, beak, color) {
this.language = language
}
fun speak() = println("Speak! $language")
override fun sing(vol: Int) {
super.sing(vol)
println("I'm a parrot!")
speak()
}
}
- super 키워드로 상위 클래스의 프로퍼티, 메서드, 생성자를 사용할 수 있음
- super.프로퍼티, super.메서드(), super()
- 상위 클래스에서 구현한 내용을 그대로 사용하고 필요한 내용만 추가하고 싶은 경우
this로 현재 객체 참조
여러 개의 부 생성자 참조하기
open class Person {
constructor(firstName: String) {
println("[Person] firstName: $firstName")
}
constructor(firstName: String, age: Int) { // 3번
println("[Person] firstName: $firstName, $age")
}
}
class Developer: Person {
constructor(firstName: String): this(firstName, 10) { // 1번
println("[Developer] $firstName")
}
constructor(firstName: String, age: Int): super(firstName, age) { // 2번
println("[Developer] $firstName, $age")
}
}
fun main() {
val sean = Developer("Sean")
}
- 인자가 1개 이므로 Developer의 부 생성자 1번으로 진입
- Developer의 부 생성자 1번에서 this()가 있으므로 부 생성자 2번 호출
- Developer의 부 생성자 2번에서 super()가 있으므로 부모 클래스의 생성자 3번 호출
- Person의 부 생성자 3번 실행
- Developer의 부 생성자 2번 실행
- Developer의 부 생성자 1번 실행
주 생성자와 부 생성자 함께 사용하기
class Person(firstName: String, out: Unit = println("[Primary Constructor] Parameter")){
val fName = println("[Property] Person fName: $firstName")
init {
println("[init] Person init block")
}
constructor(firstName: String, age: Int,
out: Unit = println("[Secondary Constructor] Parameter")): this(firstName) {
println("[Secondary Constructor] Body: $firstName, $age")
}
}
fun main() {
val p1 = Person("Kildong", 30)
println()
val p2 = Person("Dooly")
}
p1 객체 생성
- 인자가 2개 이므로 Person의 부 생성자로 진입
- Person의 부 생성자에 this()가 있으므로 주 생성자 호출
- fName 프로퍼티 할당
- init 초기화 블록 실행
- Person의 부 생성자 실행
p2 객체 생성
- 인자가 1개 이므로 Person의 주 생성자로 진입
- fName 프로퍼티 할당
- init 초기화 블록 실행
바깥 클래스 호출하기
클래스를 선언할 때 클래스 안에 다시 클래스를 선언하는 것이 가능함
특정 클래스의 내부에 선언된 클래스 : 이너 클래스 / 이너 클래스의 바깥에 있는 클래스 : 바깥 클래스
open class Base {
open val x: Int = 1
open fun f() = println("Base Class f()")
}
class Child : Base() {
override val x: Int = super.x +1
override fun f() = println("Child Class f()")
inner class Inside {
fun f() = println("Inside Class f()")
fun test() {
f()
Child().f()
super@Child.f()
println("[Insdie] super@Child.x: ${super@Child.x}")
}
}
}
fun main() {
val c1 = Child()
c1.Inside().test()
}
- 바깥 클래스의 메서드/프로퍼티를 사용하는 경우 : 바깥클래스().메서드()/프로퍼티
- 바깥 클래스의 부모 클래스의 메서드/프로퍼티를 사용하는 경우 : super@바깥클래스.메서드()/프로퍼티
인터페이스 참조하기
인터페이스 : 일종의 구현 약속. 인터페이스를 참조하는 클래스는 인터페이스가 가지고 있는 내용을 구현해야 하는 가이드를 제시
* 코틀린에서는 자바와 마찬가지로 클래스의 다중 상속이 지원되지 않음
* 인터페이스는 필요한 만큼 다수의 인터페이스를 지정해 구현할 수 있음 → 인터페이스의 프로퍼티/메서드의 이름 중복 가능
open class A {
open fun f() = println("A Class f()")
fun a() = println("A Class a()")
}
interface B {
fun f() = println("B Interface f()")
fun b() = println("B Interface b()")
}
class C : A(), B {
override fun f() = println("C Class f()")
fun test() {
f()
b()
super<A>.f()
super<B>.f()
}
}
fun main() {
val c = C()
c.test()
}
- 이름이 중복되지 않은 메서드는 바로 사용 가능
- 이름이 중복된 메서드의 경우 : super<클래스명/인터페이스명>.메서드()
정보 은닉 캡슐화
캡슐화 (Encapsulation) : 클래스를 작성할 때 속성이나 기능을 숨기는 것 → 정보 은닉
가시성 지시자
가시성 (Visibility) : 클래스, 메서드, 프로퍼티의 접근 범위
민감하거나 불필요한 부분은 숨기고 사용하기 위해 필요한 부분만 공개 → 클래스/메서드/프로퍼티에 가시성 지시자로 지정 가능
- private : 이 요소는 외부에서 접근할 수 없음
- public : 이 요소는 어디서든 접근이 가능함 (디폴트)
- protected : 외부에서 접근할 수 없으나 하위 상속 요소에서는 가능함
- internal : 같은 정의의 모듈 내부에서는 접근 가능함
private
: 접근 범위가 선언된 요소에 한정하는 가시성 지시자
private class PrivateClass {
private var i = 1
private fun privateFunc() {
i += 1
}
fun access() {
privateFunc()
}
}
class OtherClass {
// val opc = PrivateClass() - 프로퍼티 opc는 private가 되어야 함
fun test() {
val pc = PrivateClass()
}
}
fun main() {
val pc = PrivateClass()
// pc.i
// pc.privateFunc()
}
fun TopFunction() {
val tpc = PrivateClass()
}
- 같은 파일에서 private로 지정한 객체를 생성할 수 있음
- 다른 클래스에서 프로퍼티로서 객체를 지정하려면 프로퍼티도 똑같이 private로 선언해야 함
- 객체를 생성했다고 하더라도 private 클래스의 멤버는 다른 클래스나 main(), 최상위 함수에서 접근 불가
- private 멤버는 해당 클래스 내부에서만 접근이 가능
protected
: 최상위에 선언된 요소에는 지정할 수 없고 클래스나 인터페이스와 같은 요소의 멤버에만 지정할 수 있는 가시성 지시자
open class Base {
protected var i = 1
protected fun protectedFunc() {
i += 1
}
fun access() {
protectedFunc()
}
protected class Nested
}
class Derived : Base() {
fun test(base: Base): Int {
protectedFunc()
return i
}
}
fun main() {
val base = Base()
// base.i
// base.protectedFunc()
base.access()
}
- 멤버가 클래스인 경우에도 protected를 사용할 수 있음
- protected로 지정된 멤버는 상속된 하위 클래스에서 자유롭게 접근이 가능함
- 외부 클래스나 객체 생성 후 점(.) 표기를 통해 protected 멤버에 접근하는 것은 허용하지 않음
internal
* internal : 프로젝트 단위의 모듈
* 기존의 자바에서는 package라는 지시자에 의해 패키지 이름이 같은 경우에 접근을 허용함* 코틀린에서는 패키지에 제한하지 않고 하나의 모듈 단위를 대변하는 internal를 사용
가시성 지시자와 클래스의 관계
open class Base {
// 이 클래스에서는 a, b, c, d, e 접근 가능
private val a = 1
protected open val b = 2
internal val c = 3
val d = 4
protected class Nested {
// 이 클래스에서는 b, c, d, e 접근 가능
public val e: Int = 5
private val f: Int = 6
}
}
class Derived : Base() {
// 이 클래스에서는 b, c, d, e 접근 가능
// a는 접근 불가
override val b = 5
}
class Other(base: Base) {
// base.a, base.b는 접근 불가
// base.c, base.d는 접근 가능(같은 모듈)
// Base.Nested에는 접근 불가, Nested::e 접근 불가
}
- Base 클래스에서 파생된 Derived 클래스 : protected, internal, public으로 지정된 프로퍼티에 접근 가능
- 외부 클래스인 Other 클래스 : internal, public으로 지정된 프로퍼티만 접근 가능
- 오버라이딩 된 멤버가 있는 경우 상위 클래스와 동일한 가시성 지시자를 갖음
클래스와 클래스의 관계
- 약한 참조 관계 : 연관(Association), 의존(Dependency) → 어떤 객체에서 또 다른 객체를 '이용한다' / 생명주기가 다름
- 집합(Aggregation) 관계 : 연못과 오리의 관계 → 연못과 오리는 따로 떨어져도 문제가 없음
- 합성/구성(Composition) 관계 : 자동차와 엔진의 관계 → 자동차가 파괴되면 엔진도 동작하지 않음 / 생명주기가 의존
연관 관계
: 2개의 서로 분리된 클래스가 연결을 가지는 것
→ 단방향/양방향으로 연결될 수 있으며, 두 요소가 서로 다른 생명주기를 가지고 있음
class Patient(val name: String) {
fun doctorList(d: Doctor) {
println("Patient: $name, Doctor: ${d.name}")
}
}
class Doctor(val name: String) {
fun patientLst(p: Patient) {
println("Doctor: $name, Patient: ${p.name}")
}
}
fun main() {
val doc1 = Doctor("KimSabu")
val patient1 = Patient("Kildong")
doc1.patientLst(patient1)
patient1.doctorList(doc1)
}
- Doctor와 Patient 클래스의 객체는 따로 생성되며 서로 독립적인 생명주기를 가지고 있음
- 위의 예시에서는 두 클래스가 서로의 객체를 참조하는 양방향 참조를 가짐
의존 관계
: 한 클래스가 다른 클래스에 의존되어 있어 영향을 주는 경우
class Patient(val name: String, var id: Int) {
fun doctorList(d: Doctor) {
println("Patient: $name, Doctor: ${d.name}")
}
}
class Doctor(val name: String, val p: Patient) {
val customerId: Int = p.id
fun patientLst(p: Patient) {
println("Doctor: $name, Patient: ${p.name}")
println("Patient Id: $customerId")
}
}
fun main() {
val patient1 = Patient("Kildong", 1234)
val doc1 = Doctor("KimSabu", patient1)
doc1.patientLst(patient1)
patient1.doctorList(doc1)
}
- Doctor 클래스에서 Patient를 매개 변수로 받아야 하므로 Patient 객체가 먼저 생성되어 있어야 함
- Doctor 클래스는 Patient 클래스에 의존함
집합 관계
: 연관 관계와 거의 동일하지만 특정 객체를 소유한다는 개념이 추가된 것
class Pond(_name: String, _members: MutableList<Duck>) {
val name: String = _name
val members: MutableList<Duck> = _members
constructor(_name: String): this(_name, mutableListOf<Duck>())
}
class Duck(val name: String)
fun main() {
val pond = Pond("myFavorite")
val duck1 = Duck("Duck1")
val duck2 = Duck("Duck2")
pond.members.add(duck1)
pond.members.add(duck2)
for (duck in pond.members) {
println(duck.name)
}
}
- 연못은 개념적으로 여러 오리를 소유할 수 있고, 오리는 한번에 한 연못에서만 놀 수 있음
- 오리를 여럿 추가하기 위해 배열/리스트 구조가 필요함
- 2개의 개체는 따로 생성되어 서로의 생명주기에 영향을 주지 않음
구성 관계
: 집합 관계와 거의 동일하지만 특정 클래스가 어느 한 클래스의 부분이 되는 것
→ 소유자 클래스가 삭제되면 구성하고 있던 클래스도 같이 삭제됨 (생명주기가 의존)
class Car(var name: String, val power: String) {
private var engine = Engine(power)
fun startEngine() = engine.start()
fun stopEngine() = engine.stop()
}
class Engine(power: String) {
fun start() = println("Engine has been started")
fun stop() = println("Engine has been stopped")
}
fun main() {
val car = Car("tico", "100hp")
car.startEngine()
car.stopEngine()
}
- Engine 클래스는 Car 클래스의 생명주기에 의존적
- car 객체를 생성함과 동시에 Engine 클래스의 객체도 생성됨
'Kotlin > Kotlin 프로그래밍' 카테고리의 다른 글
[Kotlin] 다양한 클래스와 인터페이스 (3) (0) | 2021.05.13 |
---|---|
[Kotlin] 다양한 클래스와 인터페이스 (2) (0) | 2021.05.10 |
[Kotlin] 다양한 클래스와 인터페이스 (1) (0) | 2021.04.21 |
[Kotlin] 프로퍼티와 초기화 (0) | 2021.04.20 |
[Kotlin] 클래스와 객체 (1) (0) | 2021.04.16 |
[Kotlin] 프로그램의 흐름 제어 : 조건문, 반복문, 예외처리 (0) | 2021.04.14 |
[Kotlin] 함수와 함수형 프로그래밍 (3) - 코틀린의 다양한 함수 (0) | 2021.04.13 |
[Kotlin] 함수와 함수형 프로그래밍 (2) - 고차함수와 람다식 (0) | 2021.04.07 |
댓글