해당 내용은 이지스 퍼블리싱 카페에서 공부하며 올렸던 글을 다듬었습니다.
< 클래스 선언 >
- 클래스 키워드 : class
class User { }
- 생성자를 본문이 아닌 선언부에 작성할 수 있어서 본문이 없는 클래스도 의미가 있음
* 코틀린에서는 이런 클래스가 자주 등장!
- 클래스의 멤버 : 생성자, 변수, 함수, 클래스
- 생성자 : constructor() 키워드로 사용
- 클래스 안에 다른 클래스 선언 가능
- 객체 생성 구문
* 코틀린에서는 new 키워드 사용 안 함
* 클래스 이름과 같은 함수로 객체를 생성함
val user = User("kim")
- 객체를 생성할 때 생성자가 자동으로 호출됨
- 전달한 인자는 클래스에 선언된 생성자의 매개변수와 들어맞아야 함
- 코틀린에서 생성자는 주 생성자와 보조 생성자로 구분
[ 주 생성자 ]
* constructor 키워드로 클래스 선언부에 선언 (생략 가능)
* 한 클래스에 하나만 가능
* 컴파일러가 매개변수가 없는 주 생성자를 자동으로 추가
class User constructor() { } // 주 생성자 선언
class User() { } // constructor 키워드 생략 예
class User { } // 매개변수가 없는 주 생성자 자동 선언
class User(name: string, count: Int) { } // 주 생성자의 매개변수
=> 객체를 생성할 때 매개변수의 타입과 개수에 맞는 인자를 전달해야 함
class User(name: String, count: Int) { }
* init : 주 생성자를 이용해 객체를 생성할 때 특정한 로직을 수행할 수 있음
=> 주 생성자의 본문을 구현하고 싶을 때 사용
=> init 키워드로 지정한 영역은 객체를 생성할 때 자동으로 실행
class User(name: String, count: Int) {
init {
println("i am init...")
}
}
fun main() {
val user = User("kkang, 10)
}
---------- < 실행 결과 > ----------
i am init...
-----------------------------------
* 생성자의 매개변수는 기본적으로 생성자에서만 사용할 수 있는 지역 변수
// 생성자의 매개변수를 다른 함수에서 사용하는 예 (첫번째 방법)
class User(name: String, count: Int) {
// 생성자의 매개변수를 다른 함수에서 사용하는 예
// 클래스 멤버 변수 선언
var name: String
var count: Int
init {
// 클래스 멤버 변수에 생성자 매개변숫값 대입
this.name = name
this.count = count
}
fun someFun() {
println("name: $name, count: $count")
}
}
fun main() {
val user = User("kkang", 10)
user.someFun()
}
---------- < 실행 결과 > ----------
name: kkang, count: 10
----------------------------------
=> 주 생성자의 매개변수는 생성자 안에서만 사용할 수 있는 지역 변수지만
매개변수를 var나 val 키워드로 선언하면 클래스의 멤버 변수가 됨
※ 주 생성자만 유일하게 매개변수에 var나 val 키워드로 선언 가능!!!
// 생성자의 매개변수를 클래스의 멤버 변수로 선언하는 방법 (두번째 방법)
class User(val name: String, val count: Int) {
fun someFun() {
println("name: $name, count: $count")
}
}
fun main() {
val user = User("kkang", 10)
user.someFun()
}
---------- < 실행 결과 > ----------
name: kkang, count: 10
----------------------------------
[ 보조 생성자 ]
* 클래스의 본문에 constructor 키워드로 선언하는 함수
=> 여러 개 추가 가능
=> 객체를 생성할 때 자동으로 호출
=> 생성자 본문을 중괄호 { }로 묶어서 객체 생성과 동시에 실행할 영역 지정 가능
class User {
constructor(name: String) {
println("constructor(name: String) call...")
}
constructor(name: String, count: Int) {
println("constructor(name: String, count: Int) call...")
}
}
fun main() {
val user1 = User("kkang")
val user2 = User("kkang", 10)
}
---------- < 실행 결과 > ----------
constructor(name: String) call...
constructor(name: String, count: Int) call..."
----------------------------------
* 주 생성자와 보조 생성자를 모두 선언한다면 반드시 생성자끼리 연결해야 함
=> 주 생성자가 있다면 this() 구문을 이용해 주 생성자를 호출해야 함
class User(name: String) {
constructor(name: String, count: Int): this(name) {
println("class User in constructor(name: String, count: Int): this(name) call...")
}
}
* 보조 생성자가 여러 개라면 어떤 식으로든 주 생성자가 호출되게 해야 함 (규칙!!!)
class User(name: String) {
constructor(name: String, count: Int): this(name) {
println("First constructor(name, count) call...")
}
constructor(name: String, count: Int, email: String): this(name, count) {
println("Second constructor(name, count, email) call...")
}
}
- 주 생성자, 보조 생성자 모두 클래스의 생성자이며, 객체를 생성할 때 호출되는 건 똑같음
- 단지 주 생성자와 보조 생성자를 모두 선언한다면 보조 생성자로 객체를 생성할 때에는
반드시 주 생성자가 실행되게 만들어야 한다는 규칙이 있을 뿐
- 필수 매개변수와 실행 구문은 주 생성자에 작성하고
보조 생성자가 실행될 때 주 생성자도 함께 실행하려는 의도
- 결국 객체를 여러 형태로 생성할 수 있도록 매개 변수를 다양하게 구성할 때
생성자의 공통된 코드는 주 생성자에 작성하라는 의미
< 클래스를 재사용하는 상속 >
- 상속 : 클래스를 선언할 때 다른 클래스를 참조해서 선언하는 것
- 선언부에 콜론(:)과 함께 상속받을 클래스 이름을 입력해야 함
// 클래스 상속 형식
// 상위 클래스 : open 키워드 이용
open class Super {
}
// 하위 클래스 : 상위 클래스(Super)를 상속받아 하위 클래스(Sub) 선언
class sub: Super() {
}
- 상위 클래스 : 상속의 대상이 되는 클래스
- 하위 클래스 : 상속을 받는 클래스
- 코틀린의 클래스는 기본적으로 다른 클래스가 상속할 수 없음
* 상위 클래스 => open 키워드 사용
하위 클래스 => 이름: 상위 클래스명()
- 상위 클래스를 상속받은 하위 클래스의 생성자에서는 상위 클래스의 생성자를 호출해야 함 (규칙)
// 매개변수가 있는 상위 클래스의 생성자 호출
open class Super(name: String) {
}
class Sub(name: String): Super(name) {
}
- 상위 클래스의 생성자 호출문을 꼭 클래스 선언부에 작성할 필요는 없음
// 상위 클래스의 생성자 호출문을 꼭 클래스 선언부에 작성할 필요는 없음
// 하위 클래스에 보조 생성자만 있을 때 상위 클래스의 생성자 호출
open class Super(name: String) {
}
class Sub: Super {
constructor(name: String): super(name) {
}
}
- 상속이 주는 최고의 이점 : 상위 클래스에 정의된 멤버(변수, 함수)를 하위 클래스에서 자신의 멤버처럼 사용 가능
- 오버 라이딩 (재정의) : 상위 클래스에 선언된 변수나 함수를 같은 이름으로 하위 클래스에서 다시 선언하는 것
* 규칙 : 상위 클래스에서 오버 라이딩을 허용할 변수나 함수 선언 앞에 open 키워드를 추가하는 것
open 키워드로 선언한 변수나 함수를 하위 클래스에서 재정의 시 override라는 키워드를 추가해야 함
// 오버라이딩의 예
// 오버라이딩: 상위 클래스에 선언된 변수나 함수를 같은 이름으로 하위 클래스에서 재정의하는 것
// 주로 함수 재정의할 때 사용
open class Super {
open var someData = 10
open fun someFun() {
println("i am super class function: $someData")
}
}
class Sub: Super() {
override var someData = 20
override fun someFun() {
println("i am sub class function: $someData")
}
}
---------- < 실행 결과 > ----------
i am sub class function : 20
----------------------------------
- 접근 제한자 : 클래스의 멤버를 외부의 어느 범위까지 이용하게 할 것인지를 결정하는 키워드
- 접근 제한자의 종류
접근 제한자
|
최상위에서 이용
|
클래스 멤버에서 이용
|
public
|
모든 파일에서 가능
|
모든 클래스에서 가능
|
internal
|
같은 모듈 내에서 가능
|
같은 모듈 내에서 가능
|
protected
|
사용 불가
|
상속 관계의 하위 클래스에서만 가능
|
private
|
파일 내부에서만 이용
|
클래스 내부에서만 이용
|
* 접근 제한자를 생략하면 public이 기본
* internal의 모듈은 그래들이나 메이븐 같은 빌드 도구에서 프로젝트 단위 또는 같은 세트 단위를 가리킴
* private는 클래스에서 이용할 때는 해당 클래스 내부에서만 접근할 수 있음.
최상위에서 private로 선언하면 해당 파일 내부에서만 접근 가능
// 접근 제한자 사용 예
open class Super {
var publicData = 10
protected var protectedData = 20
private var privateData = 30
protectedData++ // 오류
privateData++ // 오류
}
class Sub: Super() {
fun subFun() {
publicData++
protectedData++
privateData++ // 오류
}
}
fun main() {
val obj = Super()
obj.publicData++
obj.protectedData++ // 오류
obj.privateData++ // 오류
}
< 코틀린 클래스 종류 >
- 데이터 클래스 : data 키워드 사용. 자주 사용하는 데이터를 객체로 묶어줌
* 주목적: 데이터를 다루는데 편리한 기능을 제공하는 것
- VO(Value-Object) 클래스를 편리하게 이용할 수 있게 해 줌
class NonDataClass(val name: String, val email: String, val age: Int) // 일반 클래스
data class DataClass(val name: String, val email: String, val age: Int) // 데이터 클래스
- equals() : 객체의 데이터를 비교하는 함수
* VO 클래스는 데이터를 주요하게 다루므로 객체의 데이터가 서로 같은지 비교할 때가 많음
=> 객체가 같은지(X), 객체의 데이터가 같은지(O)
// 데이터 클래스의 기본 사용법 + equals 함수 예제
class NonDataClass(val name: String, val email: String, val age: Int) // 일반 클래스
data class DataClass(val name: String, val email: String, val age: Int) // 데이터 클래스
fun main() {
val non1 = NonDataClass("kkang", "a@a.com", 10)
val non2 = NonDataClass("kkang", "a@a.com", 10)
val data1 = DataClass("kkang", "a@a.com", 10)
val data2 = DataClass("kkang", "a@a.com", 10)
println("non data class equals: ${non1.equals(non2)}")
println("data class equals: ${data1.equals(data2)}")
}
---------- < 실행 결과 > ----------
non data class equals: false
data class equals: true
----------------------------------
- 단, equals()는 주 생성자에 선언한 멤버 변수의 데이터만 비교 대상으로 삼는다.
// 데이터 클래스의 equals() 함수 예
// equals() 함수는 주 생성자에 선언한 멤버 변수의 데이터만 비교함!!!!
data class DataClass(val name: String, val email: String, val age: Int) {
lateinit var address: String
constructor(name: String, email: String, age: Int, address: String): this(name, email, age) {
this.address = address
}
}
fun main() {
// equals() 예제
val obj1 = DataClass("kkang", "a@a.com", 10, "seoul")
val obj2 = DataClass("kkang", "a@a.com", 10, "busan")
println("obj1.equals(obj2) : ${obj1.equals(obj2)}")
}
---------- < 실행 결과 > ----------
obj1.equals(obj2) : true
----------------------------------
- toString() : 객체가 가진 값을 확인해야 할 때
* 자주 사용!!
* 데이터 클래스와 일반 클래스의 toString() 반환 값이 다름
* toString() 역시 주 생성자의 매개변수에 선언된 데이터만 출력 대상
fun main() {
// toString 함수 예제
class NonDataClass(val name: String, val email: String, val age: Int)
data class DataClass(val name: String, val email: String, val age: Int)
val non = NonDataClass("kkang", "a@a.com", 10)
val data = DataClass("kkang", "a@a.com", 10)
println("non data class toString : ${non.toString()}")
println("data class toString : ${data.toString()}")
}
---------- < 실행 결과 > ----------
non data class toString : com.example.a211113test.DataClassKt$main$NonDataClass@17f6480
data class toString : DataClass(name=kkang, email=a@a.com, age=10)
----------------------------------
- 오브젝트 클래스 : 익명 클래스를 만들 목적
* 클래스를 선언하면서 동시에 객체를 생성해야 함
* 보통은 타입까지 함께 입력해서 선언
// 오브젝트 클래스의 사용 예
// 오류 원인 : 타입을 명시하지 않아 코틀린 최상위 타입인 Any로 취급
// => Any 타입 객체에는 data, some()이라는 멤버가 없어서 오류 발생
val obj = object {
var data = 10
fun some() {
println("data : $data")
}
}
fun main() {
obj.data = 30 // 오류
obj.some() // 오류
}
- 컴패니언 클래스 : companion 키워드 사용. 멤버 변수나 함수를 클래스 이름으로 접근하고자 할 때 사용
* 객체를 생성하지 않고서도 클래스 이름으로 특정 멤버를 이용 가능
* 자바의 static 대체
* 컴패니언 클래스를 지원하는 이유
① 자바는 모든 것을 클래스 내부에 선언하므로 객체로 묶일 필요학 없는 멤버는 static으로 선언하지만,
코틀린은 최상위에 함수나 변수를 선언할 수 있고
객체로 이용할 필요가 없는 멤버를 굳이 클래스에 선언하지 않아도 됨
② 코틀린에서는 클래스에 선언하는 멤버를 대부분 객체로 이용해야 함
// 컴패니언 클래스 예제
class MyClass {
companion object { // companion 키워드로 선언해야 함!!
var data = 10
fun some() {
println(data)
}
}
}
fun main() {
MyClass.data = 30
MyClass.some()
}
---------- < 실행 결과 > ----------
30
----------------------------------
'Programming > Kotlin' 카테고리의 다른 글
[6일차] 깡샘의 안드로이드 앱 프로그래밍 with 코틀린(Do it!) / 06 뷰를 이용한 화면 구성 (0) | 2022.05.08 |
---|---|
[5일차] 깡샘의 안드로이드 앱 프로그래밍 with 코틀린(Do it!) / 05 코틀린의 유용한 기법 (0) | 2021.12.21 |
[3일차] 깡샘의 안드로이드 앱 프로그래밍 with 코틀린(Do it!) / 03 코틀린 시작하기 (0) | 2021.12.04 |
[2일차] 깡샘의 안드로이드 앱 프로그래밍 with 코틀린(Do it!) / 02 안드로이드 앱의 기본 구조 (0) | 2021.12.02 |
[1일차] 깡샘의 안드로이드 앱 프로그래밍 with 코틀린(Do it!) / 01 개발 환경 준비하기 (0) | 2021.12.01 |