Article directory
- kotlin foundation building
kotlin foundation building
1. The most basic syntax of Kotlin
1.1 Variables
Var represents a mutable variable, and val represents an immutable variable. Note that they are not constants. The variable name is written in front and the type is written in the back. If the compiler can infer your type, then the type does not need to be declared.
var age:Int = 18
val name :String = "zhangsan"
The compiler automatically infers types.
var age = 18
val name = "zhangsan"
Null safe type compiler error
val name2: String = null
If you still want to assign an initialization value
val name2: String? = null
Note: String and String? Are two completely different types. They cannot be assigned to each other.
What if you still want to force compilation? Add two exclamation marks, !! means force. When you are sure that name2 is not null, you can forcefully assign it to name.
var age: Int = 18
var name: String = "zhangsan"
var name2: String? = null
fun main() {
name = name2!!
}
But if we do the opposite, can we assign name to name2?
Yes, name is definitely not empty. name2 contains both nullable and non-nullable values, so it can be assigned a value.
var age: Int = 18
var name: String = "zhangsan"
var name2: String? = null
fun main() {
name = name2!!
name2 = name
}
1.2 Function
fun declaration function, printLen function name, str: String parameters and parameter types, : String return value, "12312123 str " template string, str" template string,s t r " Template string, symbol can refer to a variable and combine this variable into a new string.
fun printLen(str: String): String {
println("12312123 $str")
return str
}
have a test
var age: Int = 18
var name: String = "zhangsan"
var name2: String? = null
fun main() {
// name = name2!!
// name2 = name
printLen("1231231")
}
fun printLen(str: String): String {
println("adads $str")
return str
}
2. Kotlin is fully compatible with Java, can it be called casually?
2.1 Syntax changes
Java and Kotlin interaction syntax changes
Kotlin can be written in a file and not necessarily in a class.
//Utils.kt
fun echo(name: String) {
println("$name")
}
Java must be written in a class. When calling the code in the kotlin file in java, the UtilsK suffix must be added with Kt. The kotlin file will be compiled into public static variables, so in the main function of java, the file name and function name can be called directly. .
// Main.java
public static void main(String[] args) {
UtilsKt.echo("hello");
}
Kotlin’s special syntax, starting with object, followed by a class declaration
object Test {
fun sayMessage(msg: String) {
println(msg)
}
}
transfer:
kotlin code
Test.sayMessage("hello")
java code
Test.INSTANCE.sayMessage("hello");
Next, the difference between the two languages of our Kangkang class
java code
TestMain.class
kotlin code
TestMain::class.java
To call the Java class class, JavaMain, in kotlin, since this class is a Java class, the JavaMain::class.java suffix needs to be added with .java
java code
public class JavaMain {
}
kotlin code
fun testClass(clazz: Class<JavaMain>) {
println(clazz.simpleName)
}
fun main() {
testClass(JavaMain::class.java)
}
The following demonstrates calling a kotlin class. If it is a kotlin class, KClass needs to be declared like this. When KotlinMain::class is called, it is called directly. There is no need to add .java to the suffix because it is a kotlin class.
kotlin code
class KotlinMain {
}
import kotlin.reflect.KClass
fun testClass(clazz: Class<JavaMain>) {
println(clazz.simpleName)
}
fun testClass(clazz: KClass<KotlinMain>) {
println(clazz.simpleName)
}
fun main() {
testClass(JavaMain::class.java)
testClass(KotlinMain::class)
}
2.2 kotlin keyword processing
For example, in java, there is a variable named in
public class JavaMain {
public static String in = "in";
}
In kotlin, in is a keyword. When calling java in kotlin, this variable cannot be called directly. Single quotes are required.
fun main() {
println(JavaMain.`in`)
}
3. Common problems encountered by novices using Kotlin
3.1 Kotlin has no encapsulated classes
Take a look at the demo
java code
public class A implements AInterface {
public static final A a = new A();
@Override
public void putNumber(int num) {
System.out.println("int");
}
@Override
public void putNumber(Integer num) {
System.out.println("Integer");
}
}
public interface AInterface {
void putNumber(int num);
void putNumber(Integer num);
}
kotlin code
fun main() {
A.a.putNumber(123)
}
Output: int
We found that the output is int instead of Interge, which means that when kotlin is running, it calls the method of the basic data parameters and does not execute the overloaded method of the encapsulated class.
3.2 Kotlin types are null value sensitive
java code
public class A {
public static String format(String string) {
return string.isEmpty() ? null : string;
}
}
Call the format method in kotlin,
fun main() {
val format = A.format("123")
val format1: String = A.format("123")
val format2: String = A.format("123")
}
Three return types,
The first type is automatically inferred. String with an exclamation mark is only present when Java and Kotlin interoperate. It cannot be declared manually. The compiler considers this type to be a Java type, but it is used in the Kotlin code, so it has exclamation mark
Second, the three types are declared manually.
3.3 Kotlin does not have static variables and static methods
After we add the @JvmStatic annotation, we can call this method in Java like kotlin.
This method will also be compiled into a public static method after the code is compiled.
object Test {
@JvmStatic
fun sayMessage(msg: String) {
println(msg)
}
}
java code
Test.sayMessage("hello")
Instead of this
Test.INSTANCE.sayMessage("hello");
4. Can functions also be nested? Pay attention to this feature
4.1 Characteristic syntax of functions
Function example
fun echo(name: String) {
println("$name")
}
fun echo(name: String ="zhangtao") {
println("$name")
}
fun echo(name: String) = println("$name")
Parameters can have default values,
fun echo(name: String ="zhangtao") {
println("$name")
}
Test, do not pass parameters, just take the default value
fun main() {
echo()
}
Advantages: It can greatly reduce the number of overloaded functions.
There is only one statement in the function body that can be directly assigned to this function.
fun echo(name: String) = println("$name")
4.2 Nested functions
Purpose:
A function that triggers recursion under certain conditions does not want to be accessed by an external function. The internal function can access the local variables of the external function.
fun function() {
var str = "helle world"
fun say(count: Int = 10) {
println(str)
if (count > 0) {
say(count - 1)
}
}
say()
}
test
fun main() {
function()
}
4.3 Extension functions
File is the class name, the method name of readText extension,
fun File.readText(charset: Charset = Charsets.UTF_8):
String = readBytes().toString(charset)
test is the same as our normal calling method.
fun main() {
val file = File("test.txt")
println(file.readText())
}
How to use our extension functions in java.
java code
public class A {
public static void main(String[] args) {
File file = new File("test.txt");
String s = FilesKt.readText(file, Charsets.UTF_8);
System.out.println(s);
}
}
Static analysis of extension functions:
demo: does not have polymorphic run types,
open class Animal
class Dog : Animal()
fun Animal.name() = "animal"
fun Dog.name() = "dog"
fun Animal.printfName(animal: Animal) {
println(animal.name())
}
fun main() {
Dog().printfName(Dog())
}
output
Decompile and look at this code
animal's extension function name()
fun Animal.name() = "animal"
//对应下面的反编译代码
@NotNull
public static final String name(@NotNull Animal $this$name) {
Intrinsics.checkNotNullParameter($this$name, "$this$name");
return "animal";
}
dog’s extension function name()
fun Dog.name() = "dog"
//对应下面的反编译代码
@NotNull
public static final String name(@NotNull Dog $this$name) {
Intrinsics.checkNotNullParameter($this$name, "$this$name");
return "dog";
}
Animal extension function printfName
fun Animal.printfName(animal: Animal) {
println(animal.name())
}
//对应下面的反编译代码
//@NotNull Animal $this$printfName 这个参数指的是扩展这个函数的那个对象
public static final void printfName(@NotNull Animal $this$printfName, @NotNull Animal animal) {
Intrinsics.checkNotNullParameter($this$printfName, "$this$printfName");
Intrinsics.checkNotNullParameter(animal, "animal");
String var2 = name(animal);
System.out.println(var2);
}
public static final void main() {
printfName((Animal)(new Dog()), (Animal)(new Dog()));
}
After the main function is decompiled, (Animal)(new Dog()), (Animal)(new Dog()) the Dog object we passed in is actually forced to Animal, so the extension method name() of animal is finally output. .
4.4 The syntax of DSL’s basic Lambda closure
java code conventional writing method
public class A {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
}
});
thread.start();
}
}
When writing Lambda, the Runnable object is omitted as a pair of brackets and arrows are added.
public class A {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
});
thread.start();
}
}
kotlin code
fun main() {
val thread = Thread({
-> Unit })
thread.start()
}
If the Lambda has no parameters, the arrow symbol can be omitted
fun main() {
val thread = Thread({
})
thread.start()
}
If the Lambda is the last parameter of the function, you can put the curly braces outside the parentheses
fun main() {
val thread = Thread(){
}
thread.start()
}
If the function has only one parameter and this parameter is a Lambda, you can omit the parentheses
fun main() {
val thread = Thread {
}
thread.start()
}
Lambda closure declaration
fun main() {
echo.invoke("123")
echo("123")
}
val echo = {
name: String ->
println(name)
}
The parameters are online, and there can only be a maximum of 22 parameters.
fun main() {
lambdaA(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
}
val lambdaA = {
a1: Int, a2: Int, a3: Int, a4: Int, a5: Int, a6: Int, a7: Int, a8: Int, a9: Int, a10: Int, a11: Int, a12: Int, a13: Int, a14: Int, a15: Int, a16: Int, a17: Int, a18: Int, a19: Int, a20: Int, a21: Int, a22: Int ->
println("!23")
}
Output: !23
If we add one more parameter for a total of 23 parameters, an exception will be thrown.
Lambda will be compiled into an anonymous inner class, Function23, with 23 parameters
There are only 22 parameters in the source code
solve:
Manually define a Function23.
4.5 Higher-order functions
kotlin code
fun main() {
onlyif(true) {
println("打印日志")
}
}
fun onlyif(isDebug: Boolean, block: () -> Unit) {
if (isDebug) block()
}
Key Point: Functions are “first-class citizens”
fun main() {
val runnable = Runnable {
println("run")
}
val function: () -> Unit
function = runnable::run
onlyif(true, function)
}
fun onlyif(isDebug: Boolean, block: () -> Unit) {
if (isDebug) block()
}
Output: run
Normal execution
4.6 Inline functions
Optimize code with inlining
Kotlin's Lambda is an anonymous object
You can use inline to modify the method, so that when the method is compiled, the method call will be disassembled into statement calls, thus reducing the creation of unnecessary objects.
fun main() {
val runnable = Runnable {
println("run")
}
val function: () -> Unit
function = runnable::run
onlyif(true, function)
}
inline fun onlyif(isDebug: Boolean, block: () -> Unit) {
if (isDebug) block()
}
Plus inline decompilation
public static final void onlyif(boolean isDebug, @NotNull Function0 block) {
int $i$f$onlyif = 0;
Intrinsics.checkNotNullParameter(block, "block");
if (isDebug) {
block.invoke();
}
}
main function, statement call
public static final void main() {
Runnable runnable = (Runnable)null.INSTANCE;
Function0 function = null;
function = (Function0)(new Function0(runnable) {
// $FF: synthetic method
// $FF: bridge method
public Object invoke() {
this.invoke();
return Unit.INSTANCE;
}
public final void invoke() {
((Runnable)this.receiver).run();
}
});
boolean isDebug$iv = true;
int $i$f$onlyif = false;
function.invoke();
}
No decompilation
public static final void onlyif(boolean isDebug, @NotNull Function0 block) {
Intrinsics.checkNotNullParameter(block, "block");
if (isDebug) {
block.invoke();
}
}
main function, method call
public static final void main() {
Runnable runnable = (Runnable)null.INSTANCE;
Function0 function = null;
function = (Function0)(new Function0(runnable) {
// $FF: synthetic method
// $FF: bridge method
public Object invoke() {
this.invoke();
return Unit.INSTANCE;
}
public final void invoke() {
((Runnable)this.receiver).run();
}
});
onlyif(true, function);
}
Reduce the generation of temporary objects. inline will only be used to modify higher-order functions.
5. Classes and Objects
1. Constructor
Kotlin classes are public final by default, and init is executed when the constructor is called.
open class MainActivity(var int: Int) : AppCompatActivity(), View.OnClickListener {
init {
println("loge====")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
}
override fun onClick(v: View?) {
}
}
Kotlin constructor, the hidden primary constructor follows the class name, the secondary constructor, indirectly or directly inherits the primary constructor or the constructor of the parent class.
class MyView : View {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
}
2. Access modifiers
private
protected
public
internal: The same module can call each other.
3. Companion objects
kotlin code
class StringUtils {
companion object {
fun ifEmpty(str: String): Boolean {
return "" == str
}
}
}
kotlin code call
fun main() {
StringUtils.ifEmpty("123")
}
java code call
public class A {
public static void main(String[] args) {
StringUtils.Companion.ifEmpty("213123")
}
}
4. Singleton class
class Single private constructor() {
companion object {
fun get(): Single {
return Holder.instance
}
}
private object Holder {
val instance = Single()
}
}
fun main() {
Single.get()
}
5. Dynamic proxy
Dynamic proxy for classes
fun main() {
Zoo(Dog()).back()
}
interface Animal {
fun back()
}
class Dog : Animal {
override fun back() {
println("12312")
}
}
class Zoo(animal: Animal) : Animal by animal
Output 12312
Suppose we are in Zoo
class Zoo(animal: Animal) : Animal by animal{
override fun back() {
println("Zoo")
}
}
Output: Zoo
6. Kotlin-specific classes
data class
public final
getter()/setter()
toString(),hashCode(),equals(),copy()
data class User(var id: Int, var name: String)
enum class
Sealed type
sealed class TestOne {
object A : TestOne()
object B : TestOne()
object C : TestOne()
object D : TestOne()
}
6. Advanced features
1. Deconstruction
operator: marks a function as overloading an operator or implementing a convention
class User(var age: Int, var name: String) {
operator fun component1() = age
operator fun component2() = name
}
fun main() {
val user = User(12, "name")
val (age, name) = user
println(age)
println(name)
}
Commonly used in traversing maps
fun main() {
val map = mapOf<String, String>("key" to "key", "value" to "value")
for ((k, v) in map) {
println("$k, $v")
}
}
2. Loop and set operators
Five commonly used loops
fun main() {
for (i in 1..10) {
println(i)
}
for (i in 1 until 10) {
println(i)
}
for (i in 10 downTo 1) {
println(i)
}
for (i in 1..10 step 2) {
println(i)
}
repeat(10) {
println(it)
}
}
Traverse the collection, deconstruct
fun main() {
val list = arrayListOf<String>("a", "b", "c")
for (str in list) {
println(str)
}
for ((index, str) in list.withIndex()) {
println("$index $str")
}
}
3. Set operators
set operator
fun main() {
val arrayListOf = arrayListOf<Char>('a', 'b', 'c', 'd')
val a = arrayListOf.map {
it - 'a' }.filter {
it > 0 }.find {
it > 1 }
}
4. Scope functions
Five commonly used functions
run{},with(T){},let{},apply{},also{}.
import javax.jws.soap.SOAPBinding.Use
data class User(var name: String)
fun main() {
val user = User("1231")
//let与run都会返回闭包的执行结果,区别在于let有闭包参数,而run没有闭包参数
val let = user.let {
user: User -> "let::${
user.javaClass}" }
println(let)
val run = user.run {
"run::${
this.javaClass}" }
println(run)
//also与apply都不返回闭包的执行结果,区别在于aso有闭包参数,而apply没有闭包参数
user.also {
println("also::${
it.javaClass}") }
user.apply {
println("apply::${
this.javaClass}") }
//takeIf 的闭包返回一个判断结果,为false时,takeIf函数会返回空
// takeUnless 与 takeIf 刚好相反, 闭包的判断结果,为true时函数会返回空
user.takeIf {
it.name.length > 0 }?.also {
println("姓名为${
it.name}") } ?: println("姓名为空")
user.takeUnless {
it.name.length > 0 }?.also {
println("姓名为空") } ?: println("姓名${
user.name}")
//重复执行当前闭包
repeat(5) {
println(user.name)
println(it)
}
//with比较特殊,
//不是以扩展方法的形式存在的,而是一个顶级函数
with(user) {
this.name = "with"
}
}
5. Infix expression, operator overloading
A function is declared as an infix function only when it is used for two objects with similar roles. Recommended examples: and, to, zip. Counterexample: add.
If a method modifies its receiver, do not declare it infixed.
6. DSL
7. Kotlin extension library
1. kotlinx.coroutines
What is coroutine
Improve program running efficiency by improving CPU utilization and reducing thread switching.
Controllable: coroutines can initiate subtasks that can be controlled
Lightweight: coroutines are very small and occupy less resources than threads
Syntactic sugar: Make multitasking or multithread switching no longer use callback syntax
Start coroutine
runBlocking: T, //Used to perform coroutine tasks,
usually only used to start the outermost coroutine
launch: Job, //used to execute coroutine tasks
async/await: Deferred, //used to execute coroutine tasks
package com.chk.myexpandlist
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
fun main() {
runBlocking<Unit> {
val launch = launch {
repeat(1000) {
i ->
println("挂起中 $i。。。")
delay(500L)
}
}
var job2 = async {
delay(500L)
return@async "hello"
}
println(job2.await())
delay(1300L)
println("main:主线程等待中")
launch.cancel()
launch.join()
println("main:即将完成退出")
}
}
There are a total of four parameters in the source code of launch in the three startup methods.
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
)
context: current program context, coroutine and coroutine switching parameters, CoroutineContext scheduler, can be placed in a thread,
start: How to start the coroutine,
block: started coroutine,
demo
private val okHttpClient = OkHttpClient()
private val request = Request.Builder().url("https//baidu.com").get().build()
fun displayDashboard(textView: TextView) = runBlocking {
withContext(Dispatchers.Main) {
textView.text = withContext(Dispatchers.Default) {
okHttpClient.newCall(request).execute().body()?.string()
}
}
}
java code
public class Dashboard {
private final OkHttpClient okHttpClient = new OkHttpClient();
private final Request request = new Request.Builder().url("https://baidu.com").get().build();
private final Handler handler = new Handler(Looper.getMainLooper());
public void display(final TextView textView) {
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String string = response.body().string();
handler.post(() -> textView.setText(string));
}
});
}
}
test
class TestMainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_main)
}
fun test2(view: View) {
val tvContent = findViewById<TextView>(R.id.mContent)
Dashboard().display(tvContent)
displayDashboard(tvContent)
}
fun test1(view: View) {
runBlocking {
withContext(Dispatchers.Main) {
println("在主线程中启动一个协程," + Thread.currentThread().name)
}
}
}
}
xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:text="hello world"
android:id="@+id/mContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btnTestThread"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="test1"
android:text="测试1" />
<Button
android:text="测试2"
android:onClick="test2"
android:id="@+id/btnTestCoroutine"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
object AndroidCommonPool : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
AsyncTask.THREAD_POOL_EXECUTOR.execute(block)
}
}
suspend keyword:
// suspend, the function modified by suspend can only be called by the function modified by suspend
// Because the function (or lambda) modified by suspend will have an extra parameter type called Continuation after being compiled, // The asynchronous call of the coroutine is essentially a callback
suspend fun getHtml(): String {
return withContext(AndroidCommonPool) {
URL("http://baidu.com").readText()
}
}
Call getHtml in java
public class JavaActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LaunchCoroutineKt.getHtml(new Continuation<String>() {
@NonNull
@Override
public CoroutineContext getContext() {
return null;
}
@Override
public void resumeWith(@NonNull Object o) {
}
});
}
}
Special way to start coroutine
buildSequence/yield: Sequence
buildlterator: lterator
produce : Channel
2. kotlinx-io
```kotlin
object AndroidCommonPool : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
AsyncTask.THREAD_POOL_EXECUTOR.execute(block)
}
}
suspend keyword:
// suspend, the function modified by suspend can only be called by the function modified by suspend
// Because the function (or lambda) modified by suspend will have an extra parameter type called Continuation after being compiled, // The asynchronous call of the coroutine is essentially a callback
suspend fun getHtml(): String {
return withContext(AndroidCommonPool) {
URL("http://baidu.com").readText()
}
}
Call getHtml in java
public class JavaActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LaunchCoroutineKt.getHtml(new Continuation<String>() {
@NonNull
@Override
public CoroutineContext getContext() {
return null;
}
@Override
public void resumeWith(@NonNull Object o) {
}
});
}
}
Special way to start coroutine
buildSequence/yield: Sequence
buildlterator: lterator
produce : Channel