[kotlin] 코틀린 기본 문법(3) - 코틀린 객체지향 프로그래밍, 상속과 서브클래싱
객체
Object, Instance라고도 부르는 객체는 소프트웨어 애플리케이션의 빌딩 블록으로 쉽게 이용 및 재사용할 수 있는 자급자족의 기능 모듈
클래스 정의
객체가 생성되었을 때의 형태를 정의
// 기본적인 클래스 코드
class BankAccount {
}
//프로퍼티 (변수) 추가
class BankAccount {
var accountBalance: Double = 0.0
var accountNumber: Int = 0
}
메서드 정의
코틀린에서의 메서드는 fun을 통해 함수를 정의할 수 있다.
class BankAccount {
var accountBalance: Double = 0.0
var accountNumber: Int = 0
fun displayBalance() {
println("Number $accountNumber")
println("Current balance is $accountBalance")
}
}
클래스 객체 선언 및 초기화
클래스를 참조해야 할 땐 저장할 변수를 선언하고 접근하면 된다.
val account1: BankAccount = BankAccount()
val account1 = BankAccount() // 타입 추론 생략 가능
기본 및 보조 생성자
클래스 생성되는 시점에 초기화 작업을 수행해야 할 경우 생성자를 이용해 구현할 수 있다.
class BankAccount {
var accountBalance: Double = 0.0
var accountNumber: Int = 0
constructor(number: Int, balance: Double) {
accountNumber = number
accountBalance = balance
}
...
}
클래스의 인스턴스 생성 시 다음과 같이 프로퍼티에 초기값을 제공할 수 있다.
val account: BankAccount = BankAccount(321123123, 123.45)
또한 클래스는 여러개의 보조 생성자를 포함할 수 있으며 인스턴스마다 다른 초기값을 설정할 수 있다.
class BankAccount {
var accountBalance: Double = 0.0
var accountNumber: Int = 0
var lastName: String = ""
constructor(number: Int, balance: Double) {
accountNumber = number
accountBalance = balance
}
constructor(number: Int, balance: Double, name: String) {
accountNumber = number
accountBalance = balance
lastName = name
}
...
}
하지만 매번 이렇게 보조 생성자를 추가할 수 없으니 기본 생성자를 이용하면 더욱 간단하게 표현할 수 있다.
class BankAccount(val accountNumber: Int, var accountBalance: Double) {
...
}
기본 생성자로 선언했으니 클래스 본문 안에서 변수를 선언할 필요가 없다.
물론 기본 생성자에 추가로 여러개의 보조 생성자를 선언할 수 있다.
class BankAccount(val accountNumber: Int, var accountBalance: Double) {
var lastName: String = ""
constructor(accountNumber: Int,
accountBalance: Double,
name: String) : this(accountNumber, accountBalance) {
lastName = name
}
}
초기화 블록
클래스에서 생성자 다음에 호출되는 초기화 블록을 포함할 수 있다.
class BankAccount(val accountNumber: Int, var accountBalance: Double) {
init {
// 초기화 코드
}
...
}
메서드 호출과 프로퍼티 접근
위에 만든 BankAccount 클래스를 호출하고 클래스에 선언한 프로퍼티에 접근하는 방법은 다음과 같다.
// 인스턴스 변수의 값을 저장
val balance1 = account1.accountBalance
// 인스턴스 변수의 값을 설정
account1.accountBalance = 1234.56
// 인스턴스 메서드 호출
account1.displayBalance()
커스텀 접근자
프로퍼티가 반환 또는 설정되기 전 커스텀 접근자를 통해 설정할 수 있다.
보통 getter 와 setter를 사용해서 구현한다.
class BankAccount(val accountNumber: Int, var accountBalance: Double) {
val fees: Double = 25.00
val balanceLessFees: Double
get() {
return accountBalance - fees
}
set(value) {
accountBalance = value - fees
}
fun displayBalance() {
println("Number $accountNumber")
println("Current balance is $accountBalance")
}
}
중첩된 내부 클래스
코틀린에서는 클래스 안에 다른 하나의 클래스를 중첩할 수 있다.
class ClassA {
class ClassB {
}
}
위 코드에서는 ClassB는 외부 클래스 안의 프로퍼티에 접근할 수 없다.
접근이 필요하다면 inner를 사용하면 된다.
class ClassA {
var myProperty: Int = 10
inner class ClassB {
val result = 20 + myProperty
}
}
컴패니언 객체
코틀린 클래스는 하나의 컴패니언 객체를 포함할 수 있다.
컴패니언 객체는 클래스의 모든 인스턴스에 공통적인 메서드와 변수를 포함한다.
class MyClass {
fun showCount() {
println("counter = " + counter)
}
companion object {
var counter = 1
fun counterUp() {
counter += 1
}
}
}
fun main(args: Array<String>) {
println(MyClass.counter)
}
메인 메서드에서 단순히 counter 변수의 현재 값을 표시하지만 클래스 인스턴스가 아닌 클래스 자체의 메서드를 호출해 표시한다.
컴패니언 객체를 포하하는 클래스의 모든 인스턴스는 현재 변수값을 포함해 동일한 컴패니언 객체에 접근할 수 있다.
fun main(args: Array<String>) {
println(MyClass.counter)
MyClass.counterUp()
println(MyClass.counter)
val instanceA = MyClass()
instanceA.showCount()
val instanceB = MyClass()
instanceB.showCount()
}
// 실행 결과
// 1
// 2
// counter = 2
// counter = 2
위 메인 메서드를 실행하게 되면 두 인스턴스 모두 증가된 값 2를 반환하게 되는데, 이는 두 클래스 인스턴스가 동일한 컴패니언 객체 데이터를 공유하고 있음을 알 수 있다.
서브 클래싱
부모 클래스에서 서브 클래스를 파생할 때는 먼저 해당 부모 클래스를 공개로 선언해야 한다.
이유는 코틀린 코드에서 오류가 덜 발생하도록 디자인된 안전 조치이다.
클래스 헤더 안에 open 키워드를 기술하면 된다.
// 부모 클래스
open class MyParentClass {
var myProperty: Int = 0
}
// 서브클래스
class MySubClass : MyParentClass() {
}
기본 및 보조 생성자를 포함한 클래스의 서브클래스를 생성하는 방법은 다음과 같다.
// 부모 클래스
open class MyParentClass(var myProperty: Int) {
}
// 서브클래스
class MySubClass(myProperty: Int) : MyParentClass(myProperty) {
}
부모 클래스가 하나 이상의 보조 생성자를 포함하고 있다면, 해당 생성자 또한 서브클래스 선언 안에서 구현되어야 하고, 서브클래스 생성자에 인수로 전달된 값들을 부모 클래스의 보조 생성자로 전달해서 호출해야 한다.
서브클래스에서 부모 클래스를 참조할 때는 super 키워드를 사용한다.
// 부모 클래스
open class MyParentClass {
var myProperty: Int = 0
constructor(number: Int) {
myProperty = number
}
}
// 서브클래스
class MySubClass : MyParentClass {
constructor(number: Int) : super(number)
}
// 서브클래스 생성자 안에서 코드 작성을 하려면 괄호 안에 코드를 작성한다.
class MySubClass : MyParentClass {
constructor(number: Int) : super(number) {
...
}
}
상속
다음 예시는 BankAccount의 서브클래스인 SavingsAccount가 부모 클래스의 모든 메서드와 프로퍼티를 상속한 예제이다.
open class BankAccount {
var accountNumber = 0
var accountBalance = 0.0
constructor(number: Int, balance: Double) {
accountNumber = number
accountBalance = balance
}
open fun displayBalance() {
println("Number $accountNumber")
println("Current balance is $accountBalance")
}
}
class SavingsAccount : BankAccount {
var interestRate: Double = 0.0
// 부모 클래스 생성자 호출
constructor(accountNumber: Int, accountBalance: Double) :
super(accountNumber, accountBalance)
// 커스텀 보조 생성자 호출
constructor(accountNumber: Int, accountBalance: Double, rate: Double) :
super(accountNumber, accountBalance) {
interestRate = rate
}
fun calculateInterest(): Double {
return insterestRate * accountBalance
}
// 상속된 메서드 오버라이드
override fun displayBalance() {
super.displayBalance() //오버라이드된 메서드 호출
println("Prevailing interest rate is $interestRate")
}
}
SavingsAccount 클래스의 인스턴스 생성, 변수 설정, 메서드 호출이 모두 가능하다.
SavingsAccount 클래스는 이제 부모 클래스인 BankAccount와 거의 동일한 방식으로 아래처럼 이용 가능하다.
val savings1 = SavingAccount(12311, 600.00, 0.07)
println(savings1.calculateInterest())
savings1.displayBalance()
이렇게 코틀린의 기본 문법을 알아보았다.
코틀린에서만 사용되는 특수한 경우들을 위주로 알아봐서 내용이 많지는 않지만
이정도만 알고 있어도 코틀린으로 코드를 작성하는데는 무리가 없을 것이다.
코틀린 기본 문법(1) - 코틀린 데이터 타입, 변수, 널 허용