Before the order
Java has language features associated with specific classes in the standard library. For example, objects that implement the java.lang.Iterable interface can be used in forEach loops. Kotlin also offers many features similar to this principle, but that implement specific language features by calling specific functions, a technique called conventions. (For example, you can use the + operator on objects of a class that implements a special method called plus.)
Because the set of interfaces that a class implements is fixed, Kotlin cannot modify an existing Java class to implement a language feature. However, it is also possible to make methods of any convention capable of the Kotlin convention by defining them as extensions of Java classes.
Kotlin doesn’t allow developers to customize their own operators, because Kotlin limits how many operators you can overload, as well as the names of their corresponding functions.
Arithmetic operator overloading
In Java, arithmetic operators are only allowed for primitive data types, and String is limited to the + operator. Arithmetic operators are not allowed for other classes.
The most straightforward example of using a convention in Kotlin is the arithmetic operator, which means that you can use arithmetic operators on any type as long as you implement the corresponding method of the convention. The operator keyword is used to denote that you use this method as an implementation of the convention.
Binary arithmetic operator
The operator | The function name | expression | conversion |
---|---|---|---|
*(multiplication operator) | times | a * b | a.times(b) |
/(division operator) | div | a / b | a.div(b) |
%(modulo operator) | rem | a % b | a.rem(b) |
+(addition operator) | plus | a + b | a.plus(b) |
-(subtraction operator) | minus | a – b | a.minus(b) |
Arithmetic operators of custom types have the same precedence as arithmetic operators of primitive data types.
The operand type of the operator function is not required to be the same. But you cannot interchange the two operands because Kotlin does not automatically support commutativity. To support commutativity, you need to define the functions of the corresponding arithmetic operators in the operation types on both sides.
Kotlin does not require that the return value type be the same as the operand type. It also allows overloading of the convention functions, that is, defining multiple operator functions of different parameter types.
data class Point(var x:Int,var y:Int)
operator fun Point.plus(point: Point):Point{
returnPoint(x + point.x,y + point.y)} operator fun Point. Plus (value: Int){println("x = ${x + value} y = ${y + value}")} fun main(args:Array<String>){val point1 = Point(3,4) val point2 = Point(3,4) println(point1 + point2) println(point1 + 1) }Copy the code
Operator functions and Java
Calling Kotlin’s operator in Java is very simple; you simply call the corresponding function of the operator like a normal function. However, since Java does not have the operator keyword, the only constraint on defining specific functions of the convention in Java is that the type and number of arguments match.
The plus method defines two addition operators in Java:
#daqi.java
public class Point {
public int x;
public int y;
public Point(int x ,int y){
this.x = x;
this.y = y;
}
public Point plus(Point p){
return new Point(x + p.x, y + p.y);
}
public Point plus(int p){
return new Point(x + p, y + p);
}
@Override
public String toString() {
return "x = " + x + " , y = "+ y; }}Copy the code
Declare the convention extension function for the Java class in Kotlin, using the addition operator:
#daqiKotlin.ktOperator Fun Point. Plus (longNum:Long):Point{operator fun Point.returnPoint(this.x + longNum.toInt(), This.y + longnum.toint ())} fun main(args:Array<String>){var point1 = Point(3,4) var point2 = Point(4,5) Println (point1 + 1) println(point2 + 1L)}Copy the code
Extension functions can nicely add Kotlin operator capabilities to existing Java classes, but they can’t access properties or methods that are private or protected.
Compound auxiliary operators
In addition to simple arithmetic operator overloading, Kotlin also supports compound assignment operator overloading, that is, the +=, -=, and other compound assignment operators.
The operator | The function name | expression | conversion |
---|---|---|---|
* = | timesAssign | a *= b | a.timesAssign(b) |
/ = | divAssign | a /= b | a.divAssign(b) |
% = | remAssign | a %= b | a.remAssign(b) |
+ = | plusAssign | a += b | a.plusAssign(b) |
– = | minusAssign | a -= b | a.minusAssign(b) |
Compound auxiliary operators can be used when an operator function of a type is defined that returns the basic arithmetic operator of that type, and the type of the right-hand operand matches the arguments of the operator function. For example, the plus function defines different argument types:
operator fun Point.plus(point: Point):Point{
x += point.x
y += point.y
return this
}
operator fun Point.plus(value: Int):Point{
x += value
y += value
return this
}
Copy the code
Use the compound assignment operator += with the plus function:
Fun main(args: Array<String>) {var point1 = Point(3,4) var point2 = Point(4,5) point2 += point1 point2 += 1}Copy the code
This means that both the methods of the basic arithmetic operator and the methods of the compound assignment operator can be called when the compound auxiliary operator is used. The compiler raises an error if there are operator methods that match the basic arithmetic operator of the bilateral operand type and operator methods that match the compound assignment operator. The solution:
- Converts the operator to the corresponding
operator
Method, which calls the method directly. - Replace var with val to cause the compiler to call that of the compound assignment operator
operator
Method (for example: plusAssign)
Operators and sets
Collections are supported in the Kotlin library using +, -, +=, and -= to add or subtract elements. The + and – operators always return a new collection, and the += and -= operators always modify the collection in place.
Unary operators and bitwise operators
The operator | The function name | expression | conversion |
---|---|---|---|
+ | unaryPlus | +a | a.unaryPlus() |
– | unaryMinus | -a | a.unaryMinus() |
! | not | ! a | a.not() |
++ | inc | +, + + a | a.inc() |
— | dec | A -, – a | a.dec() |
When the inc and dec functions are defined to overload the increment and decrement operators, the compiler automatically supports the same semantics as the prefix and postfix increment operators for ordinary numeric types. For example, to call the prefix form ++a, the steps are:
- Assign the result of a.inc() to a
- Return the new value of a as the result of the expression.
Comparison operator
As with arithmetic operators, Kotlin allows you to overload comparison operators (==,! =, >, <, etc.). The comparison can be done directly using the operator, rather than calling equals or compareTo as Java does.
The equal operator
If the == operator is used in Kotlin, it is converted to a call to equals. ! The = operator is also converted to a call to equals, but the result is reversed.
Unlike other operators, == and! = can be used for airlift arithmetic because these operators check for null operands. Null == null is always true.
expression | conversion |
---|---|
a == b | a? .equals(b) ? : (b === null) |
a ! = b | ! (a? .equals(b) ? : (b === null)) |
When you override equals, you can refer to the equals function automatically generated by the data class:
public boolean equals(@Nullable Object var1) {
if(this ! = var1) {if (var1 instanceof Point) {
Point var2 = (Point)var1;
if (this.x == var2.x && this.y == var2.y) {
return true; }}return false;
} else {
return true; }}Copy the code
- Returns true when comparing own objects.
- If the type is different, return false.
- Check by key field, return true if condition is met.
Kotlin provides the identity operator (===) to check whether two parameters are references to the same object, the same as Java’s == operator. But = = = and! == (identity check) cannot be overridden, so there is no convention for them.
The == operator and! The = operator uses only equals(other: Any?). : Boolean, which can be overridden to provide a custom equality detection implementation. No other functions of the same name (such as equals(other: Point)) or extension functions are called, because implementations inherited from the Any class always take precedence over extension functions and other functions of the same name.
Sort operator
In Java, classes can implement the Comparable interface and determine whether one object is greater than another in the compareTo method. But only basic data types can be compared using < or >; all other types have no concise syntax for calling compareTo methods and require explicit calls.
Kotlin supports the same Comparable interface (whether Java or Kotlin’s Comparable interface), and the comparison operator will be converted to the compareTo method. All classes that implement the Comparable interface in Java can use the comparison operator in Kotlin.
expression | conversion |
---|---|
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a >= b | a.compareTo(b) >= 0 |
a <= b | a.compareTo(b) <= 0 |
The compareValuesBy function is provided in the Kotlin library to succinctly implement the compareTo method. This method takes two objects to compare, and a method reference to the value used for the comparison:
data class Point(var x:Int,var y:Int):Comparable<Point>{
override fun compareTo(other: Point): Int {
returncompareValuesBy(this,other,Point::x,Point::y) } } fun main(args: Array<String>) {val point1 = Point(3,4) var point2 = Point(4,5) println("result = ${point1 < point2}")}Copy the code
The equals and compareTo methods already add an operator to the parent class and need not be overloaded.
Convention of sets and intervals
The most common way to handle combinations is to get and set elements by subscript, and to check if elements belong to the current collection. Kotlin provides operator syntax support for these operations:
- Use the subscript operator
a[b]
Gets or sets the element. - use
in
Operator, which checks whether an element is in a set or interval, can also be used for iteration.
Subscript operator
Reading an element using the subscript operator is converted into a call to the GET operator method. Set is called when an element is written.
expression | conversion |
---|---|
a[i] | a.get(i) |
A [i_1,……, i_n] | Atul gawande et (i_1,… , i_n) |
a[i] = b | a.set(i, b) |
A [i_1,…… i_n] = b | A.s et (i_1,… , i_n, b) |
A Map can also use subscript operators to pass keys as subscripts into the subscript operator to obtain a value. For mutable maps, you can also use the subscript operator to change the value of the corresponding key.
Note: Arguments to GET can be of any type, so when using the subscript operator on map, the argument type is the type of the key.
The in operator.
The IN operator is used to check whether an object belongs to a collection. It is a convention, and the corresponding function is contains.
expression | conversion |
---|---|
a in c | c.contains(a) |
RangTo agreed
When an interval needs to be created, it is always created using.. Operator. . The operator is a convention for calling the rangeTo function.
expression | conversion |
---|---|
start.. end | start.rangeTo(end) |
You can define a rangeTo function for any class. However, if the class implements a Comparable interface, you can create an interval directly using the rangeTo function provided by the Kotlin standard library for the Comparable interface.
public operator fun <T : Comparable<T>> T.rangeTo(that: T): ClosedRange<T> = ComparableRange(this, that)
Copy the code
Use Java8’s LocalDate to construct a range of dates:
fun main(args: Array<String>) {
val now = LocalDate.now()
val vacation = now .. now.plusDays(10)
println(now.plusWeeks(1) in vacation)
}
Copy the code
. Operator notes:
- . Operators have lower precedence than arithmetic operators, but it is best to enclose arguments to avoid confusion:
0.. (n + 1)Copy the code
- When an interval expression calls a functional Api, the interval expression must be enclosed first; otherwise, compilation fails:
(0.. 10).filter { it % 2 == 0 }.map { it * it }.forEach { println(it) }Copy the code
The iterator agreed
The in operator can be used in a for loop to indicate that the iteration is being performed. This means that Kotlin’s for loop will be converted to a call to list.iterator(), which then calls hasNext and next methods repeatedly.
Iterator methods are also a convention in Kotlin, which means that iterator() can be defined as an extension function. For example, the Kotlin library defines an extension function iterator for Java’s CharSequence, which allows us to iterate over a regular Java string.
for(s in "daqi"){
}
Copy the code
Deconstruction statement
Kotlin provides destructuring declarations that allow you to expand a single compound value and use it to initialize multiple individual variables.
Fun main(args: Array<String>) {val point = point (3,4) val(x,y) = point}Copy the code
A destructible declaration looks like a normal variable declaration, but it has multiple variables in parentheses. But in fact, the deconstruction declaration also uses the principle of convention. To initialize each variable in the deconstruction declaration, it is necessary to provide the corresponding componentN function (where N is the position of the variable in the declaration).
Val point = point (3,4) val y = point.component2()Copy the code
Data classes
Kotlin provides an easy way to generate a data container by declaring a class as a data class, that is, a data class.
The compiler automatically generates the following methods from all properties declared in the main constructor of the data class:
- equals()/hashCode()
- toString()
- ComponentN () corresponds to all attributes in declarative order
- copy()
The data class must meet the following requirements:
- The primary constructor needs to have at least one argument (the default argument can be used to implement the no-argument primary constructor)
- All arguments to the main constructor need to be marked as val or var
- Data classes cannot be abstract, open, sealed, or internal
The equals method checks whether all properties declared in the main constructor are equal; HashCode () generates a hash based on all the attributes declared in the main constructor; ComponentN () is generated in the order of all the properties declared in the main constructor; ToString () generates a string of the following format: “Point(x=3, y=4)”.
Data classes that explicitly implement equals(), hashCode(), or toString(), or those functions that have final implementations in their parent classes, use existing functions; Data classes are not allowed to provide explicit implementations of componentN() and copy().
If the class is not a data class, and objects of that class can be used to destruct the declaration, manually declare the corresponding operator modifier componentN() function (both member and extension functions can be used) :
fun main() {val(x,y) = Piont(1,2)} class Piont(val x:Int,val y:Int){operator fun component1():Int{return x
}
operator fun component2():Int{
return y
}
}
Copy the code
Usage scenarios
- Traverse map
Use the destruct declaration to quickly get the keys and values of the entries in the map, quickly traversing.
for ((key, value) inMap) {// Directly use the key, value}Copy the code
- Return multiple variables from a function
Create a data class that requests to store the returned information, using a destruct declaration to divide it into different values when a method is called to retrieve the returned information:
Data class Result(val resultCode: Int,val status: Int,val Body :String) Fun getHttpResult(...) : Result {// various calculationsreturnResult(resultCode, Status, josnBody)} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - / / get the return value val (the resultCode, status,josnBody) = getHttpResult()Copy the code
Note: We can also return two variables using the Pair class from the standard library as the return value.
- Destruct in a lambda expression
Similar to map traversal, the map. Entry argument in lambda is destructed to declare:
val map = mapOf(1 to 1)
map.mapValues { (key, value) ->
"key = $key ,value = $value "
}
Copy the code
Pay attention to
Because the data class componentN() is generated in the order of all the properties declared in the main constructor. That is, component1() returns the first value declared in the main constructor, Component2 () returns the second value declared in the main constructor, and so on.
For variables that are not needed in the destruct declaration, you can replace their names with underscores, and Kotlin will not call the corresponding componentN()
Fun main(args: Array<String>) {val point = point (3,4) val(_,y) = point println(y)}Copy the code
component1()
fun main(args: Array<String>) {val point = point (3,4) // the y-coordinate should be the second position, but since the _ placeholder is not used, it will be assigned to component1(), which uses the x-coordinate to assign the y-coordinate. val(y) = point println(y) }Copy the code
Who call
References to deconstructing declarations are often accompanied by calls to drop out. But dropping is not a convention. It is a way for methods decorated with the Infix keyword to be called just like basic arithmetic operators. That is, the dot and parentheses of the calling function are ignored and the function name is placed between the target object and the parameter.
// stop calling 1 to"one"// Call 1.to("one")
Copy the code
Infix functions must meet the following requirements:
- Member functions or extension functions
- There’s only one parameter
- Parameters cannot accept variable parameters and cannot have default values
Usage scenarios
- interval
Use.. The interval created by the operator is a closed interval. When we need to create an inverted or semi-closed interval, or even set the step of the interval, the downTo, until and step used are not keywords, but methods modified by infix keyword one by one, which are rendered by dropping calls.
- map
When creating a map, use dropouts on keys and vlaue to add elements to improve readability.
val map = mapOf("one" to 1,"two" to 2)
Copy the code
Dropout call priority
Infix function calls take precedence over arithmetic operators, type conversions, and rangeTo operators. So 0 until n * 2 is equivalent to 0 until (n * 2).
But infix function call priority is higher than Boolean operators && and | |, is, and in testing, and other operators. So 7 in 0 until 10 is equivalent to 7 in (0 until 10).
References:
- Kotlin in Action
- Kotlin website
Android Kotlin series:
Kotlin’s Knowledge generalization (I) — Basic Grammar
Kotlin knowledge generalization (2) – make functions easier to call
Kotlin’s knowledge generalization (iii) — Top-level members and extensions
Kotlin knowledge generalization (4) – interfaces and classes
Kotlin’s knowledge induction (v) — Lambda
Kotlin’s knowledge generalization (vi) — Type system
Kotlin’s knowledge induction (7) — set
Kotlin’s knowledge induction (viii) — sequence
Kotlin knowledge induction (ix) — Convention
Kotlin’s knowledge induction (10) — Delegation
Kotlin’s knowledge generalization (xi) — Higher order functions
Kotlin’s generalization of knowledge (xii) – generics
Kotlin’s Generalization of Knowledge (XIII) — Notes
Kotlin’s Knowledge induction (xiv) — Reflection