원시 타입에만 산술 연산자 사용 가능
data class Point(val x: Int, val y: Int) {
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
/// 확잠함수로도 나타냄 .
operator fun Point.plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
fun main() {
val p1 = Point(10, 20)
val p2 = Point(30, 40)
println(p1 + p2)
}
// 이런 식으로 두 타입이 같지않더라도 됨 repeat 수행
// int를 넣어주지만 스트링을 반환한다
operator fun Char.times(count: Int): String {
return this.toString().repeat(count)
}
fun main() {
println('a' * 3)
}
/// aaa
plus 함수앞에operator 키워드를붙여야한다
연산자 오버로딩 앞에 필수 operator 예약어
언어에서 미리 정해진 연산자만 오버로딩가능!
일반함수와마찬가지로 operator 함수도 오버로딩할수있다.
operator fun Point.plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
operator fun Point.plus(value: Int): Point {
return Point(x + value, y + value)
}
operator fun Int.plus(point: Point): Point {
return Point(this + point.x, this + point.y)
}
fun main() {
val p1 = Point(10, 20)
val p2 = Point(30, 40)
// 두 Point 객체를 더하는 함수를 호출합니다.
val p3 = p1 + p2
println(p3) // 출력: Point(x=40, y=60)
// Point 객체와 정수 값을 더하는 함수를 호출합니다.
val p4 = p1 + 5
println(p4) // 출력: Point(x=15, y=25)
// 정수 값과 Point 객체를 더하는 함수를 호출합니다.
val p5 = 5 + p1
println(p5) // 출력: Point(x=15, y=25)
}
## 복합 대입 연산자
plus 연산자 오버로딩하면 자동으로
+= -= 등 연산자도 같이 지원 이런걸 복합 대입 연산자
operator fun Point.plusAssign(other: Point) {
x += other.x
y += other.y
}
fun main() {
var p = Point(10, 20)
val q = Point(30, 40)
// p와 q를 더한 뒤, 그 결과를 p에 대입합니다.
p += q
println(p) // 출력: Point(x=40, y=60)
}
/// += 참조를 다른 참조로 바꿔 치기하거나...
/// 객체 내부 상태 변경하고 싶을 때.
val numbers = ArrayList<Int>()
numbers += 42
println(numbers[0])
// 42
변수가 변경 가능할 경우에만 복합 대입연산자 사용 가능
책에서는 += 연산은 참조를 다른참조로 바꿔치기한다.
왜? 기존 객체 상태를 변경함으로 바꿔치기한다 책에서 표현한듯..
// 책 코드 예시
val numbers = ArrayList<Int>()
numbers += 42
println(numbers[0])
+= 는 plus와 plusAssign 양쪽 컴파일 할 수있어서.
두 함수 모두 정의시 오류 발생.
클래스 일관적인 설계를 위해 동시 정의 금지!
코틀린 표준라이브러리 컬렉션에는 두가지 접근방법 제시
+,- 는 항상 새로운 컬렉션 반환
=+ , =- 변경 가능 컬렉션에 작용해 메모리 객체상태 변화 읽기전 용컬렉션인경우 변경 적용한 복사본 반환.
operator fun Point.unaryMinus(): Point {
return Point(-x, -y)
}
// 예시 코드
val p = Point(10, 20)
println(-p) // 출력: Point(x=-10, y=-20)
단항 연산자는 인자를 취하지 않음.
bd ++ -> 후위연산자 bd값 반환하고 증가
++bd -> 전위연산자 bd값 증가시키고 반환
자바에서 Equals CompareTo 대신
코틀린은 == 직접 사용가능 간결해짐.
=== 식별자 비교 연산자
class Point(val x: Int, val y: Int) {
override fun equals(other: Any?): Boolean {
// 최적화: 파라미터가 "this"와 같은 객체인지 살펴본다.
if (other === this) return true
// 파라미터 타입을 검사한다.
if (other !is Point) return false
// Point로 스마트 캐스트해서 x와 y 프로퍼티에 접근한다.
return other.x == x && other.y == y
}
}
// 예시 코드
println(Point(10, 20) == Point(10, 20)) // true
println(Point(10, 20) != Point(5, 5)) // true
println(null == Point(1, 2)) // false
정렬 최소 최댓값 비교에 Comparable 인터페이스 구현해서 구함.
한 객체의 다른객체의 크기를 비교해 정수로 나타내줌
코틀린에서도 CompareTo 지원
관례를 적용해 인터페이스 아넹있는 compareTo 메소드 호출
< , > , <= , >= 는 compareTo 호출 컴파일됨.
class Person(
val firstName: String,
val lastName: String
) : Comparable<Person> {
override fun compareTo(other: Person): Int {
return compareValuesBy(this, other,
Person::lastName, Person::firstName
)
}
}
val pl = Person("Alice", "Smith")
val p2 = Person("Bob", "Johnson")
println(pl < p2) // false
in 연산자와 대응되는 함수는 contain
data class Rectangle(val upperLeft: Point, val lowerRight: Point)
operator fun Rectangle.contains(p: Point): Boolean {
return p.x in upperleft.x until lowerRight.x &&
p.y in upperleft.y until lowerRight.y
}
val rect = Rectangle(Point(10, 20), Point(50, 50))
println(Point(20, 30) in rect) // true
println(Point(5, 5) in rect) // false
val now = LocalDate.now()
val vacation = now..now.plusDays(10)
printIn(now.plusWeeks(1) in vacation) // true
// now(오늘)부터 시작해 10일짜리 범위를 만든다.
// 그 특정 날짜가 날짜 범위 안에 들어가는지 검사한다.
// now..now.plusDays(10)이라는 식은 컴파일러에 의해 now.rangeTo(now.plusDays(10))으로 변환된다.
// rangeTo 함수는 LocalDate의 멤버는 아니며, Comparable에 대한 확장 함수이다.
// rangeTo 연산자는 다른 산술 연산자보다 우선순위가 낮다. 하지만 혼동을 피하기 위해 괄호로 인자를 감싸 주면 더 좋다.
val n = 9
printIn(0..(n + 1)) // 0..10
// 0..n + 1이라고 써도 되지만 괄호를 치면 더 의미가 명확해진다.
// 또한 0.n.forEach()와 같은 식은 컴파일할 수 없음에 유의하라.
// 범위 연산자는 우선순위가 낮아서 범위의 메소드를 호출하려면 범위를 괄호로 둘러싸야 한다.
(0..7).forEach { print("$it ") } // 0 1 2 3 4 5 6 7
// 범위를 괄호로 둘러싸라.
for(x in list) -> List.iterator() 호출
hasNext , next 호출 반복식을 ㅗ변환 됨.
val p = Point(10, 20)
val (x, y) = p
println(x) // 10
println(y) // 20
// data 클래스 타입이 아닌경우 어떻게 구현하는지
class Point(val x: Int, val y: Int) {
operator fun component1() = x
operator fun component2() = y
}
![](https://velog.velcdn.com/images/superkkj/post/16109ca8-bb3c-4d47-a948-8280994862b2/image.png)
- Data 클래스에서 주 생성자 프로퍼티는 컴파일러가 자동으로
- ComponetN 만들어 줌 .
- 구조 분해선언은 함수에서 여러값 반환 할 때 유용
data class NameComponents(val name: String, val ext: String)
fun splitFilename(fullName: String): NameComponents {
val result = fullName.split('.', limit = 2)
return NameComponents(result[0], result[1])
}
val (name, ext) = splitFilename("example.kt")
println(name)
println(ext)
///////
data class NameComponents(val name: String, val extension: String)
fun splitFilename(fullName: String): NameComponents {
val (name, extension) = fullName.split('.', limit = 2)
return NameComponents(name, extension)
}
val (name, extension) = splitFilename("example.kt")
println(name)
println(extension)
구조 분해선언을 한 예제들 ,,
코틀린 표준 라이브러리는 componentN 5까지 제공함
책에서는 그이상은 컴파일 오류발생.
fun printEntries(map: Map<String, String>) {
for ((key, value) in map) {
println("$key -> $value")
}
}
val map = mapOf("Oracle" to "Java", "JetBrains" to "Kotlin")
printEntries(map)
Oracle -> Java
JetBrains -> Kotlin
맵에서도 이렇게 활용 가능함.. 루프인가..??
Map.Entry 확장함수로 componet1,2 제공한다고 책에 나와있다.
위에 내용 코드로 예시를 들어줌
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): Type {
// 게터 로직을 정의합니다.
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Type) {
// 세터 로직을 정의합니다.
}
}
class Foo {
var p: Type by Delegate()
}
지연 초기화란? 객체 일부분 초기화하지 않고 남겨 뒀다가 실제로 사용시
초기화 하는패턴
class Person(val name: String) {
private var remails: List<Email>? = null
val emails: List<Email>
get() {
if (emails == null) {
emails = loadEmails(this)
// "_emails" 속성을 사용하여 데이터를 저장하고 이메일의 대리자/객체 역할을 합니다.
// Get your email on first access.
}
return emails!!
}
}
// Example usage:
val p = Person("Alice")
println("Loading emails for Alice...")
println(p.emails) // Loads emails for Alice
println(p.emails) // Returns previously loaded emails
뒷받침프로퍼티란 속성에대해 get /set을 내가 원하는대로 정의하는 것 그래서 속성을 저장하거나 읽는것
위에 코드에는 뒷받침 프로퍼티 기법을 사용
emails는 _emails라는프로퍼티에대한읽기 연 산 을 제 공 한 다 .
Null이 될수있는타입 _email , 널이 될수없는타입 Email
스레드 안전하지 않고,, 지연 초기화 필드가많으면?
위임 프로퍼티를 사용하자!
데이터를 저장할때 쓰이는 뒷받침 프로퍼티 값이 오직 한 번만 초기화됨을 보장하는 게터 로직을 캡슐화해줌 (뒷받침 프로퍼티자동?)
class Person(val name: String) {
val emails by lazy { loadEmails(this) }
// Uses the "lazy" delegate to lazily initialize the "emails" property with the result of "loadEmails()"
// The "this" keyword refers to the current instance of the "Person" class
// The lambda expression is only called the first time "emails" is accessed, and the result is cached for subsequent accesses
}
// Example usage:
val p = Person("Alice")
println("Loading emails for Alice...")
println(p.emails) // Loads emails for Alice
println(p.emails) // Returns previously loaded emails