1. 클래스 연관관계 알아보기
클래스 관계
- 상속관계 (is a) : 클래스를 상속해서 하나의 클래스처럼 사용한다.
- 연관관계 (has a) : 클래스를 상속하지 않고 내부적인 속성에 객체를 만들어서 사용한다.
- 결합관계 (약한 has a) : 연관관계를 구성하는 방식 중에 클래스 간의 주종관계 없이 단순하게 사용하는 관계를 말한다.
- 조합관계 (강한 has a) : 연관관계를 구성하는 방식 중에 클래스 간의 주종관계가 있어서 따로 분해해서 사용할 수 없는 관계를 말한다.
- 의존관계 (사용 has a) : 필요한 클래스를 매개변수 등으로 받아 필요한 시점에 사용하는 관계를 말한다.
결합(Aggregation) 관계
단순하게 사용하는 클래스에서 사용된 클래스의 객체를 속성으로 만들어서 사용
보통 주 생성자에 객체를 전달받아서 구성
다른 클래스를 단순하게 사용할 뿐 두 클래스가 만든 객체가 동일한 생명주기일 필요는 없다
즉, 한 클래스의 객체가 소멸해도 다른 클래스의 객체는 계속 활용할 수 있다.
class Address(
val streetNum: Int,
val city: String,
val state: String,
val country: String
) {
fun printAddr() {
println("주소 = $streetNum $city $state $country")
}
}
class College(
val collegeName: String,
val collegeAddr: Address // 다른 클래스를 속성에 할당
)
val ad1 = Address(55,"관악구","서울시","대한민국")// 주소 생성
val obj1 = College("서울대",ad1) // 주소 배정
println(obj1.collegeName) // 서울대
obj1.collegeAddr.printAddr() // 주소 = 55 관악구 서울시 대한민국
조합(Composition) 관계
결합관계보다 더 결합이 강해서 두 클래스는 주종관계이고 생명주기도 동일하다.
두 클래스는 항상 같이 생성되고 같이 소멸하는 구조일 경우에만 사용하는 관계이다.
class CarEngine { // 조합대상 클래스
fun startEngine() {
println("엔진 가동")
}
}
open class Car( // 베이스 클래스
var colour: String,
var maxi_speed: Int
) {
fun carDetails() {
println("차 색상 : $colour , 최고 속도 : $maxi_speed")
}
}
class CarProduct(color: String, max_speed: Int) : Car(color, max_speed) { // 조합을 구성하는 클래스
lateinit var carEngine: CarEngine
fun startCarProduct() {
carEngine = CarEngine() // 다른 클래스로 조합 구성
carEngine.startEngine()
}
}
val carJazz = CarProduct("Red", 240)
carJazz.carDetails()
carJazz.startCarProduct()
//차 색상 : Red , 최고 속도 : 240
//엔진 가동
자동차와 생산된 자동차의 의미를 분리하기 위해 자동차 클래스를 상위클래스로 지정.
- CarProduct 클래스는 Car를 상속.
- CarProduct 클래스 내부에 부품인 CarEngine 클래스의 객체를 속성에 할당.
의존(Dependency) 관계
특정 메서드 등에 객체를 전달받아서 사용만 하는 관계이다.
class Account(val accountNo: Int, var balance: Int = 0) { // 의존 처리 클래스
fun depoosit(amount: Int) {
println("입금")
balance += amount
}
}
class Customer(var balance: Int) {
fun makeDeposit(acc: Account) { // 메서드의 매개변수로 의존 클래스 전달
acc.depoosit(balance) // 내부에서 의존 클래스의 메서드 실행
}
}
val acc = Account(123)
val cus = Customer(3000)
cus.makeDeposit(acc)
println(acc.accountNo)
println(acc.balance)
//입금
//123
//3000
위 코드는 두 클래스를 정의하고 특정 기능을 처리하는 메서드에 객체를 전달해서 그 메서드가 처리가 완료되도록 지원한다.
특정 고객이 계좌를 만들 때 Account클래스의 객체를 전달해서 처리하도록 지정했다.
고객이 계좌를 소유하지만 변경할 수 있다.
2. 속성과 메서드 재정의
속정 정의
코틀린은 속성을 일반적인 변수처럼 정의하고 살용한다.
속서으로 사용하려면 게터와 세터를 사용자 정의를 통해 재정의할 수 있다.
속성의 메서드 세터를 비공개로 처리 -> 외부에서 변경 불가
class Counterr {
var value: Int = 0 // 변경 가능한 속성 정의
get() {
println("get value $field")
return field
}
private set // 비공개 속성을 사용해서 외부 갱신 금지
fun inc_() = value++ // 메서드로 내부에서 비공개 속성 갱신
}
val counter = Counterr()
for (i in 1..5) {
counter.inc_() // 외부에서는 메서드로 속성 갱신
}
println(counter.value)
//get value 0
//get value 1
//get value 2
//get value 3
//get value 4
//get value 5
//5
클래스 내 속성의 세터를 비공개 처리했다. (게터는 공개)
객체를 생성하고 속성을 조회할 수 있지만, 갱신은 할 수 없다.
이 속성의 값을 변경하려면 별도의 메서들르 만들어야 한다.
연산자 오버로딩
직접 사용자가 클래스를 정의할 때 연산자에 해당하는 메서드를 작성하면 객체가 연산자로 처리할 수 있다.
operator 예약어를 연산자를 재정의하는 메서드 앞에 붙인다.
class Amount(var total: Int, var balance: Int) { // 클래스 정의
operator fun plus(other: Amount) = Amount( // 메서드로 연산자 오버로딩
this.total + other.total,
this.balance + other.balance
)
operator fun plus(scale: Int) = Amount(
this.total + scale,
this.balance + scale
)
override fun toString() = "Amount($total, $balance)"
}
val amt = Amount(200, 100)
val amt2 = Amount(300, 100)
val amt3 = amt + amt2
println(amt3) // Amount(500, 200)
val amt4 = amt2 + 100
println(amt4) // Amount(400, 200)
클래스 내부에 operator로 지정한 plus 메서드를 정의했다.
동일한 이름이지만 매개변수 자료형이 달라서 메서드 오버로딩 방식으로 2개의 메서드를 정의할 수 있다.
infix처리
두개의 변수 가운데에 오는 함수
예약어 infix는 함수나 메서드를 정의할 때 가장 앞에 지정한다.
infix fun Int.add(x:Int):Int{
return this + x
}
println(3.add(5)) //8
println(3 add 5) //8
3. 특정 자료를 다루는 클래스 알아보기
데이터 클래스
클래스 간 전송 등을 하려면 데이터 즉 속성만을 가진 클래스가 필요하다. 이때 데이터 클래스를 정의해서 사용하면 편리하다.
toString(), hashCode(), equals(), copy(), componentsN() 메소드를 자동으로 만들어주는 클래스이다.
data class User(val name: String, var age: Int)
val u1 = User("Ej", 20)
val u2 = User("Ej", 20)
println(u1 == u2) // true
println(u1 === u2) // false
val u3 = User("Eunjeong", 20)
val u4 = u3.copy(age = 10)
println(u3 == u4) // false
println(u3 === u4) // false
동일한 값을 갖는 데이터 클래스의 객체를 생성하고 변수에 할당했다.
이 두 객체는 값은 같지만 다른 객체라는 것을 알 수 있다.
다른 객체를 만들고 이 객체를 복사해서 비교하면 값도 다르고 객체도 다른 것을 알 수 있다.
이넘 클래스
특정 상숫값을 관리하는 클래스이다.
- 예약어 enum을 클래스 앞에 작성
- 생성할 객체를 모두 내부에 정의하고 이 객체의 이름을 상수처럼 사용한다. -> 이름을 모두 대문자로 작성
enum class CardType{
SILVER, GOLD, PLATINUM
}
println(CardType.PLATINUM.name) // 객체의 이름
println(CardType.PLATINUM.ordinal) // 객체의 순서
println(CardType.SILVER < CardType.PLATINUM) // 객체간 비교
//PLATINUM
//2
//true
fun changeCardType(cardType:CardType) = when(cardType){
CardType.SILVER -> CardType.GOLD
CardType.GOLD -> CardType.PLATINUM
CardType.PLATINUM -> CardType.SILVER
}
이넘 클래스는 특정 범주를 객체로 처리한다.
이 범주 조건으로 처리하면 when 조건식으로 표현할 때 else를 정의하지 않아도 된다.
.ordianl 추천하지 않는다.
=> 중간에 순서를 바꾸면 ordinal이 바뀔 수 있기 때문.
대신, 프로퍼티를 하나 더 추가하는것이 더 안전
enum class CardType(val color: String) { // 클래스에 속성 추가
SILVER("gray"),
GOLD("yellow"),
PLATINUM("black")
}
println(CardType.SILVER.color) // 이넘 객체 내부의 속성 조회
println(CardType.valueOf("SILVER")) // 클래스 메서드르 이넘값 조회
이넘 클래스에 속성을 추가하여 사용할 수 있다.
The values() function is still supported, but we recommend that you use the entries property instead.
인라인 클래스
기본 자료형과 구별해서 새로운 자료형이 필요한 경우 인라인 클래스를 만든다.
@JvmInline을 사용하고, 예약어 value를 클래스 앞에 작성한다. (inline 키워드는 deprecated)
// For JVM backends
@JvmInline
value class Password(private val s: String)
// No actual instantiation of class 'Password' happens
// At runtime 'securePassword' contains just 'String'
val securePassword = Password("Don't try this in production")
인라인 클래스에는 기본 생성자에서 초기화된 단일 속성이 있어야 한다.
런타임 시 인라인 클래스의 인스턴스는 이 단일 속성을 사용하여 표시된다.
@JvmInline
value class Person(private val fullName: String) {
// Allowed since Kotlin 1.4.30:
init {
check(fullName.isNotBlank()) {
"Full name shouldn't be empty"
}
}
// Allowed by default since Kotlin 1.9.0:
constructor(name: String, lastName: String) : this("$name $lastName") {
check(lastName.isNotBlank()) {
"Last name shouldn't be empty"
}
}
}
+)
data object - 1.9.0
toString(), equals(), hashCode() 존재.
내부 프로퍼티가 없는 경우에 사용
- dataclass와 차이점 : copy, componentN이 없다.
data object EndOfFile : ReadResult
https://kotlinlang.org/docs/inline-classes.html#inline-classes-vs-type-aliases
'Study > Kotlin' 카테고리의 다른 글
Kotlin / 추상클래스, 인터페이스 알아보기 (0) | 2023.09.10 |
---|---|
Kotlin / 컬렉션 알아보기 (0) | 2023.09.02 |
Kotlin / 내장 자료형 알아보기 (0) | 2023.08.20 |
Kotlin / 클래스 알아보기 (0) | 2023.08.13 |
IntelliJ / git code author, usage 없애기 (0) | 2023.08.11 |