convention
If we define a special function of plus, then we can use the + operator on instances of that class, and that’s the convention
Kotlin prescribs many of these rules, but the programmer doesn’t need to know them, just rely on IDEA’s intelligent cues
Overloading arithmetic operators
Overloading binary arithmetic operations
Defines a member of the plus operator overload function
class Point(val x: Int.val y: Int) {
operator fun plus(other: Point): Point {
return Point(this.x + other.x, this.y + other.y)
}
override fun toString(a): String {
return "Point{x = $x, y = $y}"}}fun main(a) {
val point2 = Point(1.2) + Point(3.4)
println(point2)
}
Copy the code
-
You can see that the modifier operator is used to define an operator-overloaded function
-
The plus function corresponds to the + of the operator according to the convention concept
-
The corresponding overloaded functions include:
- How the operator overload function is written does not affect the precedence of the operator
Does not result in * / having a lower priority than + –
- Operator overloading of extension functions can be defined
operator fun Point.plus(other: Point): Point {
return Point(this.x + other.x, this.y + other.y)
}
Copy the code
Kotlin can override operators with either member functions or extension functions
- The types of the left and right sides of the operator can be different
operator fun Point.times(d: Double): Point {
return Point((this.x * d).toInt(), (this.y * d).toInt())
}
println(p * 1.5)
Copy the code
Note: The order of the overloaded functions can not be reversed. The Point type defined above is left and the Double type is right, so (1.5 * p) is not allowed
private operator fun Double.times(point: Point): Point {
return Point((this * point.x).toInt(), (this * point.y).toInt())
}
Copy the code
- To define overloaded operator extension functions, you can do this:
If I want to write a variable * 3 with the value ‘a’ to get a String of ‘aaa’, I would write val STR: String = ‘a’ * 3
Then we can create extension functions
private operator fun Char.times(count: Int): String {
TODO("Not yet implemented")}Copy the code
Now add the functions and return values we need
private operator fun Char.times(count: Int): String {
return toString().repeat(count)
}
Copy the code
Note, however, that the generated operator overload extension function defaults to private and you can remove the private visibility modifier if you don’t need it
The receiver is a Char, the argument is an Int, but the return value is a String, so Char + Int = String looks odd
- Kotlin does not define bitwise operators, so none of the bitwise operators can be overloaded, but Kotlin provides many infix calling functions
- SHL moves left with sign
- SHR moves right with sign
- Ushr unsigned shift to the right
- And the bitwise and
- The or or by location
- Xor (exclusive or
- Inv is reversed by bit
println(0x0F and 0x0F)
Copy the code
Overloaded compound operators
The += and -= operators are called compound operators in Kotlin
- Compound operator
+ =
In the case of operator overloading of mutable objects, it defines new reference objects that can be reused directly from previously written pairs+
Operator to override the function
var point = Point(1.1)
point += Point(2.3) Point = point + point (2, 3) = point + point (2, 3)
Copy the code
- use
+ =
When the compound operator changes the contents of the container and does not reassign new references, it is necessary to define operator overload functions
val arrayList = arrayListOf(1.2.3.4.5)
arrayList += 6
Copy the code
The += operator overload defines a function called plusAssign
public inline operator fun <T> MutableCollection<in T>.plusAssign(element: T) {
this.add(element)
}
Copy the code
- When the same class is written
plus
和plusAssign
Both operators overload functions. In theory, both functions should be called, so they should not exist at the same time. If they do exist at the same time, then you can change the receiver toval
Type, like thisplusAssign
It doesn’t work becauseval
Reassignment is not supported
Override the unary operator
Follow the previous tips, first write unary operator
val point = Point(1.2)
println(-point)
Copy the code
You can then use the IDE to generate operators to override extension functions (you can also select member functions)
private operator fun Point.unaryMinus(a): Point {
return Point(-this.x, -this.y)
}
Copy the code
-
Unary operators take no arguments
-
Function overloading of the increment and decrement operators
operator fun Point.inc(a): Point {
return Point(this.x++, this.y++)
}
Copy the code
The increment operator has operators such as I ++ and ++ I, both of which use the same extension function as the overloaded operator in Kotlin
var decimal = BigDecimal(0)
decimal++
println(decimal)
++decimal
println(decimal)
Copy the code
public inline operator fun BigDecimal.inc(a): BigDecimal = this.add(BigDecimal.ONE)
public inline operator fun BigDecimal.inc(a): BigDecimal = this.add(BigDecimal.ONE)
Copy the code
It was thought that ++ I and I ++ would become the same when the operator overloads the function
0
2
Copy the code
The decompilation turns out to be done by the powerful Kotlin compiler
I++ decompilation will look like this:
// i++ will look like this:
int i = 0;
System.out.println(i);
i = i + 1;
// ++ I would look like this:
int i = 0;
i = i + 1;
System.out.println(i);
Copy the code
See? One is print and then +1, the other is print and then +1, kotlin compiler YYds
Overload the comparison operator
= = = = =! = > < >= <= etc. These are comparison operators
Equal operator:equals
- By kotlin’s agreement,
= =
和equals
The corresponding = =
和! =
This can be compared to null, for examplea.equals(b)
A will check for null before callingequals
judge
= = =
Identity operator
(1) The identity operator, like the Java == operator, compares addresses, called references
(2) The identity operator === cannot be overloaded
= =
Operator does not support extended function operator overloading
The convention for == is equals, which already exists in Any. An extension function that defines operator overloading will never be called because the Any member function takes precedence over the extension function
- If you wrote
= =
If the extension function is overloaded with the operator of! =
The kotlin compiler will help you
override fun equals(obj: Any?).: Boolean {
// Compare references (addresses)
if (obj === this) return true
// Compare types
if (obj !is Point) return false
return (this.x == obj.x) && (this.y == obj.y)
}
Copy the code
val point1 = Point(1.2)
val point2 = Point(1.2)
if (point1 == point2) {
println(true) // true
} else {
println(false)}Copy the code
If you look closely, equals is not an overridden function. It is an overridden function, so there is no way to override equals
Sort operator: compareTo
The sort operator can be implemented in two ways
- implementation
Comparable
- Operator overloads functions
We’ll see the compareValuesBy function, which takes two comparison objects, selects the fields of the comparison object, and compares them in the order in which the arguments are passed. If the Person::firstName comparison results (if not equal), the Person::lastName comparison is not followed
Collections and conventions (operator overloading of collections)
[]
Operator overloads manipulate objects with the help of GET /set
In Kotlin we can do this:
Val value = map[key]
Mutable collection writes: mutableMap[key] = value
All of these operations are kotlin’s low-level operations. They are implemented using get and set functions. Kotlin changes the read to get(key) and the write to PUT (key). Value (a function like set)
Now how do we add similar operations to our custom classes?
Taking the previous Point class as an example, p[0] gets the x variable and P [1] gets the Y variable
Using our previous cleverness, we generated the following two functions using the IDE
private operator fun Point.set(index: Int, value: Int) {
when(index) {
1 -> this.x = value
2 -> this.y = value
}
}
private operator fun Point.get(index: Int): Int? {
return when(index) {
1 -> this.x
2 -> this.y
else -> null}}fun main(a) {
val point = Point(1.2)
println(point[0])
println(point[1])
point[0] = 10
point[1] = 20
}
Copy the code
Index corresponds to the index of p[index], so we can use the convention rules to overload the function with the GET operator
In convention (Contains function)
private class Rectangle(val upperLeft: Point, val lowerRight: Point) {
operator fun contains(point: Point): Boolean {
return point.x in min(upperLeft.x, lowerRight.y) until max(lowerRight.x, upperLeft.x) &&
point.y in min(upperLeft.y, lowerRight.y) until max(lowerRight.y, upperLeft.y)
}
}
fun main(a) {
val rectangle = Rectangle(Point(4.4), Point(0.0))
val point = Point(1.1)
println(point in rectangle)
}
Copy the code
RangTo agreedn.. n+1
val now = LocalDateTime.now()
valvacation = now.. now.plusDays(10)
println(now.plusWeeks(1) in vacation)
Copy the code
now.. Now. PlusDays (10) will be compiled to
ClosedRange vacation = RangesKt.rangeTo((Comparable)now, (Comparable)now.plusDays(10L));
Copy the code
Used in the for loopiterator
conventionin
fun main(a) {
for (c in "abcd") {
println(c)
}
}
Copy the code
In source code:
public operator fun CharSequence.iterator(a): CharIterator = object : CharIterator() {
private var index = 0
public override fun nextChar(a): Char = get(index++)
public override fun hasNext(a): Boolean = index < length
}
Copy the code
Deconstruct declarations and component functionscomponentN
To expand a compound value to initialize multiple variables is called a destruct declaration
But if you want to destruct a normal object, you need to add component functions,
The following figure shows that normal functions cannot use destruct declarations. You need to create member component functions or extend component functions. Of course, you can change the class to data Class Point
private operator fun Point.component1(a): Int = x
private operator fun Point.component2(a): Int = y
fun main(a) {
val p = Point(10.20)
val (x, y) = p
println("x = $x, y = $y")}Copy the code
Our destruct declaration is to assign the value of the composition function to the component function
data class NameComponents(val name: String, val extension: String)
fun splitFileName(fullName: String): NameComponents {
val split = fullName.split(".")
return NameComponents(split[0], split[1])}fun main(a) {
val (name, extension) = splitFileName("1.txt")
println("name = $name, extension = $extension")}Copy the code
A function can return multiple values, but the destruct declaration is not infinite. It only allows parsing the first five fields of an object
Reuse property access logic: delegate events
Basic usage of delegate attributes (conventionsby
和 getValue/setValue
Function)
In the previous delegate class, we learned that the essence of a delegate is to borrow a chicken from an egg
The essence of class delegation is that the client inherits an interface, but the implementation of the interface function is delegated to another subclass object that also implements the interface, and calls the function as a class combination
class C : InterfaceB {
override fun doSomething(a): Unit {
// do something}}class A(val cObject: InterfaceB = C()) : InterfaceB by cObject {
override fun doSomething(a): Unit {
cObject.doSomething()
}
}
Copy the code
The essence of attribute delegation in this section is that an attribute gives a get/set function to another object that also implements get/set(getValue/setValue)
class Foo {
var p:Type by Delegate()
}
Copy the code
In the code above Deletgate() generates an object to initialize the P property during delegation, and the client needs to be defined by convention to be able to be delegated by
And here’s the deal:
class Delegate : ReadWriteProperty<Foo, String> {
override fun getValue(thisRef: Foo, property: KProperty< * >): String {
TODO("Not yet implemented")}override fun setValue(thisRef: Foo, property: KProperty<*>, value: String) {
TODO("Not yet implemented")}}Copy the code
The convention states that the convention object needs to implement the ReadWriteProperty interface
Or the agreement goes something like this:
class Delegate {
operator fun getValue(foo: Foo, property: KProperty< * >): String {
TODO("Not yet implemented")}operator fun setValue(foo: Foo, property: KProperty<*>, s: String) {
TODO("Not yet implemented")}}Copy the code
Two operators need to be defined to override the getValue/setValue functions
Either of these conventions is fine
Use delegate properties: lazy initialization andby lazy()
Use another property to implement lazy loading ▲
In the past, if we want to achieve lazy loading of attributes, we need to use the temporary nullable attribute. When we need to load this attribute for the first time, we need to determine whether the temporary attribute is null
class Person(val name: String) {
private var _emails: List<String>? = null
val email: List<String>
get() {
if (_emails == null) {
_emails = loadEmail(this)}return _emails!!
}
private fun loadEmail(person: Person): List<String>? {
return listOf("[email protected]"."[email protected]")}}Copy the code
This method is used more, do not need any concept, directly make a lazy loading attribute, and from the code to judge that our email attribute is completely dependent on _email so when translated into Java source must be only _email attribute, Email only has a get/set function (val, so only get)
Kotlin providedlazy
Function to implement lazy loading
class Person(val name: String) {
val emails by lazy { loadEmail() }
private fun loadEmail(a): List<String> {
println("LoadEmail called")
return listOf("[email protected]"."[email protected]")}}Copy the code
-
Lazy is a library function that takes lambda () -> T and the return value of lazy is the return value of lambda,
-
Lazy is thread-safe. Lazy can switch the thread lock you want, or turn it off entirely, as needed
-
The lazy function finally returns an object with a getValue function
Lazy source code analysis
- So let’s start here
val emails by lazy { loadEmail() }
Copy the code
- use
by
Property, normally calledby
Of the following objectgetValue/setValue
Delta function, depending on the situation,lazy
There should be implementationsgetValue
function
Lazy {loadEmail()} This returns absolutely an object and should have either getValue or setValue functions
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
Copy the code
It’s got a new SynchronizedLazyImpl class object
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
private vallock = lock ? :this
override val value: T
get() { / / slightly}
}
Copy the code
The above is the core algorithm, to analyze the above code is also analyzed, but getValue such a function??
You can choose to install the extSee plug-in for IDEA and then view the extension functions
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any? , property:KProperty< * >): T = value
Copy the code
Find that it calls the get function of value
Just analyze his core method now
override val value: T
get() {
val _v1 = _value
if(_v1 ! == UNINITIALIZED_VALUE) {@Suppress("UNCHECKED_CAST")
return _v1 as T // return _v1 assigns to the email variable
}
// add a thread lock. The lock here is actually this object
return synchronized(lock) {
val _v2 = _value
if(_v2 ! == UNINITIALIZED_VALUE) {@Suppress("UNCHECKED_CAST") (_v2 as T) // return _v2 assigns to the email variable
} else {
// the return value of lambda is returned
valtypedValue = initializer!! (a)// store in _value, wait for next judgment, if! UNINITIALIZED_VALUE returns the object directly
_value = typedValue
// Initialize lambda
initializer = null
// Returns the lambda return value object
typedValue // return typedValue assigns to the email variable}}}Copy the code
UNINITIALIZED_VALUE, initialized at the beginning of _value in the above code, is assigned to the return value of the lambda. == UNINITIALIZED_VALUE Checks whether the value has been assigned
The implementation of _value and value is exactly the same as the lazy implementation of ▲ in the previous section with another attribute
So when we start delegating, _value is initialized, but value is still empty (no, value doesn’t actually have that field)
Implementing delegate properties
Now, we learned that we can implement delay with another object, and we can implement delegate that way
class ObservableProperty(val propName: String, var propValue: Number, val supportChange: PropertyChangeSupport) {
fun getValue(a): Number {
return propValue
}
fun setValue(value: Number) {
supportChange.firePropertyChange(propName, propValue, value)
propValue = value
}
}
class Person(_name: String, _age: Int, _scope: Double) {
private val supportChange = PropertyChangeSupport(this)
val name: String = _name
private val __age = ObservableProperty("age", _age, supportChange)
var age: Int
get() = __age.getValue() as Int
set(value) {
__age.setValue(value)
}
private val __scope = ObservableProperty("scope", _scope, supportChange)
var scope: Double
get() = __scope.getValue() as Double
set(value) {
__scope.setValue(value)
}
fun addPropertyChangeEvent(listener: PropertyChangeListener) {
supportChange.addPropertyChangeListener(listener)
}
fun removePropertyChangeEvent(listener: PropertyChangeListener) {
supportChange.removePropertyChangeListener(listener)
}
}
fun main(a) {
val person = Person("zhazha".23.98798.0)
person.addPropertyChangeEvent {
PropertyChangeListener {
println("field ${it.propertyName} changed from ${it.oldValue} to ${it.newValue}")
}
}
person.age = 22
person.scope = 1000000.0
}
Copy the code
The above example uses PropertyChangeSupport to monitor property changes. If the property value changes, it will be monitored.
class ObservableProperty(_propValue: Int, _supportChange: PropertyChangeSupport) : ReadWriteProperty<Person, Int> {
var propValue: Int = _propValue
val supportChange = _supportChange
override fun getValue(thisRef: Person, property: KProperty< * >): Int {
return propValue
}
override fun setValue(thisRef: Person, property: KProperty<*>, value: Int) {
supportChange.firePropertyChange(property.name, propValue, value)
propValue = value
}
}
open class PropertyChangeAware {
protected val supportChange = PropertyChangeSupport(this)
fun addPropertyChangeEvent(listener: PropertyChangeListener) {
supportChange.addPropertyChangeListener(listener)
}
fun removePropertyChangeEvent(listener: PropertyChangeListener) {
supportChange.removePropertyChangeListener(listener)
}
}
class Person(_name: String, _age: Int, _salary: Int) : PropertyChangeAware() {
val name: String = _name
var age: Int by ObservableProperty(_age, supportChange)
var salary: Int by ObservableProperty(_salary, supportChange)
}
fun main(a) {
val person = Person("zhazha".22.17000)
person.addPropertyChangeEvent {
PropertyChangeListener {
println("field ${it.propertyName} changed ${it.oldValue} to ${it.newValue}")
}
}
person.age = 23
person.salary = 500000
}
Copy the code
Delegate (getValue/setValue) ¶ Delegate (getValue/setValue) ¶ Delegate (getValue/setValue) ¶ Delegate (getValue/setValue) ¶ Delegate (getValue/setValue) ¶
We can also use a built-in delegate class to do this, so we don’t have to write it ourselves.
open class PropertyChangeAware {
protected val supportChange = PropertyChangeSupport(this)
fun addPropertyChangeEvent(listener: PropertyChangeListener) {
supportChange.addPropertyChangeListener(listener)
}
fun removePropertyChangeEvent(listener: PropertyChangeListener) {
supportChange.removePropertyChangeListener(listener)
}
}
class Person(_name: String, _age: Int, _salary: Int) : PropertyChangeAware() {
val name: String = _name
private val observer = { property: KProperty<*>, oldValue: Int, newValue: Int ->
supportChange.firePropertyChange(property.name, oldValue, newValue)
}
var age: Int by Delegates.observable(_age, observer)
var salary: Int by Delegates.observable(_salary, observer)
}
fun main(a) {
val person = Person("zhazha".22.20000)
person.addPropertyChangeEvent {
PropertyChangeListener {
println("field ${it.propertyName} changed ${it.oldValue} to ${it.newValue}")
}
}
person.age = 23
person.salary = 5000000
}
Copy the code
As far as we can tell, the right side of the by keyword can be a function call, another property, or any other expression that satisfies the delegate function
Change rules for attribute delegates
class C {
var prop: Type by MyDelegate()
}
Copy the code
Where MyDelegate is going to generate a property
and use an object of type KProperty to represent the type of that object, which is called
The compiler generates code:
class C {
private val <delegate> = MyDelegate()
var prop: Type
get() = <delegate>.getValue(this, <property>)
set(value) = <delegate>.setValue(this, <property>, value)
}
Copy the code
Save attribute values in map
Delegate by to a Map object
class Person {
private val _attributes = hashMapOf<String, String>()
fun setAttributes(attrName: String, value: String) {
_attributes[attrName] = value
}
// get() = _attributes["name"]!!
val name: String by _attributes
// get() = _attributes["company"]!!
val company: String by _attributes
}
fun main(a) {
val person02 = MapDemo.Person()
val data = mapOf("name" to "Dmitry"."company" to "Jetbrain")
for ((attrName, value) in data) {
person02.setAttributes(attrName, value)
}
println(person02.name)
println(person02.company)
}
Copy the code
The core code is here:
// get() = _attributes["name"]!!
val name: String by _attributes
// get() = _attributes["company"]!!
val company: String by _attributes
Copy the code
In plain English, the name of the variable is used as the key of the HashMap and the value is obtained