Do it! 코틀린 프로그래밍 [둘째마당, 객체 지향 프로그래밍] 학습
super.메서드()
super.프로퍼티
의 형식으로 사용super()
를 사용해 생성자를 호출할 수 있음// 부모클래스
open class Bird(var vol: Int) {
open fun sing(vol: Int) = println("Sing vol: $vol) // (1)
}
// 자식클래스
class Parrot(name: String) {
override fun sing(vol: Int) {
super.sing(vol) // Bird 클래스의 sing() 실행. (1) 출력
println("I'm a parrot! Ths volume level is $vol")
}
}
📌 바깥 클래스 호출하기
📌 인터페이스에서 참조하기
open class A {
open fun f() { }
fun a() { }
}
interface B {
fun f() { } //oepn , 클래스A의 f()와 중복
fun b() { }
}
class C: A(), B { // 쉼표를 사용해 클래스와 인터페이스 지정
// 클래스A와 인터페이스B에서 f()함수의 이름이 중복되므로
// 클래스C에서 필수적으로 f()를 오버라이딩 해주어야함
override fun f() { /* 재정의 */}
fun test() {
f() // 클래스C의 fun()
b() // 인터페이스B의 b()
super<A>.f() // A 클래스와 B인터페이스의 함수명이 겹침
super<B>.f() // 앵클 브래킷을 사용해 접근하려는 클래스와 인터페이스의 이름을 정해줌
}
}
클래스를 작성할 때 속성이나 기능을 숨기는것을 캡슐화(Encapsulation)라고 함. 이러한 정보 은닉은 객체 지향 프로그래밍의 가장 큰 특징
📌 가시성 지시자
📌 가시성 지시자의 접근 범위
private class PrivateClass {
private var i = 1
private fun 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() // 객체 생성 가능
}
open class Base {
protected var i = 1
protected fun protectedFunc() {}
fun access() {
protectedFunc() // 접근 허용
}
protected class Nested // 내부 클래스에는 지시자 허용
}
class Derived : Base() {
fun text(base: Base): Int{
protectedFunc() // Base 클래스의 자식 클래스이므로 접근 가능
return i
}
}
fun main() {
val base = Base()
base.i // 접근 불가
base.protectedFunc() // 접근 불가
base.access() // 접근 가능
}
📍자바의 가시성 지시자 기본값인 package는 코틀린에서 사용하지 않음. 프로젝트 단위 묶음의 .jar 파일이 달라져도 패키지 이름이 동일하면 다른 파일에서도 접근 할 수 있는 보안 문제를 막고자 코틀린에서는 internal을 사용
internal class InternalClass {
internal var i = 1
internal fun icFunc() { }
fun access(){
icFunc() // 접근 가능
}
}
class Other {
val ic = InternalClass() // 불가
internal val ic = InternalClass() // 가능 - 프로퍼티를 internal로 맞춰주어야함
fun test(){
ic.i // 접근 가능
ic.icFunc() // 접근 가능
}
}
fun main() {
val mic = InternalClass()
mic.i // 접근 가능
mic.icFunc() // 접근 가능
}
open class Base{
private val a=1
protected open val b=2
internal val c =3
val d = 4 // public
protected class Nested {
// a, b, c, d, e, f 접근 가능
public val e: Int = 5
private val f: Int = 6
}
}
class Derived: Base(){
// b, c, d, e 접근 가능
// a, f 접근 불가
override val b = 5
}
class Other(base: Base){
// base.a, base.b -> 접근 불가
// base.c, base.d -> 접근 가능
// Base.Nested ->접근 불가
}
클래스 간의 관계는 두 클래스가 서로 참조하느냐 아니냐에 따라 나뉘고, 그런 다음 두 클래스가 생명 주기에 영향을 주는지에 따라 나뉠 수 있음
class Patient(val name: String) {
// Doctor 클래스를 인자로 참조
fun doctorList(d: Doctor) { }
}
class Doctor(val name: String) {
// Patient 클래스를 인자로 참조
fun patientList(p: Patient) { }
}
fun main() {
val doc1 = Doctor("KimSabu")
val patient1 = Patient("Kildon")
}
-> 서로 독립적인 생명주기를 가짐
-> 양방향 참조
class Patient(val name: String) {
fun doctorList(d: Doctor) { }
}
class Doctor(val name: String, val p:Patient) { }
fun main() {
val patient1 = Patient("Kildon")
// Patient를 매개변수로 받아야 하므로 Patient 객체가 먼저 생성되어있어야 함
// 따라서 Doctor클래스는 Patient 클래스에 의존
val doc1 = Doctor("KimSabu", patient1)
}
class Pond(_members: MutableList<Duck>){
val members: MutableList<Duck> = _members
// 주생성자가 존재한다면 부생성자는 무조건 주생성자에게 직간접적으로 생성을 위임해야함
// 따라서 부생성자는 this를 통해 주생성자에게 생성을 위임
constructor(): this(mutableListOf<Duck>())
}
class Duck()
fun main() {
val pond = Pond()
val duck1 = Duck()
val duck2 = Duck()
pond.members.add(duck1)
pond.members.add(duck2)
}
-> 연못 객체 pond는 오리 객체 duck1과 duck2를 소유
class Car(val power: String) {
private var engine = Engine(power)
fun startEngine() = engine.start()
}
// Engine 클래스는 Car클래스의 생명주기에 의존적
class Engine(power: String){
fun start() { }
}
fun main() {
// car객체 생성과 동시에 engine 객체도 생성
// car객체가 삭제되면 engine 객체도 삭제
val car = Car("100hp")
car.startEngine()
}