Get to know Kotlin thoroughly
origin
In 2019, Google announced at I/O that the Kotlin programming language is now the language of choice for Android application developers. (Kotlin-first)
Kotlin, developed by JetBrains, is 100% Java interoperable and has many features that Java does not support. Two years ago, at I/O 2017, Google announced support for Kotlin in its Android Studio IDE. Kotlin began to co-exist with Java as a first-level language for Android development. Because Java has contributed so much to the evolution of Android, the idea that it is the language of choice for Android is firmly established, and it is improving all the time.
Within a few years, However, Kotlin quickly took over the market due to its advantages over Java, such as greater security and simplicity, said Chet Haase, Google’s chief Evangelist for Android: “Kotlin’s popularity has been growing over the past two years, with over 50% of professional Android developers now using Kotlin to develop their apps.” This can be seen in the data from this year’s Stack Overflow annual Developer Survey, which shows that Kotlin received 72.6% positive feedback among developers’ favorite programming languages.
Why did Google choose Kotlin
The most immediate reason Google chose Kotlin was because of Oracle’s rivalry.
Oracle bought Sun, which developed Java in 1995, for $7.4 billion in 2010 and sued Google for $8.8 billion in less than eight months for copyright infringement on the Java language.
A look back at oracle and Google’s Java copyright tug-of-war:
- In 2010, Oracle sued Google, claiming that Google’s Android operating system made unauthorized use of Java apis
- In 2012, Google successfully convinced the court that API was not protected by copyright, and a district court ruled that API was not protected by law and dismissed the case
- In 2012, Oracle complained about the ruling and appealed to the United States Court of Appeals
- In 2014, three appellate court judges unanimously threw out the district court’s decision in the case and declared API protected by copyright
- In 2014, Google appealed the ruling to the Supreme Court
- In 2015, the Supreme Court rejected Google’s appeal and sent the case back to a district court
- In March 2016, Oracle raised the claim to $9.3 billion
- In April 2016, a settlement meeting between Google’s CEO and Oracle’s failed
- In May 2016, the San Francisco District Court held that Google’s use of Java apis was protected by the “Fair Use” principle
- Oracle appealed to the Court of Appeals for the Federal Circuit in October 2016
- In 2017, the Court of Appeals for the Federal Circuit heard Oracle’s appeal
- In March 2018, the Court of Appeals for the Federal Circuit ruled in Favor of Oracle
- Google then appealed again, but it was dismissed in August 2018
- In January 2019, Google asked the U.S. Supreme Court for a final ruling on its Java API copyright lawsuit with Oracle
Google chose Kotlin simply to get out of patent litigation. Kotlin, of course, does his part. Excellent performance indeed.
A glance at grammar
1.Hello World
@JvmStatic
fun main(args: Array<String>) {
print("Hello World!")}Copy the code
2. The variable
First we define a num variable with var and a STR variable with val.
var num : Int = 1
val str : String = "test"
Copy the code
According to the figure above, two points need to be noted:
2.1. Val is equal to the final
We can see that num defined by var can be reassigned, but STR cannot. Val is actually equivalent to final String in Java, meaning that variables defined by val have the final keyword added by default.
2.2. Can be empty? And empty matching
The second point is that the num variable cannot be assigned to Null if it is defined as Int, and we need to do so if necessary
var num : Int?
Copy the code
2.3. Type inference
val str = ""
Copy the code
Kotlin has type inference and the above statement is equal to Java
final String str = ""
Copy the code
Custom getter&setter
var msg: String
get() = field
set(value) {
field = value
}
Copy the code
3. The function
Definition 3.1.
3.1.1 Ordinary functions
Define a function called test that returns a String? , may return null
fun test(a): String? {
return null
}
Copy the code
Call:
The call is similar to Java, since test returns a nullable string, add? : indicates that the current face is empty.
valresult = test() ? :"x"
Copy the code
Can also be
fun isEmpty(str: String) = str.isEmpty()
Copy the code
IsEmpty returns the value of isEmpty().
3.1.2 Default Parameters
Kotlin supports functions with default parameters, which are default if not passed.
data class EnvConfig(val baseUrl: String, val isDebug: Boolean = false)
// Construct 1 equals EnvConfig("https://xx.com",false)
val env1 = EnvConfig("https://xx.com")
Copy the code
3.1.3 Named Parameters
The Kotlin method call can specify parameter names to avoid confusion. It’s more intuitive.
EnvConfig(
baseUrl = "https://xx2.com",
isDebug = true
)
Copy the code
3.2 Top-level functions and attributes
Kotlin can define globally callable utility functions, which are compiled into static methods of the file to be called.
TopFunc.kt
fun toString(obj:Any) = obj.toString()
Copy the code
Translate to Java classes
public final class TopFuncKt {
@NotNull
public static final String toString(@NotNull Object obj) {
Intrinsics.checkParameterIsNotNull(obj, "obj");
returnobj.toString(); }}Copy the code
Same thing with the top-level property
var count = 0
Copy the code
Java
public static final int getCount(a) {
return count;
}
public static final void setCount(int var0) {
count = var0;
}
Copy the code
3.3 Add methods to someone else’s class: extend functions and attributes
3.3.1 Extend the function
An extension function is very simple; it is a member function of a class.
TopFunc.kt
// Define a member function method in which this refers to the extended object.
// where this is the string
fun String.print(a) = println(this)
/ / use
"string extension".print()
/ / output
string extension
Copy the code
The extension function is also a top-level function, so it is also a static function in Java, called as follows:
TopFuncKt.print("extension in Java");
Copy the code
Extension functions exist only to shorten the syntax. They are not “extensions” in the true sense of the word, nor can they be really extended, so extension functions cannot be overwritten or called as member functions in Java.
3.3.2 Extend attributes
Similar to extension functions, extended properties provide a way to extend a class’s API to access properties using property syntax instead of function syntax. ** Although they are called attributes, they can have no state and no proper place to store them. ** You cannot add additional fields to an existing instance of a Java object.
var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value: Char) {
setCharAt(length - 1, value)
}
Copy the code
** The code above provides a quick way to access the class’s member methods using the extended attribute, but does not add the lastChar attribute to the StringBuilder class. ** However, extended attributes are still useful, such as converting a common Android Float to DP:
val Float.dp
get() = get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics)
Copy the code
3.4 Variable arguments and infix calls
Vararg: variable parameter. Just be careful
fun test(vararg string: String){
test2(*string)// Add * to pass the variable argument
}
fun test2(vararg string: String){}Copy the code
Infix call:
// Allow functions to be called using infix symbols. The function returns a Pair
infix fun Any.with(other:Any) = Pair(this,other)
Copy the code
val c = "3" with 4 // c is pair
val (f, s) = "3" with 4 // Val (f,s) is called a destruct declaration, giving the Pair's first to f and second to s.
println("f:$f,s:$s")
// Output the result
f:3,s:4
Copy the code
4. Program logic control
4.1 If with a return value
Instead of the ternary operator, Kotlin uses an if statement with a return value. The return value is the last line of code in each condition
//Kotlin uses the last line of code in each condition as the return value
fun largeNumber(number1: Int,number2: Int) : Int{
return if(number1 > number2){
number1
}else {
number2
}
}
// Based on the syntax sugar and Kotlin type derivation mechanism we learned above, we could also abbreviate the largeNumber function, which ended up like this
fun largeNumber(number1: Int,number2: Int) = if(number1 > number2) number1 else number 2
Copy the code
4.2 When with a return value
Switch does not work very well in Java:
①Switch statements can only support certain types, such as integer, shorter than integer, string, enumerated type. If we are not using these types, Switch is not available
② All case conditions of Switch statements must have a break at the end
All of these issues have been addressed in Kotlin, and Kotlin has added a number of powerful new features:
The last line of code in the condition is the return value
④ The WHEN conditional statement allows any type of argument to be passed in
⑤ When condition body conditional format: matching value -> {execute logic}
⑥ Like the if statement, the {} in the body of the condition can be omitted if there is only one line of code in the body
//when has parameters
fun getScore(name: String) = when (name) {
"tom" -> 99
"jim" -> 80
"lucy" -> 70
else -> 0
}
// If there are no arguments in Kotin, use the == operator to determine whether strings or objects are equal
fun getScore(name: String) = when {
name == "tom" -> 99
name == "jim" -> 80
name =="lucy" -> 70
else -> 0
}
Copy the code
4.3 Loop Statements
There are two main types of loop statements in Kotlin :while and for-in. While is the same as in Java.
For-in in Kotlin is much more user-friendly than in Java.
for-in:
/ / to use.. Create an ascending range with closed ranges at both ends [0,10]
for (i in 0.10.){
print("$i ")}// Print the result
0 1 2 3 4 5 6 7 8 9 10
Copy the code
for-until:
// use until to create an ascending interval with the left end closed and the right end open [0,10]
for (i in 0 until 10){
print("$i ")}// Print the result
0 1 2 3 4 5 6 7 8 9
Copy the code
for-downTo:
// use downTo to create a descending interval where both ends are closed [10,0]
for (i in 10 downTo 0){
print("$i ")}// Print the result
10 9 8 7 6 5 4 3 2 1 0
Copy the code
Step by step:
// Use downTo to create descending ranges with closed ranges at both ends, skipping three elements at a time
for (i in 10 downTo 0 step 3){
print("$i ")}Copy the code
Iterative list
// Use withIndex to iterate through the list
val list = arrayListOf("10"."11"."100")
for ((index,element) in list.withIndex()){// Destruct the statement
println("$index:$element")}// Print the result
0:10
1:11
2:100
Copy the code
Iterative map
val map = mapOf(1 to "Java".2 to "Kotlin".3 to "Flutter")// infix call
for ((key, value) in map) { / / deconstruction
println("$key:$value")}// Print the result
1:Java
2:Kotlin
3:Flutter
Copy the code
Class 5.
As with Java, classes are defined as follows
class Base {
var num = 1
}
Copy the code
But the meaning is not quite the same.
5.1 the visibility
** In Kotlin, the default class visibility is public and final. Inner classes are static by default, and non-static inner classes are marked with inner.
① Visibility in Kotlin
private
: private, visible inside this classprotected
: Subclass visibleinternal
: Visible in the modulepublic
: Default, public
(2) compare the Java
private
: private, visible inside this classprotected
: Subclass visibledefault
: The default is visible within the packagepublic
: public
Single constructor & private constructor
class Response private constructor(val code: Int.val msg: String){
override fun toString(a): String {
return "code:$code,msg:$msg"}}Copy the code
Multiple constructors
// Non-open cannot be inherited
class Response {
val code: Int
val msg: String
constructor(code: Int) {
this.code = code
msg = ""
}
constructor(code: Int, msg: String) {
this.code = code
this.msg = msg
}
override fun toString(a): String {
return "code:$code,msg:$msg"}}Copy the code
Getters for code and MSG are generated automatically.
In Kotlin, there is also a single inheritance and multiple implementations. When common, the inheritance class is written to the first place, followed by a comma to keep up with the interface interface.
public class String : Comparable<String>, CharSequence{. }Copy the code
Sealed class (private construct, default open)
sealed class Expr {
class Num(val value: Int) : Expr()
class Sum(val left: Expr, val right: Expr) : Expr()
}
Copy the code
5.2 the Object keyword
(1) the singleton class
object SingleTon {
@JvmStatic
fun isEmpty(str: String) = str.isEmpty()
}
Copy the code
Decompile to Java
public final class SingleTon {
public static final SingleTon INSTANCE;
@JvmStatic
public static final boolean isEmpty(@NotNull String str){
Intrinsics.checkParameterIsNotNull(str, "str");
CharSequence var1 = (CharSequence)str;
boolean var2 = false;
return var1.length() == 0;
}
private SingleTon(a) {}static {
SingleTon var0 = newSingleTon(); INSTANCE = var0; }}Copy the code
As you can see from the surface, Object modifies a single class and is represented as a singleton implemented by a static code block.
For @jvmstatic methods, Kotlin adds the static keyword to the method, and the static keyword does not exist in Kotlin.
(2) the anonymous class
test(object : Listener {
})
interface Listener{}Copy the code
There is no new keyword in Kotlin, so anonymous classes are implemented with Object.
③ Associated objects
Kotlin does not have static, so how to implement the definition and use of static variables, constants, etc.
The answer is the companion
class UserManager {
companion object{
val USER_TYPE = 0x01}}Copy the code
The companion Object above generates an inner class Companion and adds a static getter that returns USER_TYPE, as follows
public final class UserManager {
private static final int USER_TYPE = 1;
public static final UserManager.Companion Companion = new UserManager.Companion((DefaultConstructorMarker)null);
public static final class Companion {
public final int getUSER_TYPE(a) {
returnUserManager.USER_TYPE; }... }}Copy the code
PS: the const keyword
The const keyword can be used only in static classes, only with val, that is, const val, and only with primitive types. Meaning is a compile-time constant, replaced with the value of that constant where used. As follows:
object SingleTon {
const val str = "const"
}
fun test(a): String? {
return SingleTon.str
}
Copy the code
Test decompiles Java as follows:
public final String test(a) {
return "const";
}
Copy the code
You can see that Kotlin inline the const constant.
5.3 class delegate
equals
In Java, you can use == to compare primitive data types, which compare values, and reference types, which compare references. In Kotlin == is equal to calling Equals in Java. If you want to compare references, use ===.
By the keyword
Decorator pattern code is usually more template, and kotlin uses the by keyword to delegate classes. Such as:
class MyList<T> : List<T> by ArrayList() {
// ArrayList implements all the interfaces of List by default
}
Copy the code
Convert to Java:
public final class MyList implements List.KMappedMarker {
// $FF: synthetic field
private final ArrayList $$delegate_0 = newArrayList(); .public Object get(int index) {
return this.$$delegate_0.get(index); }... }Copy the code
Of course, we can also rewrite our own logic.
By can also be used to implement lazy loading:
private val arr by lazy { MyList<String>() }
Copy the code
It is implemented as double-check lazy loading, as follows:
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
// final field is required to enable safe publication of constructed instance
private vallock = lock ? :this
override val value: T
get() {
val _v1 = _value
if(_v1 ! == UNINITIALIZED_VALUE) {@Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
if(_v2 ! == UNINITIALIZED_VALUE) {@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
valtypedValue = initializer!! () _value = typedValue initializer =null
typedValue
}
}
}
override fun isInitialized(a): Boolean= _value ! == UNINITIALIZED_VALUEoverride fun toString(a): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
private fun writeReplace(a): Any = InitializedLazyImpl(value)
}
Copy the code
6.lambda
6.1 grammar
Lambda expressions are essentially small pieces of code that can be passed to other functions.
Simple lambda in Kotlin:
button.setOnclickListener{
}
Copy the code
Lambda in Kotlin is always surrounded by curly braces {}. Lambda expressions can be stored in variables:
val sum = { x:Int,y:Int -> x + y }
println(sum(1.2))
3
Copy the code
In Kotlin, lambda as the last argument can be placed after () as follows: 1; If lambda is the only argument, you can omit (), as follows 2.
list.maxBy({it.length})
list.maxBy(){it.length}/ / 1
list.maxBy{it.length}/ / 2
Copy the code
6.2 Set function API
For example, list filtering:
val list = arrayListOf(1.2.3.4.5.6.7.8.9.10)
list.filter { it % 2= =0 }
.forEach { print(it) }
// Print the result
246810
Copy the code
Similarly:
All: Returns true for all matches
Any: Returns true if a match exists
Count: Returns the number of matches
FirstOrNull: The first match, no NULL is returned
First: The first match, no exception thrown
Find: Finds the first match equal to firstOrNull
There is a map, flatmap, groupby…
It basically covers common RxJava operators.
apply,let,also…
Apply changes this to refer to the caller. Facilitates various operations and returns the caller
val str = "123456"
val rec = str.apply {
println("lastChar:${get(lastIndex)}")
}
println("rec:$rec")
// Print the result
lastChar:6
rec:123456
Copy the code
With changes this to the argument, returning the result of the last line of the lambda
Let creates a local variable that returns the last line of results.
val str :String ? = "123456"
valres = str? .let { println("it:$it")
"return"
}
println("res:$res")
// Print the result
it:123456
res:return
Copy the code
Also: Creates it and returns the caller
Run: Changes this to point to the caller and returns the last line
Similar syntactic sugar takeIf, repeat, etc., are defined in standard.kt.
Second, in-depth understanding
1. Kotlin type system
1.1 can be empty
Kotlin raises ERROR: Type Mismatch at run time when a null is received for a Type definition that has not been added nullable.
When a class is specified to be nullable, can a security call be used? Can you follow the Elvis operator? :. Sign in front? . Executed when the caller is null.
val str :String ? = "123456"str? .get(str.lastIndex) ? : toast("str is null") //toast is executed when STR is null
Copy the code
Safe conversion :as?
Is: checks the type and can be automatically transformed
val obj: Any = "str"
if (obj is String) {
println("obj:$obj")//obg automatically converts to string
}
// Print the result
obj:str
Copy the code
As: Type conversion, as? Safe type conversion
val obj: Any = "str"
(obj as? String)? .print()If obj is String, print is a locally defined extension function
// Print the result
str
Copy the code
!!!!! Not empty assertion
Have Kotlin throw an exception when the object is empty.
val obj: Any? = "str"obj!! .toString() obj.hashCode()// No need to add more!! , the Kotlin compiler automatically checks
Copy the code
2. Operator overload
data class Point(val x: Int.val y: Int) {
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
val p1 = Point(1.2)
val p2 = Point(5.6)
val p = p1 + p2
println("p:$p")// Automatically call toString
// Print the result
p:Point(x=6, y=8)
Copy the code
Operators available for overloading:
expression | The function name |
---|---|
a * b | times |
a / b | div |
a % b | mod |
a + b | plus |
a – b | minus |
PS: Bit operations also have their own notation
The operator | operation |
---|---|
shl | Shift the sign to the left |
shr | Move to the right with the sign |
ushr | Unsigned shift to the right |
and | Bitwise and |
or | Bitwise or |
xor | The bitwise exclusive or |
inv | According to the not |
3. Lambda as a parameter
No other parameters:
fun exec(func:()->Unit){
func.invoke()
}
exec {
println("hello world")}Copy the code
With other parameters:
fun exec(msg: String,func:(msg:String) - >Unit){
func.invoke(msg)
}
exec("hello world") {msg->
println(msg)
}
Copy the code
Lambda above is taken as a parameter that cannot be empty. If it is empty, it needs to be defined as ()? The parcel. As follows:
fun exec(msg: String,func:((msg:String) - >Unit)?{ func? .invoke(msg) }Copy the code
Lambda is good to pass as an argument, but its implementation still passes objects (anonymous classes). An object is created on every call. How to avoid this overhead and improve performance?
The answer is an inline function.
4. Series of inline functions
inline
When a function is declared inline, its function body is inline — in other words, the function is replaced directly where the function was called, rather than being called normally.
inline fun inlineTest(inlineFunc:()->Unit){
println("before invoke")
inlineFunc()
println("after invoke")
}
inlineTest {
println("hello world")}// Print the result
before invoke
hello world
after invoke
Copy the code
to Java:
String var2 = "before invoke";
System.out.println(var2);
String var5 = "hello world";
System.out.println(var5);
var2 = "after invoke";
System.out.println(var2);
Copy the code
Limitations on inline functions
When we add a return to the inline lambda, the code doesn’t work correctly; it returns halfway through.
inlineTest {
println("hello world")
return
}
println("code works as well")
// Print the result
before invoke
hello world
Copy the code
In this case, we need to add the scope for return
inlineTest {
println("hello world")
return@inlineTest
}
println("code works as well")
// Print the result
before invoke
hello world
after invoke
code works as well
Copy the code
You can also use other methods, such as noinline
noinline
Noinline modifies lambda arguments in an inline method. Noinline is used in cases where we do not want the inline feature to apply to some lambda arguments of an inline method.
// Kotlin
fun main(args: Array<String>) {
val methodName = "main"
multiplyByTwo(5) {
result: Int -> println("call method $methodName, Result is: $result")}}inline fun multiplyByTwo(
num: Int.noinline lambda: (result: Int) - >Unit): Int {
val result = num * 2
lambda.invoke(result)
return result
}
Copy the code
The result of the decompilation is:
public final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
final String methodName = "main";
byte num$iv = 5;
Function1 lambda$iv = (Function1)(new Function1() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke(Object var1) {
this.invoke(((Number)var1).intValue());
return Unit.INSTANCE;
}
public final void invoke(int result) {
String var2 = "call method " + methodName + ", Result is: " + result;
boolean var3 = false; System.out.println(var2); }});int $i$f$multiplyByTwo = false;
int result$iv = num$iv * 2;
lambda$iv.invoke(result$iv);
}
public final int multiplyByTwo(int num, @NotNull Function1 lambda) {
int $i$f$multiplyByTwo = 0;
Intrinsics.checkParameterIsNotNull(lambda, "lambda");
int result = num * 2;
lambda.invoke(result);
return result;
}
Copy the code
As you can see, because the lambda is decorated with noinline, the compiler processes the lambda as an anonymous inner class, generating a Function1 object.
crossinline
Is inline and noinline enough for us developers? Is that enough? Obviously not. Consider a case where we want the lambda to be inline as well, but we don’t want the lambda to affect the caller’s control flow. This can be due to conscious active control, but most of the time it is due to developer carelessness. We know that the Java language is a compiled language, so it would be safe to indicate or even report errors during compilation where inline lambda affects the caller’s control flow.
Crossinline was created to deal with this situation. Crossinline retains the inline feature, but an error is reported if a return is attempted within the incoming lambda. Return can only return the current lambda.
// Kotlin
fun main(args: Array<String>) {
val methodName = "main"
multiplyByTwo(5) {
result: Int -> println("call method $methodName, Result is: $result")
return@multiplyByTwo}}Copy the code
As shown in the code, you must return@multiplyByTwo instead of writing return directly.
5. The generic
5.1 Basic usage of generics
1) First, let’s explain what generics are. Generics are parameterized types that allow us to program without specifying specific types. When we define a class, method, or interface, and we add a type parameter to it, we add a generic type to the class, method, or interface
// define a generic class. Use
after the class name to define a generic class
class MyClass<T>{
fun method(params: T){}}// Generic calls
val myClass = MyClass<Int>()
myClass.method(12)
// define a generic method by adding
to the name of the method
class MyClass{
fun <T> method(params: T){}}// Generic calls
val myClass = MyClass()
myClass.method<Int> (12)
// According to the Kotlin type inference mechanism, we can omit generics
myClass.method(12)
// define a generic interface by adding
to the name of the interface
interface MyInterface<T>{
fun interfaceMethod(params: T)
}
Copy the code
type
class MyClass{
// We specify that the upper bound of the generic type is Number, so we can only pass in numeric parameters
fun <T : Number> method(params: T){}}Copy the code
Generics firm
Note that generics on the JVM are generally implemented by generic erasure, so the same is true for generics on Kotlin. But Kotlin can reified a generic with an inline function
inline fun <reified T>可迭代< * >.filterIsInstance(a): List<T> { //reified is no longer erased
val des = mutableListOf<T>()
forEach {
if (it is T) des.add(it)
}
return des
}
fun main(args: Array<String>) {
val items = arrayListOf("one".2."three")
println(items.filterIsInstance<String>())
}
// Print the result
[one, three]
Copy the code
Why can Kotlin avoid generics? Because the Kotlin compiler inserts the bytecode that implements the inline function into each call, it takes the actual type as an argument.
5.2 Covariant and contravariant
In Java, storing a reference to a Source instance in a variable of type Source is extremely safe — there are no consumer-methods to call. But Java doesn’t know this and still forbids it:
void demo(Source<String> strs) {
Source<Object> objects = strs; / /!!!!!! Not allowed in Java
/ /...
}
Copy the code
To fix this, we must declare the object of type Source<? Extends Object>, which makes no sense because we can call all the same methods on this Object as before, so the more complex types don’t add value. But the compiler doesn’t know that.
So in Kotlin, there’s a way to explain this to the compiler. This is called declarative typing: we can annotate the Source parameter type T to ensure that it is returned only from the Source member (read only, equivalent to Java? Extends T). To do this, Kotlin provides the out modifier.
//kotlin
interface Source<out T> {
fun nextT(a): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // This is ok because T is an out- argument
/ /...
}
Copy the code
In simple terms: consumers use in, producers use out
As a general rule, when the type parameter T of a class C is declared out, it can only appear in the output of C members, but the payoff is that C is safe as a superclass of C. In addition to out, Kotlin added a variant note: in. It inverts a type parameter: it can only be written to, but not read. Super T). A good example of an inverter type is Comparable:
interface Comparable<in T> {
operator fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
x.compareTo(1.0) // 1.0 has the type Double, which is a subtype of Number
// Therefore, we can assign x to variables of type Comparable
val y: Comparable<Double> = x / / OK!
}
Copy the code
6.DSL
6.1 the DSL is introduced
The full name of DSL is Domain specific language, such as HTML and XML
The characteristics of
- Solve domain-specific problems
- It and system programming language go is two extremes, system programming language is to solve all the problems, for example, Java language hopes to do Android development, but also hope to do background development, it has the characteristics of horizontal expansion. DSLS have the ability to solve domain-specific problems in vertical depth.
In general, the core idea of DSLS is to solve domain-specific problems by focusing on specialization rather than completeness.
6.2 Kotin DSL
Gradle is an open source build automation tool. Gradle is a Groovy or Kotin-based DSL. Our Android application is built using Gradle, so we can use Kotlin to write scripts and plug-ins. Besides, AndroidStudio is very friendly to Kotlin, and it is very cool to write.
Such as:
Example 1- Requesting a callback:
open class RequestCallback<T> : Callback<T> {
private val builder: Builder<T>
...
private fun onSuccess(data: T){ builder.onSuccess? .invoke(data)}private fun onError(code: Int, msg: String?).{ builder.onError? .invoke(code, msg) ? : toast(msg) }private fun onFinished(a){ decrement() builder.onFinished? .invoke() }class Builder<T> {
internal var onSuccess: ((data: T) -> Unit)? = null
internal var onError: ((code: Int, msg: String?) -> Unit)? = null
internal var onFinished: (() -> Unit)? = null
fun onSuccess(func: ((data: T) - >Unit)? {
onSuccess = func
}
fun onError(func: ((code: Int.msg: String?). ->Unit)? {
onError = func
}
fun onFinished(func: (() -> Unit)? {
onFinished = func
}
}
}
fun <T> getCallback(refresh:Boolean = false,dsl: RequestCallback.Builder<T>. () - >Unit): RequestCallback<T> {
...
return RequestCallback(b)
}
/ / useapi.fetchQrCode(orderNo, faceSuccess).enqueue(getCallback { onSuccess { ... } onError { _, msg -> ... }})Copy the code
Example 2- Extend system Animator listener:
class AnimationCallbackBuilder {
internal var onRepeat: ((animation: Animator?) -> Unit)? = null
internal var onCancel: ((animation: Animator?) -> Unit)? = null
internal var onStart: ((animation: Animator?) -> Unit)? = null
internal var onEnd: ((animation: Animator?) -> Unit)? = null
fun onRepeat(function: ((animation: Animator?). ->Unit)? {
this.onRepeat = function
}
fun onCancel(function: ((animation: Animator?). ->Unit)? {
this.onCancel = function
}
fun onStart(function: ((animation: Animator?). ->Unit)? {
this.onStart = function
}
fun onEnd(function: ((animation: Animator?). ->Unit)? {
this.onEnd = function
}
}
fun AnimatorSet.registerCallback(listener: AnimationCallbackBuilder. () - >Unit) {
val mListener = AnimationCallbackBuilder().also(listener)
addListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?).{ mListener.onRepeat? .invoke(animation) }override fun onAnimationEnd(animation: Animator?).{ mListener.onEnd? .invoke(animation) }override fun onAnimationCancel(animation: Animator?).{ mListener.onCancel? .invoke(animation) }override fun onAnimationStart(animation: Animator?).{ mListener.onStart? .invoke(animation) } }) }/ / use
set.registerCallback {
onEnd {
}
}
Copy the code
In Kotlin, specifying that a method returns a nullable type requires a? It’s tricky to tell if it’s empty. Such as:
// You can also define your own apply
inline fun <T> T.exist(block: T. () - >Unit) = block()
inline fun curActivity(block: Activity. () - >Unit)= ActivityCollector.currentActivity()? .let(block)/ / useitem? .exist{this.toString()// This changes the direction of this, and this is not null
}
curActivity{
startActivity(intent)/ / same as above, this is ActivityCollector currentActivity () to obtain the activity is not null,
// If ** is empty, do not execute **
}
Copy the code
other
Interface with default implementation, such as screenshot permission configuration:
interface ICaptureView {
fun canCapture(a) = false
}
Copy the code
Classes that can’t be screenshots only need to implement interfaces that can be overwritten.
Ktorm
What is Ktorm?
Ktorm is an efficient and concise lightweight Kotlin ORM framework written directly based on pure JDBC. It provides strongly typed and flexible SQL DSLS and convenient sequence apis to reduce the repetitive effort of operating databases. Of course, all SQL is automatically generated. Ktorm is open source based on Apache 2.0 protocol, source code hosted on GitHub, if it is helpful to you, please leave your star:kotlin-orm/ktorm
features
- No configuration files, no XML, no annotations, or even any third party dependencies, lightweight, simple and easy to use
- Strongly typed SQL DSLS expose low-level bugs at compile time
- Flexible queries with arbitrary precision control over the SQL generated
- Entity sequence API, used
filter
,map
,sortedBy
Is as convenient as using native collections in Kotlin- Easy to expand design, you can write extensions flexibly, support more operators, data types, SQL functions, database dialects, etc
conclusion
Kotlin has several advantages over Java:
①Kotlin’s syntax is more concise, and the amount of code developed using Kotlin can be reduced by 50% or more than Java development
(2) Kotlin’s syntax is more advanced. Compared to Java’s old syntax, Kotlin has added many of the syntax features of modern high-level languages (syntax sugar), greatly improving our development efficiency
Kotlin is 100% compatible with Java. Kotlin can call code written in Java directly and seamlessly use Java third-party open source libraries, which allows Kotlin to add many new features while inheriting all the wealth of Java
.
There are some disadvantages as well:
①Kotlin generates more classes than Java after compilation, which may affect the size of the installation package.
(2) Although Kotlin is fully compatible with Java, some functions are not very convenient and beautiful to use in Java, so if the library author has strict requirements for code (beautiful), it is best to wrap a Java call layer in the outer layer.
HttpManager.INSTANCE.create(MineApi.class).getCashRecordList(map).enqueue(new RequestCallback<>(baseResponseBuilder -> {/ / DSL becomes as follows, then a lambda Function, once Function1, Function2 (digits) according to the parameters such as
baseResponseBuilder.onSuccess(new Function1<BaseResponse<ArrayList<CashRecordListBean>>, Unit>({
@Override
public Unit invoke(BaseResponse<ArrayList<CashRecordListBean>> arrayListBaseResponse). returnnull; }});return null;
}));
Copy the code
③Kotlin’s KAPT needs to generate stub(Java code) that APT can parse, and then use APT to generate code, which affects the performance of KAPT and slows down the overall compilation speed of Kotlin project.
Kotlin also released an alpha version of the native Processing KSP, which claims to bring twice the speed [Announcing Kotlin Symbol Processing (KSP) alpha]
KSP offers a powerful and yet simple API for parsing Kotlin code directly, Dramatically reducing the build speed tax by KAPT’s stub generation. Indeed, initial benchmarks with the Room library show that KSP is approximately 2x faster than KAPT.
Overall, Kotlin brings more advantages than disadvantages, a concise and easy-to-use syntax, and much less code. A practical type system that avoids most null pointer exceptions. It’s also fully compatible with Java, and with Google announcing Kotlin First, Android development will migrate to Kotlin sooner or later. Plus, with the release of KSP, Kotlin’s slow compilation issues will be resolved, and the ecosystem will get better and better.
The resources
Kotlin Combat [Russia]Dmitry Jemerov, Svetlana Isakova
Kotlin inline, noinline and crossinline_
“Kotlin” series: 1. Introduction to Kotlin
Learn by comparing the Kotlin keywords out and in with generics in Java
[Announcing Kotlin Symbol Processing (KSP) Alpha]
OneMoreThing
Kotlin released the Coroutines library, which claims to be thread-based Coroutines. Next issue preview:
Has Kotlin really implemented a JVM-based coroutine?
What does suspend in Kotlin coroutine mean?
How to optimize multiple Callback nesting?
How does Retrofit implement compatible coroutines internally?
How to use coroutines to complete page lifecycle management (requests, background tasks)?