nueijeel

[Kotlin 이론] 0515 상속과 접근 제한자 본문

Kotlin

[Kotlin 이론] 0515 상속과 접근 제한자

nueijeel 2023. 5. 22. 21:58

2023-05-15

 

 

1. 상속

 

상속

: 클래스를 설계할 때 다른 클래스가 가지고 있는 부분을 물려받는 개념으로 이를 통해 클래스마다 중복된 부분을 하나의 클래스에 만들 수 있다.

 

상속을 해주는 클래스를 부모 클래스(SuperClass)라고 하고, 상속을 받는 클래스를 자식 클래스(SubClass)라고 한다.

 

open class SuperClass{}
class SubClass : SuperClass{}

 

코틀린에서 클래스를 정의하면 컴파일 했을 때 자바 코드에서는 final 클래스가 된다. 따라서 상속 관계의 부모 클래스를 정의할 때는 open 키워드를 사용해 정의해야한다. 그러면 자바 코드로 컴파일해도 일반 클래스가 되어 상속이 가능하다.

 

자식 클래스는 이름 옆에 콜론을 사용해 상속 받을 부모 클래스 이름을 명시해야 한다.

 

//기본 생성자만 있는 부모클래스
open class SuperClass1{
    var superMember1 = 100
    
    constructor(){
    	println("SuperClass의 기본 생성자")
    }
}

class SubClass1 : SuperClass1(){}
class SubClass2 : SuperClass1{
    constructor():super(){
    	println("SubClass2의 기본 생성자")
    }
}

//주 생성자가 있는 부모클래스
open class SuperClass2(var a : Int)

class SubClass3 : SuperClass2(100)
class SubClass4 : SuperClass2{
    constructor():super(100){
    	println("SubClass4의 기본 생성자")
    }
}

 

기본 생성자(인자가 없는 생성자)만 가지는 클래스와 주 생성자만 가지는 클래스를 상속받는 예제를 작성해봤다.

 

주 생성자를 가지는 SuperClass2를 상속받는 자식 클래스 SubClass3, SubClass4에서 생성자를 만든다면 반드시 부모 클래스의 주 생성자를 호출해야 한다. 만약 부모 클래스가 다수의 생성자를 가지고 있다면 자식 클래스의 생성자 선언 시 호출할 부모 클래스의 생성자를 매개변수에 따라 지정해주면 된다.

 

 

open class TestClass1(var name:String){
    var age:Int = 0
    constructor(name:String, age:Int) : this(name) {
        this.age = age
    }
}

class TestClass2 : TestClass1{
    var phoneNumber:Long = 0L
    var address : String = ""
    
    constructor(name: String, phoneNumber: Long) : super(name){
        this.phoneNumber = phoneNumber
    }
    constructor(name: String, age: Int, phoneNumber: Long, address:String): super(name, age){
        this.phoneNumber = phoneNumber
        this.address = address
    }
}

부모 클래스 TestClass1을 상속받는 자식 클래스 TestClass2이다. 

첫 번째 생성자에서는 부모 클래스의 주 생성자를 호출했고, 두 번째 생성자에서는 부모 클래스의 보조 생성자를 호출했다.

이렇게 부모 클래스의 생성자를 호출할 때는 super 키워드를 사용한다.

 

부모 클래스 TestClass1의 보조 생성자에서는 this 키워드를 사용해 자신의 주 생성자를 호출했다. 

 

this 키워드
- 객체 자신을 지칭하는 키워드이다.
- 주로 멤버와 동일한 이름의 변수나 메서드가 있을 때, 이들을 구분짓기 위해 사용한다.
- 생성자에서 다른 생성자를 호출할 때도 사용된다.

 

super 키워드
- 상속 관계일 때 부모 영역을 지칭하는 키워드이다.
- 자식 클래스에서 동일한 이름의 멤버 변수와 상속 받은 멤버 변수를 구분할 때, 오버라이딩한 메서드와 부모의 메서드를 구분할 때, 부모의 생성자를 호출할 때 사용한다.

 

 

 

2. 접근 제한자

 

접근 제한자

: 변수나 메서드, 클래스의 접근 권한을 설정할 수 있는 키워드이다.

 

