3.1. Create collections in Kotlin
Kotlin didn’t adopt his own collection class,Standard Java collection classes are used
The sample
// Create a collection
fun setCollection(a){
val set= hashSetOf(1.7.53)
/ / create the list
val list= arrayListOf(1.7.53)
/ / create a map
// To is not a special structure, but a general function
val map= hashMapOf(1 to "one".7 to "seven".53 to "fifty_three")
for(number in set){
println(number)
}
for( (index,number) in map){
println("$index.$number")}// Find the type of the object
println(set.javaClass)
println(list.javaClass)
println(map.javaClass)
//class java.util.HashSet
//class java.util.ArrayList
//class java.util.HashMap
// Get the last element of the element
println("Element Last element:${list.last()}")
println("Maximum element value:${list.max()}")}Copy the code
3.2. Make the function easier to call
Named parameters
-
example
// Custom print toString fun <T> joinToString(collection:Collection<T>,separator:String,prefix:String,postfix:String):String{ val result=StringBuilder(prefix) for((index,element) in collection.withIndex()){ if(index>0) result.append(separator) result.append(element) } result.append(postfix) return result.toString() } Copy the code
-
When calling a function defined by Kotlin, it is possible to specify the names of some parameters explicitly. If a function is named when calling, it needs to specify the names of all subsequent parameters to avoid confusion
println(joinToString(list,prefix = "&",postfix = "&",separator ="." )) Copy the code
Default Parameter Value
-
In Kotlin, you can specify default values for parameters when you declare a function
-
The default value of the argument is encoded into the function being called, not where it is called
-
example
// Custom print toString, default parameter value fun <T> joinToString2(collection:Collection<T>,separator:String=",",prefix:String="",postfix:String=""):String{ val result=StringBuilder(prefix) for((index,element) in collection.withIndex()){ if(index>0) result.append(separator) result.append(element) } result.append(postfix) return result.toString() } Copy the code
Eliminate static utility classes: top-level functions and properties
-
In Kotlin, there is no need for static utility classes, and functions can be placed directly at the top of the code file without belonging to any class
-
These functions placed at the top of the file are still members of the package
-
Properties can also be placed at the top of the file
-
The top function
-
Declare joinToString() as the top-level function
packageFunction definition and call// The top-level function is declared outside the class fun joinToString(name:String):String{ return "helloworld" } class Join {}Copy the code
-
How does this work? When compiling this file will generate some kind of, because the JVM can only execute code in the class, when you were in the use of Kotlin, know these is enough, if you need to call these functions from Java, it is necessary to understand how it will be compiled, in order to facilitate understanding, let’s look at a piece of code, it will be compiled into the same class here
packageIii. Function definition and call;public class JoinKt { public static String joinToString(String name){ return "helloworld"; }}Copy the code
-
You can see the name of the class generated by Kotlin’s compilation, which corresponds to the name of the file containing the function. All top-level functions in this file are compiled as static functions of the class. Therefore, when calling this function from Java, it is as simple as calling any other static function
public class JavaTest { public void test(){ JoinKt.joinToString("heloworld"); }}Copy the code
-
All top-level functions are compiled as static functions of this class
-
-
Change the file class name
-
To change the name of the generated class that contains Kotlin’s top-level function, annotate the file with @jVMName at the beginning of the file, in front of the package name
@file:JvmName("StringFunctions") packageFunction definition and call// The top-level function is declared outside the class fun joinToString(name:String):String{ return "helloworld" } class Join {}Copy the code
-
-
Called in Java
public class JavaTest { public void test(){ StringFunctions.joinToString("heloworld"); }}Copy the code
-
The top attributes
-
Like functions, attributes can be placed at the top of a file
Keeping individual pieces of data outside of a class is uncommon, but still valuable
-
code
@file:JvmName("StringFunctions") packageFunction definition and call// The top-level function is declared outside the class fun joinToString(name:String):String{ return "helloworld" } // Top-level attributes declared outside the class //const val opCount=0 var opCount=0 class Join { fun performOperation(a){ // The top-level attribute is referenced in the method opCount++ println("Current count value:$opCount")}}Copy the code
-
By default, the top-level property, like any other property, is exposed to Java for use through accessors (a single getter for val, a pair of getters and setters for var).
-
The const keyword
-
For ease of use, if you want to expose a constant to Java as a public static final property, use const (this applies to all properties of primitive data types, as well as strings). Static final does not have a getter, so the const modifier is used to provide a getter. Maybe get rid of the getter, and then access the property directly.)
// Top-level attributes declared outside the class const val opCount=0 Copy the code
Const must precede val and cannot be modified by var. It is equivalent to: public static final int opCount=0;
-
-
-
3.3. Add methods to other people’s classes: extension functions and attributes
The introduction
- The title is obvious: add some extended functions to a class already written, add some of your own functions or attributes to the existing base
Using the step
Extension functions are defined outside the class
- Function declaration format:
- The name of the function is preceded by the class to be extended. For example, in this case, the String class that extends Java is preceded by the String.
- This is the receiver object used to call the extension function
code
-
packageFunction definition and call/** * Note: extended functions are defined outside the class */ fun String.lastChar(a):Char=this.get(this.length-1) /** * Extended class */ class ExtendUtils { fun test(a){ println("Kotlin".lastChar()) } } Copy the code
String indicates the recipient type, and “Kotlin” indicates the recipient object
-
Self-written extension functions can be written in any.kt file:
- Functions are declared outside the class;
- The function name is preceded by the type String of the extension class.
- Receiver object this
This adds an extra function to the String class that other classes can use directly, as well as Java classes
Pay attention to
In extension functions, you can directly access other methods and properties of the extended class
Extension functions do not allow you to break their encapsulation and access private or protected members
Import and extend functions
-
If you define an extension function, it does not automatically apply across the scope of the project. Instead, if you want to use it, you need to import it, just like any other class or function
-
To avoid accidental naming conflicts, Kotlin allows you to import individual functions using the same syntax as importing classes
importFunction definition and call lastCharval c="Kotlin".lastChar() Copy the code
-
Use the keyword as to change the name of the imported class or function
importFunction definition and call lastCharas last val c="Kotlin".last() Copy the code
When you have functions with the same name in different packages, it is necessary to rename them at import time so that they can be used in the same file. In this case, for general classes and functions, there is an option to identify the class or function by its full name
Call extension functions from Java
-
When called by Java, the Kotlin file ExtendUtils becomes ExtendUtilsKt, with a Kt suffix:
ExtendUtilsKt.lastChar("Ha ha"); Copy the code
Utility functions as extension functions
- Extension functions are nothing more than efficient syntactic candy for static functions that can use a more specific type as the receiver type rather than a class
An extension function that cannot be overridden
The static nature of extension functions means that they cannot be overridden by subclasses
Kotlin treats extension functions as static functions
Extended attributes
-
Extended properties provide a way to extend a class’s API to access properties using property syntax instead of function syntax
-
example
val String.lastChar:Char get() =get(length-1) Copy the code
-
As you can see, like the extension function, the extension property is just like a normal member property of the receiver.
-
Here, getter functions must be defined because there are no supported fields, so there is no implementation of the default getter. Likewise, initialization cannot be done because there is no place to store the initial value
-
If you define the same attribute on StringBuilder, you can set it to var because the contents of StringBuilder are mutable
// Declare a mutable extension property var StringBuilder.lastChar:Char get() =get(length-1) set(value:Char) {this.setCharAt(length-1,value) } Copy the code
// It can be accessed as if using member attributes println("Kotlin".lastChar()) val sb=StringBuilder("Kotlin?") sb.lastChar='! ' println(sb) >> n Kotlin! Copy the code
-
Pay attention to
When accessing an extended property from Java, you should explicitly call its getter: stringutilkt.getlastchar (“Java”)
3.4. Processing collections: mutable arguments, infix calls, and library support
An API that extends Java collections
-
The premise for this chapter is that the Kotlin collection is the same as the Java class, but with an extension to the API. You can look at an example of getting the last element in a list and finding the maximum value in a collection of numbers:
val strings:List<String> = listOf("first"."second"."fourteenth") println(strings.last()) val numbers:Collection<Int> = setOf(1.14.2) println(numbers.max()) fourteenth 14 Copy the code
-
The answer to why there are so many rich operations on sets in Kotlin is obvious: because they are declared as extension functions. Many extension functions are declared in the Kotlin library
Mutable arguments: Make a function support any number of arguments
-
When you call a function to create a list, you can pass any number of arguments to it:
var list=listOf(2.3.5.7.11) Copy the code
-
Look at the declaration of this function in the library
fun listOf<T>(vararg values:T):List<T>{... }Copy the code
-
Kotlin mutable parameters are similar to Java, but the syntax is slightly different: Instead of using three dots after the type, Kotlin uses the vararg modifier on the parameters
-
Expansion operator
- In Java, arrays can be passed as is, whereas Kotlin requires that you explicitly unpack arrays so that each array element can be called as a separate argument in a function. Technically, this feature is called
The expansion operator, when used, precedes the corresponding argument with a *
This example shows that by expanding the operator, you can combine values from an array with some fixed values in a single call, which is not supported in Java
- In Java, arrays can be passed as is, whereas Kotlin requires that you explicitly unpack arrays so that each array element can be called as a separate argument in a function. Technically, this feature is called
Handling of key-value pairs: infix calls and destruct declarations
-
You can create a map using the mapOf function
val map=mapOf(1 to "one".7 to "seven".53 to "fifty-three") Copy the code
-
The word to is not a built-in construct, but a special kind of function call called an infix call
-
In infix calls, no additional separators are added and the function name is placed directly between the target object name and the parameter. The following two calls are equivalent
1.to("one") Copy the code
The general call to function
1To "one"Copy the code
Call the to function with the infix symbol
-
Infix calls can be used with functions that take only one argument, either plain functions or extension functions. To allow functions to be called using infix symbols, you need to mark them with the infix modifier
Here is a simple declaration of the to function
infix fun Any.to(other:Any)=Pair(this,other) Copy the code
The to function returns an object of type Pair. Pair is a Kotlin library class that represents a Pair of elements
Two variables can be initialized directly from the contents of a Pair
val (number,name) = 1 to "one" Copy the code
Kotlin allows multiple variables to be declared at once. This technique is called a destruct declaration, which shows how it can be used with Pair:
-
1 to “one” creates a Pair, which is then expanded with a destruct declaration and assigned to the number and name variables, respectively
-
The destruct declaration feature is not only used for pairs, but also to initialize two variables using the map’s key and value contents
for((index,element) in collection.withIndex()){ if(index>0) result.append(separator) result.append(element) } Copy the code
-
The to function is an extension function that can create a pair of any elements, which means it is an extension of the generic receiver: you can write 1 to “one”, “one” to 1, list to list.size(), and so on
-
Look at the declaration of the mapOf function
fun <K,V> mapOf(vararg values:Pair<K,V>):Map<K,V> Copy the code
Like listOf, mapOf accepts a variable number of arguments, except that the arguments are key-value pairs
-
3.5 processing of strings and regular expressions
Kotlin strings are exactly the same as Java strings
You can pass a string created in Kotlin code to any Java function, and you can apply any Kotlin library function to a string received from Java code, without converting, without creating additional wrapper objects
Kotlin makes standard Java strings easier to use by providing a series of useful extension functions
It also hides some obscure functions and adds extensions that are clearer and easier to use
Split string
-
Kotlin provides some overloaded extension functions with different arguments called split. The value used to carry a regular expression requires a Regex, not a String, to ensure that when a String is passed to these functions, it is not treated as a regular expression
-
A dot or dash is used to separate the string
// Explicitly create a regular expression println(12.345 6. "A".split("\ \. | -".toRegex())) [12.345.6, A] Copy the code
-
Kotlin uses exactly the same regular expression syntax as in Java
-
The pattern here matches a dot (which we escape to indicate that we mean a literal, not a wildcard) or a dash
-
In Kotlin, the extension function toRegex is used to convert a string into a regular expression
-
-
For some simple cases, regular expressions are not needed, and other overloading of the split extension function in Kotlin supports any number of plain text string separators:
println(12.345 6. "A".split("."."-")) Copy the code
Regular expression and triple quoted string
-
Example: Resolve the full path name of a file to the corresponding component: directory, filename, and extension. There are two ways to do this
-
1. Use extension functions to process strings. The Kotlin library contains functions that can be used to get substrings before (or after) the first (or last) occurrence of a given delimiter
/** * use the String extension function to resolve the file path */ fun parsePath(path: String) { // The string before the last slash val directory = path.substringBeforeLast("/") // String after the last slash val fullName = path.substringAfterLast("/") val fileName = fullName.substringBeforeLast(".") val extension = fullName.substringAfterLast(".") println("Dir:$directory,name:$fileName,ext:$extension")}Copy the code
Path The part of the string before the last slash is the directory path; The part after the dot is the extension of the file; And the file name is somewhere in between
Parsing strings is much easier in Kotlin, and regular expressions are not required, which is very powerful
-
2. Use regular expressions
-
You can also use regular expressions from the Kotlin standard library
/** * use regular expressions to resolve file paths */ fun parsePath2(path:String){ val regex="" "(. +)/(. +) \. (. +) "" ".toRegex() val matchResult=regex.matchEntire(path) if(matchResult! =null) {// Destruct declaration is used here val (directory,filename,extension)=matchResult.destructured println("Dir:$directory,name:$filename,ext:$extension")}}Copy the code
// Use regular expressions to resolve file paths parsePath2("/User/yole/kotlin-book/chapter.adoc") - Dir:/User/yole/kotlin-book,name:chapter,ext:adoc Copy the code
-
The regular expression is written in a triple-quoted string. In such a string, no characters need to be escaped, including backslashes, so it can be used. Instead of \. To represent dots, as you would with a normal string literal
-
This regular expression divides a path into three groups separated by slashes and dots. The. Pattern matches from the beginning of the string, so the first group (.+) contains the substring before the last slash. This substring contains all the preceding slashes because they match the “any character” pattern, similarly, the second group contains the substring before the last dot, and the third group contains the rest
-
A multi-line, triple-quoted string
-
The purpose of the triple quoted string is not only to avoid escaping characters, but also to allow it to contain any character, including line breaks, and to provide an easier way to embed text containing line breaks into programs
-
For example, you can draw something with ASCII code
// A multi-line triple-quoted string val kotlinLogo="" |" / /. | / /. | / \ "" " println(kotlinLogo.trimMargin(".")) Copy the code
-
A triple quoted string can contain a newline without special characters, such as \n, or escaping the \ character
-
Because multi-line strings do not support escape sequences, if you need to use the literal dollar sign in the content of the string, you must use an embedded expression like this:
val price="" "${} '$' 99.9 "" " Copy the code
-
For better formatting, use the trimMargin function
3.6 make your code cleaner: local functions and extensions
Local function
-
To make your code cleaner, you can nest these extracted functions in functions so that you get the structure you want without any additional syntax overhead
-
example
-
The saveUser function is used to save the user information to the database and ensure that the User object contains valid data
-
code
/** * functions with duplicate code */ fun saveUser(user:User){ if(user.name.isEmpty()){ throw IllegalArgumentException("Can't save user ${user.id}:empty Name")}if(user.address.isEmpty()){ throw IllegalArgumentException("Can't save user ${user.id}:empty Address")}}Copy the code
// Function with duplicate code saveUser(User(1.""."")) Copy the code
There’s very little duplication here, and you probably don’t want to validate every special case of a user field in an all-encompassing method in the class
-
However, you can do this if you put your validation code in local functions to get rid of duplication and keep your code structure clean
/** * extract local functions to avoid duplication */ fun saveUser2(user:User){ // Declare a local function to validate all fields fun validate(user:User,value:String,fieldName:String){ if(value.isEmpty()){ throw IllegalArgumentException("Can't save user ${user.id}:empty $fieldName")}}// Call local functions to validate specific fields validate(user,user.name,"Name") validate(user,user.address,"Address") // Save user to database } Copy the code
-
Local functions that can access all parameters and variables in the function, we can remove the User argument
/** * Access the argument */ of the outer function in the local function fun saveUser3(user: User) { // Now you don't need to repeat the user argument in saveUser3 fun validate(value: String, fieldName: String) { if (value.isEmpty()) { // You can access the arguments of external functions directly throw IllegalArgumentException("Can't save user ${user.id}:empty $fieldName") } } validate(user.name, "Name") validate(user.address, "Address") // Save user to database } Copy the code
-
Continue to improve by putting the validation logic in the User class extension function
packageFunction definition and callimport java.lang.IllegalArgumentException /** * extract logic to extension function */ fun User.validateBeforeSave(a){ fun validate(value:String,fieldName:String){ if (value.isEmpty()) { // The User attributes can be accessed directly throw IllegalArgumentException("Can't save user $id:empty $fieldName") } } validate(name, "Name") validate(address, "Address")}class User(val id:Int.val name:String,val address:String) { } Copy the code
/** * Save user information */ fun saveUser4(user:User){ // Call the extension function user.validateBeforeSave() // Save user to database } saveUser4(User(1.""."")) Copy the code
-
Extension functions can also be declared as local functions, but deeply nested local functions are often confusing, so multi-layer nesting is generally not recommended
-
3.7, summary
Instead of defining its own collection class, Kotlin provides a richer API based on the Java collection class
Kotlin can define default values for function parameters, which greatly reduces the need for overloaded functions, and named parameters make calls to multi-parameter functions more readable
Kotlin allows for a more flexible code structure: functions and attributes can be declared directly in a file, not just as members in a class
Kotlin can extend the API of any class, including those defined in an external library, with extension functions and attributes, without modifying its source code and without runtime overhead
Infix calls provide a concise syntax for call-like operator methods that handle a single parameter
Kotlin provides a number of string-friendly functions for both regular strings and regular expressions
Triple-quoted strings provide a succinct way around the verbose escaping and string concatenation that Java normally requires
Local functions help you keep your code clean while avoiding duplication
Attachment:
Chapter 3 function definition and call. SVG
Kotlin’s learning journey has begun
Chapter 1 Kotlin: Definition and Purpose
Chapter 2 Kotlin foundation