Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use Kotlin to improve productivity

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly introduces how to use Kotlin to improve productivity, the article is very detailed, has a certain reference value, interested friends must read it!

Recommend a Kotlin practice project debug_view_kotlin, Android floating layer debugging console implemented by kotlin, which can display memory, FPS and text log in real time.

Tip1- 's more concise string

For more information, please see case code KotlinTip1

The string in Kotlin is similar to that in basic Java, except that three quotation marks are added to facilitate the writing of long characters. In Java, these all need to be escaped, so let's take a look at the example in java.

Public void testString1 () {String str1 = "abc"; String str2 = "line1\ n" + "line2\ n" + "line3"; String js = "function myFunction ()\ n" + "{\ n" + "document.getElementById (\" demo\ ") [xss_clean] =\" My First JavaScript Function\ " \ n "+"} "; System.out.println (str1); System.out.println (str2); System.out.println (js);}

In addition to the string with a single double quotation mark, kotlin also strengthens the string by introducing three quotation marks, which can contain special characters such as line breaks, backslashes, and so on:

/ * * string Enhancement by kotlin The special characters * * / fun testString () {val str1 = "abc" val str2 = "line1\ nline2 line3" val js = "function myFunction () {document.getElementById (" demo ") [xss_clean] =" My First JavaScript Function "can be included in the three quotation marks". .trimIndent () println (str1) println (str2) println (js)}

At the same time, string template is introduced into Kotlin to facilitate the concatenation of strings, and variables and expressions can be concatenated with $symbols.

/ * * kotlin string template, you can concatenate variables and expressions * * / fun testString2 () {val strings = arrayListOf ("abc", "efd", "gfg") println ("First content is $strings") println ("First content is ${strings [0]}") println ("First content is ${if (strings.size > 0) strings [0] else" null "}")} with the $symbol.

It is worth noting that in Kotlin, the dollar sign $is a special character that cannot be displayed directly in a string and must be escaped. Method 1 is a backslash and method 2 is ${'$'}.

In / * * Kotlin, the dollar sign $is a special character that cannot be displayed directly in a string and must be escaped. Method 1 is a backslash, and method 2 is ${'$'} * * / fun testString3 () {println ("First content is\ $strings") println ("First content is ${'$'} strings")}

Most control structures in Tip2- Kotlin are expressions

First, you need to figure out a conceptual statement and expression, and then introduce the advantages of control structure expressions: simplicity

What are statements and expressions?

An expression has a value and can be used as part of another expression

Statement always surrounds the top-level element in its code block and does not have its own value

The difference between Kotlin and Java

In Java, all control structures are statements, that is, control structures have no value

In Kotlin, except for loops (for, do, and do/while), most control structures are expressions (if/when, etc.)

For more information, please see case code tip2

Example1:if statement

In java, if is a statement, there is no value, the return that must be displayed

If statement in / * * java * * / public int max (int a, int b) {if (a > b) {return a;} else {return b;}}

In kotlin, if is an expression, not a statement, because an expression has a value and can be used as a value return

In / * * kotlin, if is an expression, not a statement, similar to the Miki operator a > b in java. A: B * * / fun max (a: Int, b: Int): Int {return if (a > b) an else b}

The branch * line statement in if above is the value of the branch and will be used as the return value of the function. This is actually similar to the ternary operator in java.

/ * java ternary operator * * / public int max2 (int a, int b) {return a > b? A: B;}

The above is the ternary operator in java. In kotlin, if is an expression with value, which can be replaced completely, so there is no ternary operator in kotlin, so replace it with if. The above max function can also be simplified to the following form

/ * simplified version of kotlin * * / fun max2 (a: Int, b: Int) = if (a > b) an else b

Example2:when statement

When in Kotlin is so powerful that it can completely replace switch and if/else in Java. At the same time, when is also an expression. The * * line of each branch of when is the value of the current branch. First, take a look at switch in java.

/ * * switch * * / public String getPoint (char grade) {switch (grade) {case'Aids: return "GOOD" in java; case'Bones: case'Cards: return "OK"; case'Downs: return "BAD" Default: return "UN_KNOW";}}

There are too many restrictions on switch in java. Let's see how Kotlin simplifies it.

In / * * kotlin, when is an expression that can replace switch in Java The * line of each branch of when is the value of the current branch * * / fun getPoint (grade: Char) = when (grade) {'A'- > "GOOD",'C'- > {println ("test when") "OK"}'D'-> "BAD" else-> "UN_KNOW"}

Similarly, when statements can also replace if/else if in java, where expressions have values and are more concise

/ * if else * * / public String getPoint2 (Integer point) {if (point > 100) {return "GOOD";} else if (point > 60) {return "OK";} else if (point.hashCode () = = 0x100) {/ /. Return "STH";} else {a return "UN_KNOW";}}

And look at the version of kotlin, which uses when with no parameters, which requires only six lines of code.

