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 contract in Kotlin

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

Share

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

This article will explain in detail how to use contract in Kotlin. The editor thinks it is very practical, so I share it with you as a reference. I hope you can get something after reading this article.

test

Let's use the following two commonly used extension functions as examples.

Public inline fun T.run (block: t. ()-> R): r {contract {callsInPlace (block, InvocationKind.EXACTLY_ONCE)} return block ()} public inline fun CharSequence?.isNullOrEmpty (): Boolean {contract {returns (false) implies (this@isNullOrEmpty! = null)} return this = = null | | this.length = = 0}

Run and isNullOrEmpty I believe you often see in the development.

I don't know what that code does, so let's get rid of those lines of code and see what the difference is when the function is used.

Public inline fun T.runWithoutContract (block: t. ()-> R): r {return block ()} public inline fun CharSequence?.isNullOrEmptyWithoutContract (): Boolean {return this = = null | | this.length = = 0}

The above are two function calls after removing the contract {} code block.

Fun test () {var str1: String = "" var str2: String = "runWithoutContract {str1 =" jayce "} run {str2 =" jayce "} println (str1) / / jayce println (str2) / / jayce}

After testing, it seems that there is no problem, run code blocks can be executed normally, and the assignment operation has been done.

What if that's the case?

Remove the initial value of str and initialize it in the run code block

@ Testfun test () {var str1: String var str2: String runWithoutContract {str1 = "jayce"} run {str2 = "jayce"} println (str1) / / compilation failed (Variable 'str1' must be initialized) println (str2) / / compilation passed}

?

Didn't we initialize the assignment in runWithoutContract? Why is IDE still wrong? is there something wrong with IDE? If there's a problem, restart it. I'll go. The restart hasn't been solved yet. Okay, reload. No, no! Don't worry. Is that what Contract code blocks are for? Did it whisper something to IDE so that it compiles properly?

Okay, let's put this question aside for a while and then we'll see what's the difference between the isNullOrEmpty without contract and the one with contract.

Fun test () {val str: String? = "jayce" if (! str.isNullOrEmpty ()) {println (str) / / jayce} if (! str.isNullOrEmptyWithoutContract ()) {println (str) / / jayce}}

There still seems to be nothing wrong with it. I believe that according to the problems encountered above, you can guess that there must be a hole in it.

For example, in this case,

Fun test () {val str: String? = "jayce" if (! str.isNullOrEmpty ()) {println (str.length) / / compiled through} if (! str.isNullOrEmptyWithoutContract ()) {println (str.length) / / failed (Only safe (?.) Or non-null asserted (!) Calls are allowed on a nullable receiver of type String?)}}

According to the error prompt, you can see that after isNullOrEmptyWithoutContract determines the code block as flase, the str field is still considered by IDE to be a nullable type and must be nullified to pass. However, after the code block after isNullOrEmpty returns flase, IDE thinks that str is already non-empty, so there is no need to check null before using it.

View the contract function

Public inline fun contract (builder: ContractBuilder. ()-> Unit) {}

By tapping into the source code, we can see that contract is an inline function that takes an argument of a function type, which is an extension of ContractBuilder (that is, it has the context of ContractBuilder in this function body)

Take a look at what functions ContractBuilder provides us (mainly rely on these functions to agree on our own lambda functions)

Public interface ContractBuilder {/ / describes the case where the function returns normally without throwing any exceptions. @ ContractsDsl public fun returns (): Returns / / describes the situation returned by the function in value. Value can be true | false | null. ContractsDsl public fun returns (value: Any?): Returns / / describes the situation in which the function returns with a non-null value. @ ContractsDsl public fun returnsNotNull (): ReturnsNotNull / / describes the number of times lambda will be called in this function, and the number of times specified with kind @ ContractsDsl public fun callsInPlace (lambda: Function, kind: InvocationKind = InvocationKind.UNKNOWN): CallsInPlace}

Returns

Where returns () returns (value) returnsNotNull () will return a Returns inherited from SimpleEffect. Let's take a look at SimpleEffect.

Public interface SimpleEffect: Effect {/ / an expression that receives a Boolean value is used to change the function to indicate that when SimpleEffect is established, the expression that guarantees the Boolean value returns a value of true / / that an empty code block (`= = null`,`! = null`) can be passed to determine the instance statement (`is`, `! is`). Public infix fun implies (booleanExpression: Boolean): ConditionalEffect}

You can see that there is an infix function implies in SimpleEffect. You can use ContractBuilder's function to specify a return case and then use implies to declare the incoming expression as true.

If we see here, then we should know what the contract added by isNullOrEmpty () means.

Public inline fun CharSequence?.isNullOrEmpty (): Boolean {contract {/ / the case where the return value is false returns (false) / / means that the object calling this function is not empty (this@isNullOrEmpty! = null) returns (false) implies (this@isNullOrEmpty! = null)} return this = = null | | this.length = = 0}

Because the contract code block is added to isNullOrEmpty, tell IDE that the return value of false means that the object calling the function is not empty. So we can use non-empty objects directly after the judgment statement.

Some students may still not understand, here is another useless example (running will definitely crash ha. )

@ ExperimentalContracts / / because this feature is still being tested, we need to add this note fun CharSequence?.isNotNull (): Boolean {contract {/ / the return value is true returns (true) / / which means that the object implies / / calls the function is StringBuilder (this@isNotNull is StringBuilder) returns (true) implies (this@isNotNull is StringBuilder)} return this! = null} fun test () {val str: String? = "jayce" If (str.isNotNull ()) {str.append ("") / / String does not have this function. Because we used contract to force him to StringBuilder, so we have this function}}

Yes, the IDE did not report an error, because as declared by our contract, as long as the function returns true, the object calling the function is a StringBuilder.

CallsInPlace

/ / describe the number of times lambda will be called in this function, and specify @ ContractsDsl public fun callsInPlace (lambda: Function, kind: InvocationKind = InvocationKind.UNKNOWN) with kind: CallsInPlace

You can know that callsInPlace is used to specify the number of lambda function calls

There are four values for kind

InvocationKind.AT_MOST_ONCE: called at most once

InvocationKind.AT_LEAST_ONCE: call at least once

InvocationKind.EXACTLY_ONCE: call once

InvocationKind.UNKNOWN: unknown, unspecified default

Let's take a look at what the contract in the run function declared before.

Public inline fun T.run (block: t. ()-> R): r {contract {/ / block function, just once called callsInPlace (block, InvocationKind.EXACTLY_ONCE)} return block ()}

If you see here, you should know why the runWithoutContract we wrote will Variable 'str1' must be initialized, but the run of the system will not report an error, because run declares that lambda will be called once, so it must initialize the str2, but runWithoutContract does not declare it, so the IDE will report an error (because it may not be called, so it will not do the initialization operation).

This is the end of the article on "how to use contract in Kotlin". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, please share it for more people to see.

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