자바에서는 public, default, protected, private의 네 가지 접근 제한자를 사용한다.

코틀린에서는 public, protected, private, internal의 네 가지 접근 제한자를 사용한다.

 

< 클래스의 접근 제한자 >
- private 클래스 : 같은 파일 내에서만 객체 생성 가능한 클래스 (같은 모듈, 같은 패키지여도 다른 파일에서는 사용 불가)
- public 클래스 : 모든 파일이나 패키지, 모듈에서 객체 생성 가능한 클래스
- internal 클래스 : 같은 모듈 내에서만 객체 생성 가능한 클래스 (같은 모듈 내 다른 패키지에서도 사용 가능)
 ( ※ protected : 클래스에는 지정할 수 없는 접근 제한자 )

 

< 변수와 메서드의 접근 제한자 >
- private 요소 : 외부에서 접근할 수 없는 요소. 같은 클래스 내에서만 사용이 가능
- public 요소 : 외부에서도 접근이 자유로운 요소
- protected 요소 : 상속 관계인 클래스에서만 이 요소에 접근 가능
- internal 요소 : 모듈이 같을 경우에만 이 요소에 접근 가능

 

클래스와 멤버를 선언할 때 별다른 접근 제한자를 설정하지 않으면 public이 기본으로 지정된다.

 

 

 

3. Property

 

Property

: 프로퍼티는 문법적으로 변수를 사용하는 것처럼 작성하지만 실제로는 캡슐화가 적용된 변수에 접근하기 위해 getter 혹은 setter 를 설정한 것을 말한다.

 

코틀린에서 정의한 생성자 매개 변수들과, 클래스 멤버 변수들은 전부 private로 생성된다. val 변수의 경우 final 변수로 정의되기 때문에 생성 시 초기화 해야하고, getter 메서드가 자동으로 만들어진다. var 변수는 값의 변경이 자유롭기 때문에 setter와 getter가 모두 자동으로 만들어진다.

 

fun main(){
    var t1 = TestClass1(10, 20)
    println("t1.a1 : ${t1.a1}")
    println("t1.a2 : ${t1.a2}")
    
    var t2 = TestClass2()
    t2.a1 = 100
    println("t2.a1 : ${t2.a1}")
    println("t2.a2 : ${t2.a2}")
}

class TestClass1(var a1:Int, val a2:Int)

class TestClass2{
    var v1 = 10
    val v2 = 20
}

 

main 함수 내 println()문에서 멤버 변수의 값을 출력했는데, 이때 객체의 멤버에 직접 접근해 값을 출력하는게 아니라 내부적으로 변수 생성 시 정의된 getter / setter 가 호출되어 값을 반환하거나 설정할 수 있는 것이다.

 

만약 getter와 setter를 원하는 대로 작성하고 싶다면 다음과 같은 방법으로 직접 설정할 수 있다.

 

fun main(){
    val t1 = TestClass()
    println("t1.v1: ${t1.v1}")
    println("t1.v2: ${t1.v2}")
    
    t1.v1 = 6
    println("t1.v1: ${t1.v1}")
    
    t1.v1 = 20
    println("t1.v1: ${t1.v1}")
}

class TestClass{
    var v1 = 5
    	get(){
            println("v1의 getter 호출")
            return field
        }
        set(value){
            println("v1의 setter 호출")
            if(value in 1..10){
            	field = value
            }
        }
    val v2 = 10
    	get(){
            println("v2의 getter 호출")
            return field
        }

}

TestClass의 멤버 변수 v1, v2의 getter, setter를 설정해보았다.

 

v1은 var 변수이므로 get, set이 모두 가능하다. field는 해당 변수 자체를 의미하기 때문에 get()에서는 해당 변수의 값을 반환하는데 사용하고, set()에서는 setter 호출 시 받은 변경할 값으로 변수 값을 재설정하는데 사용한다. value 파라미터는 새롭게 할당할 값이다. 

 

getter는 값을 반환해주는 역할을 하기 때문에 get()에 반드시 field를 return 해줘야 한다. 

 

setter로 받은 value 값의 범위 검사를 통해 해당 변수의 값이 바뀔 수도 있고, 바뀌지 않을 수도 있다. 

 

 

728x90