In / * * kotlin, when is an expression that can replace the value of the current branch * * / fun getPoint2 (grade: Int) = when {grade > 90-> "GOOD" grade > 60-> "OK" grade.hashCode () = = 0x100-> "STH" else-> "UN_KNOW"}

Functions better called by Tip3-: display parameter names / default parameter values

The function of Kotlin is easier to call, mainly in two aspects: 1, the display of the marked parameter name can be convenient for code to read; 2, the function can have default parameter values, which can greatly reduce the function overload in Java. For example, now you need to implement a tool function to print the contents of the list: see case code KotlinTip3 for details

/ * * print list content * * / fun joinToString (collection: Collection, 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 ()} / * * Test * / fun printList () {val list = listOf (2,4,0) / * No parameter name * / println (joinToString (list, "-", "[") ("]")) / * the displayed parameter name * / println (joinToString (list, separator = "-", prefix = "[", postfix = "]"))}

As the above code shows, the function joinToString needs to pass in four parameters to print the contents of the list: list, delimiter, prefix, and suffix. Because of the many parameters, it is not very intuitive to know what each parameter is for when using the function later. At this time, the parameter name can be displayed to increase the readability of the code. At the same time, when defining a function, you can also give the function default parameters, as follows:

/ * * print the contents of the list with default parameters Function overloading of java can be avoided * * / fun joinToString2 (collection: Collection, 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 ()} / * * Test * / fun printList3 () {val list = listOf (2,4,0) println (joinToString2 (list, "-)) println (joinToString2 (list,", "["))

After you have the default parameter, when using the function, if you do not pass in this parameter, the default value will be used by default, which can avoid a large number of function overloads in Java.

Tip4- extension functions and properties

Extension functions and properties is a very convenient and practical feature of Kotlin, it allows us to expand third-party libraries at will, if you think the SDK api given by others is not easy to use, or can not meet your needs, you can use the extension function to completely customize. For example, in the String class, we want to get a character. There is no such direct function in String. You can use it. Declare such an extension function later: see the case code KotlinTip4 for details

/ * * extension function * * / fun String.lastChar (): Char = this.get (this.length-1) / * * Test * * / fun testFunExtension () {val str = "test extension fun"; println (str.lastChar ())}

After defining the lastChar () function in this way, you can call the function directly with the String class after you just need import to come in, which is no different from calling its own method. This avoids repetitive code and some static utility classes, and the code is more concise. For example, we can modify the function in tip3 above to print the contents of the list:

/ * * transform the list print content function in Tip3 with extension function * * / fun Collection.joinToString3 (separator: String = ",", prefix: String = "", postfix: String = ""): String {val result = StringBuilder (prefix) for ((index) Element) in withIndex () {if (index > 0) result.append (separator) result.append (element)} result.append (postfix) return result.toString ()} fun printList4 () {val list = listOf (2,4,0) println (list.joinToString3 ("/"))}

In addition to extension functions, you can also extend properties. For example, I want to implement String and StringBuilder to obtain * characters directly through attributes:

/ * * extended attribute lastChar gets * * one character * * / val String.lastChar: Char get () = get (length-1) / * * extended attribute lastChar gets * one character * * / var StringBuilder.lastChar () of StringBuilder: Char get () = get (length-1) set (value: Char) {setCharAt (length-1) Value)} / * * Test * / fun testExtension () {val s = "abc" println (s.lastChar) val sb = StringBuilder ("abc") println (sb.lastChar)}

Once the extended properties are defined, it is as convenient as using your own properties as soon as you finish import.

Why can Why?Kotlin implement features such as extension functions and properties?

To understand some syntax in Kotlin, as long as you realize that the Kotlin language * needs to be compiled into class bytecode, and Java is also compiled for class execution, that is, it can be roughly understood that Kotlin needs to be transformed into the same grammatical structure as Java. Kotlin is just a powerful syntax sugar, and the function Kotlin that Java does not have cannot be out of bounds.

So how is the extension function of Kotlin implemented? Introduce a * * method to understand the syntax of Kotlin: convert Kotlin code into Java language to understand, the steps are as follows:

1. Select Tools-- > Kotlin-- > Show Kotlin Bytecode in Android Studio to convert Kotlin to class bytecode.

Class code is not very friendly to read. Click on the Decompile in the upper left corner and it will be converted into Java.

2. Introduce another tip. If you are not familiar with Kotlin syntax, you can first write the code with Java, and then use the AndroidStudio tool to convert the Java code into Kotlin code. The steps are as follows:

Select the Java code to be converted in Android Studio-- > Select Code-> Convert Java File to Kotlin File

Let's take a look at the code after converting the above extension function to Java

