[Kotlin] 클래스(1)
📚 클래스(1)
📔 클래스 정의
클래스의 기본 구조는 머리부(header)와 몸체부(body)로 구분된다. 코틀린의 클래스 정의는 특징은 주 생성자를 머리부에 정의한다.
📖 지시자 정의
지시자는 클래스의 상속이나 클래스 멤버들의 외부에서 접근 범위를 제한한다.
- 상속 지시자(modifier)
- open: 상속할 수 있는 클래스를 만들 때 지정
- final: 코틀린 클래스는 상속을 못 하도록 되어 있는 것인 기본이기 때문에 표시하지 않으면 상속할 수 없는 클래스로 설정(기본값)
- 사용 가시성 지시자(visuality modifier) - 클래스를 사용할 수 있는 범위 지정
- 비공개(private): 파일에 지정 시, 해당 파일 내부에서만 사용 가능
- 상속만 허용(protected): 파일 내부 또는 상속한 경우에만 사용 가능
- 모듈만 허용(internal): 프로젝트 내 컴파일 단위인 모듈에서만 사용 가능. 모듈의 단위는 maven, Gradle 등에서 지정한 범위에 따름
- 공개(public): 어디서나 사용 가능(기본값)
📖 클래스 머리부 정의
- class: 클래스를 정의할 때 사용하는 키워드
- 클래스 이름: class 키워드 다음 클래스 이름을 작성. 일반적으로 파스칼 케이스를 사용
- 주 생성자(primary constructor): 클래스 이름 옆 생성자 키워드 constructor를 다음에 정의 가능하고 생략할 수 있음
- 주 생성자의 접근 지시자(private)를 지정할 경우, constructor 키워드 생략 불가
- 주 생성자 내부에 매개변수를 정의할 수 있고 매개변수 앞에 val 또는 var 키워드를 붙일 시, 속성으로 정의
- 클래스/인터페이스 상속
- 기본 생성자 다음에 콜론을 붙이고 그 다음 상속을 의미하는 클래스 또는 인터페이스를 지정하는 과정으로, 클래스는 하나만 상속 가능하지만 인터페이스는 여러 개 상속 가능
📖 클래스 몸체부 정의
- 초기화 블록
- 주 생성자는 머리부에 정의되어 내부 로직을 넣을 수 없으므로 init이라는 초기화 블록을 제공. 주 생성자가 호출 시, 초기화 블록 내부의 코드가 실행
- init 블록에서는 보다 위에 선언된 멤버 변수, 생성자 변수만 사용 가능
- 보조 생성자(secondary constructor)
- 본문에 constructor 키워드로 보조 생성자 정의
- 보조 생성자는 함수 오버로딩과 같이 여러 개 정의 가능
- 주 생성자와 같이 정의된 경우, 보조 생성자 중 하나는 반드시 주 생성자를 this로 호출해야 함
- 보조 생성자가 여러 개 정의될 경우, 다른 보조 생성자를 this로 호출 가능
- 매개변수를 속성으로 정의 불가
- 멤버 변수(속성)
- val 또는 var 키워드로 속성 선언
- 주 생성자일 경우만 매개변수로 속성 선언 가능
- 멤버 함수(메서드)
- 실제 객체가 생성되어도 객체 내부에는 메서드를 가질 수 없음
- 자바처럼 정적 메서드, 즉 클래스로 직접 접근하는 메서드는 없음
- 내부 클래스/내부 object 정의
- 클래스 내부에 내부 클래스, 이너 클래스 정의 가능
- object 정의와 동반 객체 선언 가능
📖 클래스 정의
public class 클래스명 constructor(val 속성1: Int) : Any() {
init {
println("초기화 실행") // 기본 생성자의 초기화 코드
}
var 속성2: String = "초기화 값"
constructor(매개변수1: String, 매개변수2: Int) : this(매개변수2) {
var 속성3: String = 매개변수1
}
fun 메서드1(): Unit {
// 함수 역할 정의
}
class 내부클래스 {
// 클래스 로직
}
object 내부객체 {
// 객체 로직
}
}
📔 생성자로 객체 생성
📖 주 생성자로 객체 생성
class Person (name: String, age: Int) { // 주 생성자의 매개변수 지정
val name = name // 속성을 매개변수 값으로 초기화
val age = age
}
class People(val name: String, val age: Int) // 속성을 주 생성자에 표시
val c = Person("아무개", 33) // 객체 인스턴스 생성
val d = c // 객체 인스턴스 연결, 동일한 객체
println(d.name)
println(c.name)
val p = People("홍길동", 32) // 객체 인스턴스 생성
println(p.name + " " + p.age)
아무개
아무개
홍길동 32
📖 초기화 블록 실행
class Init(name: String, age: Int) {
var name: String = ""
var age: Int = 0
init { // 초기화 블록 정의
this.name = name // 초기화 블록에서는 생성자 매개변수 사용
this.age = age // 속성 이름 앞에 this로 현재 객체 표시
println("주 생성자와 같이 실행")
}
}
val i = Init("기러기", 20)
println(i.name + " " + i.age)
주 생성자와 같이 실행
기러기 20
📖 주 생성자나 보조 생성자가 없는 클래스 정의
class NoConstructor {
val phoneNo: Int = 123
val name: String = "아우아"
var job: String = ""
var etc: String = ""
init { // 초기화 블록은 객체 생성할 때마다 처리
println("초기화 처리")
}
}
val pno = NoConstructor() // 객체 인스턴스 생성
println(pno.name + " " + pno.phoneNo)
val pno1 = NoConstructor()
println(pno1.name + " " + pno1.phoneNo)
초기화 처리
아우아 123
초기화 처리
아우아 123
📖 보조 생성자만 있는 클래스로 객체 생성
class User {
var name = ""
var age = 0
constructor(name: String, age: Int) {
this.age = age
this.name = name
}
}
val p1 = User("홍길동", 30)
println(p1.name + " " + p1.age)
val p2 = User("김길동", 29)
println(p2.name + " " + p2.age)
홍길동 30
김길동 29
📖 주 생성자에만 속성 정의
class People(var name: String, var age: Int) // 주 생성자만 정의
var p1 = People("김길동", 20) // 주 생성자로 객체 인스턴스 생성
println(p1.name + " " + p1.age)
val p2 = People("홍길동", 30)
println(p2.name + " " + p2.age)
김길동 20
홍길동 30
📖 보조 생성자 오버로딩
class PhoneNote (val phoneNo: Int, val name: String) {
var job: String = ""
var etc: String = ""
init {
println("초기화 처리")
}
// 보조 생성자 오버로딩: 매개변수가 다른 2개 정의
// 보조 생성자 중 하나는 주 생성자 호출
constructor(phoneNo: Int, name: String, job: String) : this(phoneNo, name) {
this.job = job
}
// 두 번째 보조 생성자는 첫 번째 보조 생성자 호출
constructor(phoneNo: Int, name: String, job: String, etc: String) : this(phoneNo, name, job) {
this.etc = etc
}
}
val pno1 = PhoneNote(1234, "우의정", "디자이너", "서울") // 매개변수가 4개인 보조 생성자 호출
println(pno1.name + " " + pno1.phoneNo)
val pno2 = PhoneNote(12345, "좌의정", "프로그래머") // 매개변수가 3개인 보조 생성자 호출
println(pno2.name + " " + pno2.phoneNo)
초기화 처리
우의정 1234
초기화 처리
좌의정 12345
📖 보조 생성자에 초깃값을 부여해 오버로딩 해소
class PhoneNote (val phoneNo: Int, val name: String) {
var job: String = ""
var etc: String = ""
init {
println("초기화 처리")
}
// 보조 생성자 오버로딩을 없애기 위해 초깃값 지정, 보조 생성자에서 주 생성자 호출
constructor(phoneNo: Int, name: String, job: String, etc: String = "") : this(phoneNo, name) {
this.job = job
this.etc = etc
}
}
val pno1 = PhoneNote(1234, "가나다", "디자이너", "서울") // 매개변수가 4개인 보조 생성자 호출
println(pno1.name + " " + pno1.phoneNo)
val pno2 = PhoneNote(12345, "라마바", "프로그래머") // 매개변수가 4개인 보조 생성자 호출
println(pno2.name + " " + pno2.phoneNo)
초기화 처리
가나다 1234
초기화 처리
라마바 12345
📔 멤버 속성과 멤버 메서드 활용
📖 접근 지정자
- public: 기본이 공개 상태이기 때문에 어디에서든 사용할 수 있음
- internal: 모듈 영역에서 모두 사용 가능. 프로젝트를 만들어 컴파일할 때 모듈 영역에 결정되므로 실제 프로젝트 내부에서 참조 가능
- protected: 자기 클래스나 이를 상속한 클래스에서만 참조 가능
- private: 자기 클래스 내부에서만 참조 가능
📖 패키지
패키지에는 전역 함수, 전역 변수, 클래스, 인터페이스 등을 정의 가능
// package example
fun bar() { // public
println("bar")
}
public var bar: Int = 5 // public은 어디서나 사용 가능
private set // 갱신이 setter인 경우, 파일 내부에서만 가능
internal val baz = 6 // internal은 모듈 단위에서 접근 가능
bar()
println(bar)
bar
5
📖 클래스 멤버 접근 지정
open class Super {
private val a = 1 // 비공개 접근 지정
protected open val b = 2 // 상속 접근 지정
internal open val c = 3 // 모듈 접근 지정
val d = 4 // 공개 접근 지정
protected class Nested { // 상속 내부 클래스
public val e: Int = 5
}
}
class SubClass : Super() {
// 비공개 접근 지정인 a 는 사용할 수 없음
override val b = 5 // 상속 접근 지정 b 사용
override val c = 7 // 모듈 접근 지정 c 사용
}
class Composition(val o: Super) {
fun display() {
println(o.c) // 모듈 접근 지정 사용 가능
println(o.d) // 상속 접근 지정 사용 가능
}
}
val s = SubClass()
val u = Composition(s)
u.display()
7
4
📖 주 생성자 접근 지정 처리
class C private constructor(val a: Int) { // 비공개 생성자
companion object {
private val bar = 100 // 컴패니언 객체 내의 비공개 속성
fun create(x: Int) : C { // 컴패니언 객체 내에서 생성자 메서드로 정의해 처리
return C(x)
}
fun getBar() = bar // 공개 메서드로 조회
}
fun foo() = getBar() // 컴패니언 객체의 비공개 속성의 결과를 조회
}
val c = C.create(200)
println(c.foo())
100