/ * * the extension function is converted to a static function, and the * arguments of this static function are the instance object of this class * * / public static final char lastChar (@ NotNull String $receiver) {Intrinsics.checkParameterIsNotNull ($receiver, "$receiver"); return $receiver.charAt ($receiver.length ()-1) The extended property obtained by} / * * is converted into a static get function, and the * arguments to this static function are instance objects of this class * * / public static final char getLastChar (@ NotNull StringBuilder $receiver) {Intrinsics.checkParameterIsNotNull ($receiver, "$receiver"); return $receiver.charAt ($receiver.length ()-1) The extended property set by} / * * is converted into a static set function, and the * parameters of this static function are instance objects of this class * * / public static final void setLastChar (@ NotNull StringBuilder $receiver, char value) {Intrinsics.checkParameterIsNotNull ($receiver, "$receiver"); $receiver.setCharAt ($receiver.length ()-1, value);}

You can see from the above code that when an extension function is converted to Java, it is actually a static function, and the * arguments of this static function are the instance objects of the class. After passing the instances of the class to the function, the public methods of the class can be accessed inside the function. Similarly for extended attributes, the acquired extended attributes are converted into a static get function. At the same time, the * parameters of this static function are the instance object of the class, and the set extended properties are converted into a static set function. At the same time, the * parameters of this static function are the instance objects of the class. Public methods and properties can be accessed within the function.

From the source code converted above, you can actually see the applicability and defects of extension functions and extension attributes. There are two points:

Only public methods and properties of the class can be accessed within extension functions and properties, but private and protected cannot be accessed.

An extension function cannot be override because it is a static function in Java

Here are a few more examples of extension functions to let you feel the convenience of extension functions:

/ * * show toast in activity * * / fun Activity.toast (msg: String) {Toast.makeText (this, msg, Toast.LENGTH_SHORT). Show ()} val Context.inputMethodManager: InputMethodManager? Get () = getSystemService (Context.INPUT_METHOD_SERVICE) as InputMethodManager / * * hide soft input * * / fun Context.hideSoftInput (view: View) {inputMethodManager?.hideSoftInputFromWindow (view.windowToken) 0)} / * screen width in pixels * / val Context.screenWidth get () = resources.displayMetrics.widthPixels / * screen height in pixels * / val Context.screenHeight get () = resources.displayMetrics.heightPixels / * returns dip (dp) dimension value in pixels * @ param value dp * / fun Context.dip2px (value: Int): Int = (value * resources.displayMetrics.densi

Tip5- lazy initialization of by lazy and delayed initialization of lateinit

Lazy initialization by lazy

Lazy initialization refers to postponing the initialization of a variable and de-instantiating the variable when it is used, which will be more efficient. Because we often encounter situations where a variable does not need to be initialized until it is used, or simply its initialization depends on some context that is not immediately available. For more information, please see case code KotlinTip5

/ * * lazy initialization api instance * * / val purchasingApi: PurchasingApi by lazy {val retrofit: Retrofit = Retrofit.Builder () .baseUrl (API_URL) .addConverterFactory (MoshiConverterFactory.create ()) .build () retrofit.create (PurchasingApi::class.java)}

Like the above code, the api instance generated by retrofit will not be instantiated until it is used. It is important to note that by lazy can only modify val-invariant objects, not var mutable objects.

Class User (var name: String, var age: Int) / * * initialize by lazy * * / val user1: User by lazy {User ("jack", 15)}

Delayed initialization of lateinit

In addition, for var variables, if the type is non-empty, it must be initialized, otherwise the compilation will not pass, so you need to use lateinit to delay initialization and then instantiate it when you use it.

/ * * delayed initialization of lateinit * * / lateinit var user2: User fun testLateInit () {user2 = User ("Lily", 14)}

The difference between by lazy and lateinit

By lazy modifies the variables of val

Lateinit modifies the variable of var, and the variable is of a non-empty type

Tip6- no longer has to write findViewById by hand.

In Android's View, there is a lot of code that declares a View and then instantiates the assignment from the xml to the corresponding View through the findViewById. In kotlin can be completely liberated, the use of kotlin-android-extensions plug-ins, no longer have to handwrite findViewById. The steps are as follows: see the case code KotlinTip6 for details.

Step 1, apply plugin: 'kotlin-android-extensions' in the gradle of the project

Step 2, write the layout xml file according to the original habit

Step 3, the layout corresponding to the import can be used in the java code. The View does not need to declare in advance, and the plug-in will automatically generate the corresponding View members according to the id of the layout (in fact, no attributes are generated, see below for the principle)

Import com.sw.kotlin.tips.R / * * Import plug-in generated View * * / import kotlinx.android.synthetic.main.activity_tip6.* class KotlinTip6: Activity () {/ * * automatically generates the corresponding view * * / override fun onCreate (savedInstanceState: Bundle?) {super.onCreate (savedInstanceState) setContentView (R.layout.activity_tip6) tip6Tv based on the id of layout .text = "Auto find view for TextView" tip6Img.setImageBitmap (null) tip6Btn.setOnClickListener {test ()} private fun test () {tip6Tv.text = "update"}}

Like the above code, the three View in Activity are automatically generated, no longer need to declare, then findViewById, and then transform the assignment, whether it reduces a lot of unnecessary code and makes the code very clean.

Why? What is the principle? What did the plug-in do for us?

To understand the principle or convert the above code into java language, refer to the method provided by tips4 to convert it into the following java code:

Public final class KotlinTip6 extends Activity {private HashMap _ $_ findViewCache; protected void onCreate (@ Nullable Bundle savedInstanceState) {super.onCreate (savedInstanceState); this.setContentView (2131296284); TextView var10000 = (TextView) this._$_findCachedViewById (id.tip6Tv); Intrinsics.checkExpressionValueIsNotNull (var10000, "tip6Tv"); var10000.setText ((CharSequence) "Auto find view for TextView") ((ImageView) this._$_findCachedViewById (id.tip6Img)) .setImageBitmap ((Bitmap) null); ((Button) this._$_findCachedViewById (id.tip6Btn)) .setOnClickListener ((OnClickListener) (new OnClickListener () {public final void onClick (View it) {KotlinTip6.this.test ();}}) } private final void test () {TextView var10000 = (TextView) this._$_findCachedViewById (id.tip6Tv); Intrinsics.checkExpressionValueIsNotNull (var10000, "tip6Tv"); var10000.setText ((CharSequence) "update");} public View _ $_ findCachedViewById (int var1) {if (this._$_findViewCache = = null) {this._$_findViewCache = new HashMap () } View var2 = (View) this._$_findViewCache.get (Integer.valueOf (var1)); if (var2 = = null) {var2 = this.findViewById (var1); this._$_findViewCache.put (Integer.valueOf (var1), var2);} return var2 } public void _ $_ clearFindViewByIdCache () {if (this._$_findViewCache! = null) {this._$_findViewCache.clear ();}

As shown in the above code, during the compilation phase, the plug-in will help us generate the view cache. The view is cached by a _ $_ findViewCache variable of the Hashmap structure. According to the corresponding id, it will first look it up in the cache, and then call findViewById to find it, and then store it in the HashMap.

FindViewByID in fragment

It's similar in fragment, with a slight difference, as shown in the following example:

Class Tip6Fragment: Fragment () {override fun onCreateView (inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {val view = inflater?.inflate (R.layout.fragment_tip6, container, false) / * * view cannot be used in onCreateView method at this time, it needs to be in onViewCreate The principle is that the plug-in uses getView to findViewById * / tip6Tv.text = "test2" return view} / * * needs to be in onViewCreate, and the principle is that the plug-in uses getView to findViewById * * / override fun onViewCreated (view: View?, savedInstanceState: Bundle?) {super.onViewCreated (view, savedInstanceState) tip6Tv.text = "test"}}

As shown above, Fragment needs to be aware that view cannot be used in the onCreateView method, otherwise a null pointer exception will occur. It needs to be in onViewCreate. The principle is that the plug-in uses getView to findViewById. Let's take a look at the code after converting the above code into java:

Public final class Tip6Fragment extends Fragment {private HashMap _ $_ findViewCache; @ Nullable public View onCreateView (@ Nullable LayoutInflater inflater, @ Nullable ViewGroup container, @ Nullable Bundle savedInstanceState) {View view = inflater! = null?inflater.inflate (2131296286, container, false): null; return view;} public void onViewCreated (@ Nullable View view, @ Nullable Bundle savedInstanceState) {super.onViewCreated (view, savedInstanceState); TextView var10000 = (TextView) this._$_findCachedViewById (id.tip6Tv) Intrinsics.checkExpressionValueIsNotNull (var10000, "tip6Tv"); var10000.setText ((CharSequence) "test");} public View _ $_ findCachedViewById (int var1) {if (this._$_findViewCache = = null) {this._$_findViewCache = new HashMap ();} View var2 = (View) this._$_findViewCache.get (Integer.valueOf (var1)) If (var2 = = null) {View var10000 = this.getView (); if (var10000 = = null) {return null;} var2 = var10000.findViewById (var1); this._$_findViewCache.put (Integer.valueOf (var1), var2);} return var2 } public void _ $_ clearFindViewByIdCache () {if (this._$_findViewCache! = null) {this._$_findViewCache.clear ();} / / $FF: synthetic method public void onDestroyView () {super.onDestroyView (); this._$_clearFindViewByIdCache ();}}

Similar to Activity, there will be a HashMap of View. The key difference is in _ $findCachedViewById, which requires getView or the View of the current Fragment, so the getView is still empty in onViewCreated, so the principle is easy to understand. In addition, the $_ clearFindViewByIdCache method is called on onDestroyView to clear the cache.

Tip7- uses local functions to extract duplicate code

Nesting of functions is provided in Kotlin, and new functions can be defined within the function. In this way, we can nest these advance functions in the function to extract duplicate code. As shown in the following case: see case code KotlinTip7 for details

Class User (val id: Int, val name: String, val address: String Val email: String) 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")} if (user.email.isEmpty ()) {throw IllegalArgumentException () "Can't save user ${user.id}: empty Email")} / / save to db...}

The above code is similar in determining whether name, address, and so on are empty. At this point, we can extract the same code together by declaring a general null function nested within the function:

/ * * use local functions to extract the same logic Remove duplicate code * * / fun saveUser2 (user: User) {fun validate (value: String, fildName: String) {if (value.isEmpty ()) {throw IllegalArgumentException ("Can't save user ${user.id}: empty $fildName")} validate (user.name, "Name") validate (user.address, "Address") validate (user.email "Email") / / save to db.}

In addition to using nested functions to extract, you can also use extension functions to extract at this time, as follows:

/ * * use extended functions to extract logic * * / fun User.validateAll () {fun validate (value: String, fildName: String) {if (value.isEmpty ()) {throw IllegalArgumentException ("Can't save user $id: empty $fildName")} validate (name, "Name") validate (address, "Address") validate (email "Email")} fun saveUser3 (user: User) {user.validateAll () / / save to db...}

Tip8- uses data classes to quickly implement model classes

To declare a model class in java, you need to implement a lot of code. First, you need to declare the variable as private, then you need to implement the get and set methods, and the corresponding hashcode equals toString methods, etc., as shown below: for more information, please see the case code Tip8.

Public static class User {private String name; private int age; private int gender; private String address; public String getName () {return name;} public void setName (String name) {this.name = name;} public int getAge () {return age } public void setAge (int age) {this.age = age;} public int getGender () {return gender;} public void setGender (int gender) {this.gender = gender;} public String getAddress () {return address } public void setAddress (String address) {this.address = address } @ Override public String toString () {return "User {" + "name='" + name +'\'+ ", age=" + age + ", gender=" + gender + ", address='" + address +'\'+'}' } @ Override public boolean equals (Object o) {if (this = = o) return true; if (o = = null | | getClass ()! = o.getClass () return false; User user = (User) o; if (age! = user.age) return false; if (gender! = user.gender) return false If (name! = null?! name.equals (user.name): user.name! = null) return false; return address! = null? Address.equals (user.address): user.address = = null;} @ Override public int hashCode () {int result = name! = null? Name.hashCode (): 0; result = 31 * result + age; result = 31 * result + gender; result = 31 * result + (address! = null? Address.hashCode (): 0); return result;}}

This code Java takes about 70 lines, and if you use kotlin, you can do it with only one line of code.

/ * * Kotlin automatically implements the get set method * * / class User (val name: String, val age: Int, val gender: Int, var address: String) / * * declares a data class with the data keyword, which not only automatically implements get set, but also automatically generates equals hashcode toString * * / data class User2 (val name: String, val age: Int, val gender: Int, var address)

For a class in Kotlin, the get set method is automatically implemented for its parameters. If you add the data keyword, equals hashcode toString will also be generated automatically. Principle in fact, most of the code in the data class is template code, and Kotlin cleverly puts the implementation of this template code at the stage of compiler processing.

Tip9- uses class delegation to quickly implement the decorator pattern

Implementation through inheritance can easily lead to vulnerability, for example, if you need to modify some behavior of other classes, one strategy in Java is to adopt the decorator pattern: create a new class, implement the same interface as the original class, and take the instance of the original class as a member variable. Methods that have the same behavior as the original class need not be modified, but need to be forwarded directly to the instance of the original class. As follows: see case code KotlinTip9 for details

/ * * Common decorator mode, in order to modify partial functions But you need to implement all interface functions * * / class CountingSet (val innerSet: MutableCollection = HashSet ()): MutableCollection {var objectAdded = 0 overrideval size: Int get () = innerSet.size / * * method to be modified * * / override fun add (element: t): Boolean {objectAdded++ return innerSet.add (element)} / * * method to be modified * * / override fun addAll (elements: Collection): Boolean {objectAdded + = elements.size return innerSet.addAll (elements)} override fun contains (element: t): Boolean {return innerSet.contains (element)} override fun containsAll (elements: Collection): Boolean {return innerSet.containsAll (elements)} override fun IsEmpty (): Boolean {return innerSet.isEmpty ()} override fun clear () {innerSet.clear ()} override fun iterator (): MutableIterator {return innerSet.iterator ()} override fun remove (element: t): Boolean {return innerSet.remove (element)} override fun removeAll (elements: Collection): Boolean {return innerSet.removeAll ( Elements)} override fun retainAll (elements: Collection): Boolean {return innerSet.retainAll (elements)}}

As shown above, if you want to modify some of HashSet's behavior functions add and addAll, you need to implement all the methods of the MutableCollection interface and forward these methods to innerSet for concrete implementation. Although only two of these methods need to be modified, the rest of the code is template code. As long as the template code is repeated, Kotlin, a brand new syntax candy, will find a way to put it in the compilation phase and then generate it. The class delegate by keyword can be used at this time, as shown below:

/ * * delegate the implementation of the interface to the innerSet member variable through the by keyword The function that needs to be modified can then go to override * * / class CountingSet2 (val innerSet: MutableCollection = HashSet ()): MutableCollection by innerSet {var objectAdded = 0 override fun add (element: t): Boolean {objectAdded++ return innerSet.add (element)} override fun addAll (elements: Collection): Boolean {objectAdded+ = elements.size return innerSet.addAll (elements)}}

Delegate the implementation of the interface to innerSet member variables through the by keyword, and then override the functions that need to be modified. You can achieve nearly 100 lines of function above by delegating 10 lines of code through the class, concise and clear, without the template code.

Tip10- Lambda expression simplifies code

See the case code KotlinTip10 lambda expression for details to simplify our code. To illustrate with the common OnClickListener in Android, we usually set it like this in Java:

TextView textView = new TextView (context); textView.setOnClickListener (new View.OnClickListener () {@ Override public void onClick (View v) {/ / handle click}})

In Java, you need to declare an anonymous inner class to handle, which can be simplified with lambda expressions.

Lambda expressions usually look like this

{x:Int, y:Int-> Xeroy}

Parameter-> expression and is always in curly braces

It as the default parameter name

Lambda capture, whose value is stored with the lambda code when the final variable is captured

A non-final variable whose value is encapsulated in a special wrapper whose reference is stored with the lambda code

Let's take a look at the example in Kotlin:

Val textView = TextView (context) / * * traditional method * * / textView.setOnClickListener (object: android.view.View.OnClickListener {override fun onClick (v: android.view.View?) {/ / handle click}}) / * * lambda method * * / textView.setOnClickListener ({v-> { / / handle click}})

When the parameter of lambda is not used, it can be omitted. When omitted, it can be replaced by it.

/ * * the parameter of lambda can be omitted if it is not used. When omitted, use it instead of * * / textView.setOnClickListener ({/ / handle click})

Lambda can mention it in the case of * of the parameter.

/ * * lambda can be mentioned in the case of one of the parameters * * / textView.setOnClickListener () {/ / handle click}

After lambda is mentioned, the function can be omitted if there are no other parentheses.

After / * * lambda is mentioned, the function can omit * * / textView.setOnClickListener {/ / handle click} if there are no other parentheses.

Let's take a look at how to define a function with lambda parameters if we implement it ourselves:

Interface OnClickListener {fun onClick ()} class View {var listener: OnClickListener? = null / * * traditional method * * / fun setOnClickListener (listener: OnClickListener) {this.listener = listener} fun doSth () {/ / some case: listener?.onClick ()} / * * declare lambda mode, listener: ()-> Unit * * / fun setOnClickListener (listener: ()-> Unit) {}}

After you need to declare the type of lambda in the function parameters, you can pass in an lambda expression when you call the function.

Tip11- with statement to simplify the code

With function prototype:

Inline fun with (receiver: t, block: t. ()-> R): r = receiver.block ()

The with function is not an extension function. The return value is * one line, and you can call the object's method directly.

Multiple declarations of the same variable can be omitted with the with statement in Kotlin. For example, the following function is detailed in the case code KotlinTip11

/ * * print the alphabet function. Within the function, the result variable is used in several places * * / fun alphabet (): String {val result = StringBuilder () result.append ("START\ n") for (letter in 'Aids.. print Z') {result.append (letter)} result.append ("\ nEND") return result.toString ()}

In the above function, the result variable appears five times. If you use the with statement, you don't have to do it again. Let's see how it is implemented step by step:

/ * * pass result as a parameter through the with statement Internally, the result variable can be represented by this * * / fun alphabet2 (): String {val result = StringBuilder () return with (result) {this.append ("START\ n") for (letter in 'letter.) {this.append (letter)} this.append ("\ nEND") this.toString ()}}

Through the with statement, passing result as a parameter, the result variable can be expressed internally through this, and the this can be omitted.

/ * * take the result parameter as a parameter through the with statement, and the internal this can also omit * * / fun alphabet3 (): String {val result = StringBuilder () return with (result) {append ("START\ n") for (letter in 'Aids.. parameters Z') {append (letter)} append ("\ nEND") toString ()}}

After the internal this is omitted, there is only one result now, which is actually unnecessary, so the following final version appears:

/ * * through the with statement, you can pass the object directly to others, omitting the declaration of the object * * / fun alphabet4 (): String {return with (StringBuilder ()) {append ("START\ n") for (letter in'Aids.. append ("\ nEND") toString ()}}

As above, we can change the number of explicit calls to the same variable from 5 to 0, and find out the charm of Kotlin.

Tip12- apply statement to simplify the code

Apply function prototype:

Inline fun T.apply (block: t. ()-> Unit): t {block (); return this}

The apply function, within the scope of the function, can call any method of the object and return the object.

In addition to using the with above to simplify multiple declarations of the same variable, you can also use the apply keyword to modify the function in tip11: see the case code KotlinTip12 for details

/ * * simplify the code with the application statement, and you can access the public properties and methods of the class in the curly braces of apply * * / fun alphabet5 () = StringBuilder (). Apply {append ("START\ n") for (letter in 'append (letter)} append ("\ nEND")}. ToString ()

Like the above, after passing the apply, you can access the public properties and methods of the class in the curly braces of the apply. This is very convenient for initialization of the corresponding class, such as the following example

/ * * use the application statement to simplify the initialization of the class. When the class is instantiated, you can use apply to implement all the steps that need to be initialized. Very concise * * / fun testApply (context: Context) {var imgView = ImageView (context). Apply {setBackgroundColor (0) setImageBitmap (null)} var textView = TextView (context). Apply {text = "content" textSize = 20.0f setPadding (10,0,0) 0)} var user = User (). Apply {age = 15 name = "Jack" val a = address address = "bbb"}}

When the class is instantiated, all the steps that need to be initialized can be implemented through apply, which is very concise.

Tip13- avoids NullPointerException during the compilation phase

NullPointerException is a headache for Java programmers. We know that Java is divided into detected and unchecked exceptions, and NullPointerException is an unchecked exception, that is to say, NullPointerException does not need to display to catch, often during the run, the program may report a NullPointerException and then crash. As an efficient and secure language, Kotlin tries to explicitly detect the null pointer problem during the compilation phase, leaving the problem in the compilation phase. Make the program more robust. For more information, please see case code KotlinTip13

Types in Kotlin are divided into nullable types and non-nullable types. Does it mean that it can be empty? Representative cannot be empty

Fun testNullType () {val a: String = "aa" / * * an is a non-empty type. The following assignment of a to null will compile without passing * * / / a = null a.length / * *? The declaration is a nullable type, which can be assigned as null * * / var b: String? = "bb" b = null / * * b is a nullable type. Or!. To visit * * / / b.length b?.length bread.length}

For a non-nullable type: if you directly assign a non-nullable type to an object that may be null, it cannot pass at compile time.

For a nullable type: pass? Declare that direct access cannot be compiled when accessing this type, and needs to be passed through?. Or!.

?. It means that if the type is empty, null is returned and no subsequent operations are made. If it is not empty, the corresponding method or property will be accessed.

!!. It means that NullPointerException is thrown if the type is empty, and the corresponding method or property is accessed if it is not empty, so this symbol is only used in very few specific scenarios, which means that the program does not handle the exception case and will throw NullPointerException like java code. And the following code must not appear in the code, which will make the code very readable, and if there is an exception in the null pointer, we can't immediately find out where it is empty:

/ * No chain continuous use!. * * / val user = User () usernames. Nameplates. Subsequence (0pr 5)!! .length

Corresponding to a nullable type, each visit to it needs to be carried with?. Judge

Val user: User? = User () / * * use?. Judge * * / user?.name user?.age user?.toString ()

But there's a lot of code, and kotlin does some optimizations.

/ * * or determine in advance whether it is empty. If it is not empty, it will be automatically converted to a non-empty type in this branch and you can directly access * * / if (user! = null) {user.name user.age user.toString ()}.

Determine in advance whether the type is empty by if. If it is not empty, it will be automatically converted to a non-empty type in this branch and can be accessed directly.

Let statement simplifies access to nullable object pairs

Let function prototype:

Inline fun T.let (block: (t)-> R): r = block (this)

The let function defaults to the current object as the it parameter of the closure, and the return value is * line in the function, or specify return.

The above code can also be done with the? .let statement, as follows:

/ * * through the let statement, after? .let, if it is empty, no action will be taken, and the operation after let will only be performed if it is not empty * * / user?.let {it.name it.age it.toString ()}

Through the let statement, after?. Let, if it is empty, there will be no action, and the operation after let will be performed only when it is not empty.

Elvis operator?: simplifies the handling of null values

If the value may be null, it can be troublesome to handle null values, such as the following:

/ * * handling of null values * * / fun testElvis (input: String?, user: User?) {val a: Int? If (input = = null) {a = input?.length} else {a =-1;} if (user = = null) {var newOne = User () newOne.save ()} else {user.save ()}}

The Elvis operator?: can simplify the above operation, and the?: symbol will only do the following in the case of null, as opposed to?. Let, for example, we can simplify the operation from above with two lines of code:

/ * Elvis operator?: simplify the handling of null values * / fun testElvis2 (input: String?, user: User?) {val b = input?.length?:-1; user?.save ()?: User (). Save ()}

Tip14- operator overload

Kotlin supports operator overloading, which is more flexible and intuitive for manipulating some objects.

Use operator to modify the plus\ minus function

Overloadable binary operator

A * B times

A / B div

A% B mod

A + B plus

A-B minus

The following example of Point facing coordinate points shows how to reload the operator

Class Point (val x: Int, val y: Int) {/ * * plus function overloads the addition operator * * / operator fun plus (other: Point) on Point objects: Point {return Point (x + other.x) Y + other.y)} / * * minus function overloads the subtraction operator * * / operator fun minus (other: Point) on the Point object: Point {return Point (x-other.x, y-other.y)} override fun toString (): String {return "[x-other.x, y-other.y]"}}

As shown above, the addition operator for the Point object is overloaded by the plus function, and the subtraction operator for the Point object is overloaded by the minus function, and then the two objects can be manipulated with the + and-signs:

Fun testOperator () {val point1 = Point (10,10) val point2 = Point (4,4) val point3 = point1 + point2 println (point3) println (point1-point2)}

Tip15- higher-order functions simplify the code

Higher-order function: a function that takes another function as a parameter or returns a value

Function type

(Int, String)-> Unit

Parameter type-> return type Unit cannot be omitted

Val list = listOf (2,5,10) / * pass function to filter * * / println (list.filter {it > 4}) / * * define function type * * / val sum = {x: Int, y: Int-> x + y} val action = {println (42)} val sum2: (Int, Int)-> Int = {x: Int Y: Int-> x + y} val action2: ()-> Unit = {println (42)}

Function as a parameter

Function as a parameter, that is, in a higher-order function, the parameter of a function can be a function type, for example, to define a function that deals with 2 and 3 accordingly according to the inherited operation function. For more information, please see case code KotlinTip15

/ * * define the operation functions for 2 and 3 * * / fun twoAndThree (operator: (Int, Int)-> Int) {val result = operator (2,3) println ("Result:$result")} fun test03 () {twoAndThree {a, b-> a + b} twoAndThree {a, b-> a * b}}

Operator is a function type. The specific type of the function is (Int, Int)-> Int, that is, enter two Int to return an int value. After the definition is finished, it can be used as above. To give another example, implement character filtering for the String class:

/ * * function as an argument Implement character filtering of String class * * / fun String.filter (predicate: (Char)-> Boolean): String {val sb = StringBuilder () for (index in 0 until length) {val element = get (index) if (predicate (element)) sb.append (element)} return sb.toString ()} fun test04 () {println ("12eafsfsfdbzzsa" .filter {it in 'asides.. filter f'})}

Predicate is a function type like above, and it will determine a Boolean value based on the char of the descendant.

Function as a return value

Function is also very useful as a return value. For example, our demand is to return different pricing formulas according to different types of express delivery. Ordinary express and premium express have different pricing rules. In this case, we can use the pricing rule function as the return value:

Enum class Delivery {STANDARD EXPEDITED} / * * return different express delivery methods according to different transportation types * * / fun getShippingCostCalculator (delivery: Delivery): (Int)-> Double {if (delivery = = Delivery.EXPEDITED) {return {6 + 2.1 * it}} return {1.3 * it}} fun test05 () {val calculator1 = getShippingCostCalculator (Delivery.EXPEDITED) val calculator2 = getShippingCostCalculator (Delivery.STANDARD) Println ("Ex costs ${calculator1 (5)}") println ("St costs ${calculator2 (5)}")}

If it is an ordinary express, the price is calculated according to the rule of 6 + 2.1 * it. If it is an advanced express, the price is calculated according to 6 + 2.1 * it, and different pricing functions are returned according to different types.

Tip16- uses Lambda to simplify policy patterns

The policy pattern is one of the common patterns, and the example of java is as follows. For more information, please see case code Tip16

/ * define policy interface * / public interface Strategy {void doSth ();} / * A policy * / public static class AStrategy implements Strategy {@ Override public void doSth () {System.out.println ("Do AStrategy") }} / * B Policy * / public static class BStrategy implements Strategy {@ Override public void doSth () {System.out.println ("Do BStrategy");}} / * Policy Enforcement * / public static class Worker {private Strategy strategy Public Worker (Strategy strategy) {this.strategy = strategy;} public void work () {System.out.println ("START"); if (strategy! = null) {strategy.doSth ();} System.out.println ("END");}

As shown in the above example, there are two strategies An and B. Worker does different work according to different policies. When using policies:

Worker worker1 = new Worker (new AStrategy ()); Worker worker2 = new Worker (new BStrategy ()); worker1.work (); worker2.work ()

To implement this policy pattern in java, it is inevitable to define the policy interface first, and then implement different policies according to the interface. In Kotlin, you can use Lambda to simplify the policy pattern. The above example is implemented in Kotlin:

/ * * Policy implementer * @ param strategy lambda type policy * / class Worker (privateval strategy: ()-> Unit) {fun work () {println ("START") strategy.invoke () println ("END")}} / * * Test * / fun testStrategy () {val worker1 = Worker ({println ("Do") A Strategy ")} val bStrategy = {println (" Do B Strategy ")} val worker2 = Worker (bStrategy) worker1.work () worker2.work ()}

You don't need to define the interface of the policy first, just pass the policy in the form of lambda expression.

These are all the contents of the article "how to use Kotlin to improve Productivity". Thank you for reading! Hope to share the content to help you, more related knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report