Kotlin is an object oriented language with functional features. This chapter covers important and relevant features of the language slit into basic and intermediate. Another chapter covers advanced features.
In the this section, the terms 'argument' and 'parameter' are used interchangeably.
s = null; s.isPresent()"),I(";")])],-1),C=l("h3",{id:"enumerations",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#enumerations"},[l("span",null,"Enumerations")])],-1),r={href:"https://www.geeksforgeeks.org/enum-classes-in-kotlin",target:"_blank",rel:"noopener noreferrer"},Z=l("ul",null,[l("li",null,[l("code",null,"when"),I(" statements support enumerations.")]),l("li",null,"Enum constants can declare their own anonymous classes with their corresponding methods, as well as with overriding base methods."),l("li",null,"An enum class can implement an interface but it cannot derive from a class"),l("li",null,"There are methods for listing the defined enum constants and getting an enum constant by its name."),l("li",null,"Every enum constant has properties for obtaining its name and position (starting with 0).")],-1),p={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGphdmEudXRpbC5mdW5jdGlvbi5CaW5hcnlPcGVyYXRvclxuaW1wb3J0IGphdmEudXRpbC5mdW5jdGlvbi5JbnRCaW5hcnlPcGVyYXRvclxuXG4vLyAvLyBTb3VyY2U6IGh0dHBzOi8va290bGlubGFuZy5vcmcvZG9jcy9lbnVtLWNsYXNzZXMuaHRtbFxuXG5lbnVtIGNsYXNzIERpcmVjdGlvbiB7XG4gICAgTk9SVEgsIFNPVVRILCBXRVNULCBFQVNUXG59XG5cbmVudW0gY2xhc3MgQ29sb3IodmFsIHJnYjogSW50KSB7XG4gICAgUkVEKDB4RkYwMDAwKSxcbiAgICBHUkVFTigweDAwRkYwMCksXG4gICAgQkxVRSgweDAwMDBGRilcbn1cblxuZW51bSBjbGFzcyBQcm90b2NvbFN0YXRlIHtcbiAgICBXQUlUSU5HIHtcbiAgICAgICAgb3ZlcnJpZGUgZnVuIHNpZ25hbCgpID0gVEFMS0lOR1xuICAgIH0sXG5cbiAgICBUQUxLSU5HIHtcbiAgICAgICAgb3ZlcnJpZGUgZnVuIHNpZ25hbCgpID0gV0FJVElOR1xuICAgIH07XG5cbiAgICBhYnN0cmFjdCBmdW4gc2lnbmFsKCk6IFByb3RvY29sU3RhdGVcbn1cblxuZW51bSBjbGFzcyBJbnRBcml0aG1ldGljcyA6IEJpbmFyeU9wZXJhdG9yPEludD4sIEludEJpbmFyeU9wZXJhdG9yIHtcbiAgICBQTFVTIHtcbiAgICAgICAgb3ZlcnJpZGUgZnVuIGFwcGx5KHQ6IEludCwgdTogSW50KTogSW50ID0gdCArIHVcbiAgICB9LFxuICAgIFRJTUVTIHtcbiAgICAgICAgb3ZlcnJpZGUgZnVuIGFwcGx5KHQ6IEludCwgdTogSW50KTogSW50ID0gdCAqIHVcbiAgICB9O1xuXG4gICAgb3ZlcnJpZGUgZnVuIGFwcGx5QXNJbnQodDogSW50LCB1OiBJbnQpID0gYXBwbHkodCwgdSlcbn1cblxuZnVuIG1haW4oKSB7XG4gICAgdmFsIHByb3RvY29sU3RhdGUgPSBQcm90b2NvbFN0YXRlLlRBTEtJTkdcbiAgICBwcmludGxuKFwiJHByb3RvY29sU3RhdGUsIHNpZ25hbCAtPiAke3Byb3RvY29sU3RhdGUuc2lnbmFsKCl9XCIpXG4gICAgcHJpbnRsbihcIm5hbWU6ICR7cHJvdG9jb2xTdGF0ZS5uYW1lfVwiKVxuICAgIHByaW50bG4oXCJvcmRpbmFsOiAke3Byb3RvY29sU3RhdGUub3JkaW5hbH1cIilcbiAgICBcbiAgICB2YWwgb3A6IEludEJpbmFyeU9wZXJhdG9yID0gSW50QXJpdGhtZXRpY3MuUExVU1xuICAgIHByaW50bG4ob3AuYXBwbHlBc0ludCgyLCAzMCkpXG4gICAgXG4gICAgcHJpbnRsbihQcm90b2NvbFN0YXRlLnZhbHVlcygpLmpvaW5Ub1N0cmluZyhcIiwgXCIpKVxuICAgIHByaW50bG4oUHJvdG9jb2xTdGF0ZS52YWx1ZU9mKFwiV0FJVElOR1wiKSlcbiAgICBcbiAgICB2YWwgYSA9IDEzXG4gICAgdmFsIGIgPSAzMVxuICAgIC8vIGxvb3AgdGhyb3VnaCB0aGUgcG9zc2libGUgZW51ZW1lcmF0aW9uc1xuICAgIGZvciAoZiBpbiBJbnRBcml0aG1ldGljcy52YWx1ZXMoKSkge1xuICAgICAgICBwcmludGxuKFwiJGYoJGEsICRiKSA9ICR7Zi5hcHBseShhLCBiKX1cIilcbiAgICB9XG59In0=",target:"_blank",rel:"noopener noreferrer"},B={href:"https://kotlinlang.org/docs/enum-classes.html#working-with-enum-constants",target:"_blank",rel:"noopener noreferrer"},X=l("h3",{id:"🧪-exercises",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#🧪-exercises"},[l("span",null,"🧪 Exercises")])],-1),W=l("h4",{id:"exercise-1",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercise-1"},[l("span",null,"Exercise 1")])],-1),y={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gUTogZGVjbGFyZSBhIGZ1bmN0aW9uIGNhbGxlZCBjb3VudFZvd2VscyB0aGF0IHRha2VzIGEgU3RyaW5nIGFyZ3VtZW50IFxuLy8gYW5kIHJldHVybnMgaXRzIG51bWJlciBvZiB2b3dlbHMgKGEsIHUsIGkgLCBlLCBvKS4gUGxlYXNlIHVzZSBhIGZvb3IgbG9vcC4ifQ==",target:"_blank",rel:"noopener noreferrer"},A={class:"custom-container details"},V=l("summary",null,"Please open to see the solution(s)",-1),Y={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gUTogZGVjbGFyZSBhIGZ1bmN0aW9uIGNhbGxlZCBjb3VudFZvd2VscyB0aGF0IHRha2VzIGEgU3RyaW5nIGFyZ3VtZW50IFxuLy8gYW5kIHJldHVybnMgaXRzIG51bWJlciBvZiB2b3dlbHMgKGEsIHUsIGkgLCBlLCBvKS4gUGxlYXNlIHVzZSBhIGZvb3IgbG9vcC5cblxuZnVuIGNvdW50Vm93ZWxzKHRleHQ6IFN0cmluZykgOiBJbnQge1xuICAgIHZhciBjb3VudCA9IDBcbiAgICBmb3IgKGMgaW4gdGV4dCl7XG4gICAgICAgIGlmIChcImF1aWVvXCIuY29udGFpbnMoYykpe1xuICAgICAgICAgICAgY291bnQgKz0gMVxuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiBjb3VudFxufVxuXG5mdW4gbWFpbihhcmdzOkFycmF5PFN0cmluZz4pe1xuICAgIHByaW50bG4oY291bnRWb3dlbHMoXCJIZWxsb1wiKSlcbiAgICBwcmludGxuKGNvdW50Vm93ZWxzKFwiV29ybGRcIikpXG59In0=",target:"_blank",rel:"noopener noreferrer"},J=l("h4",{id:"exercise-2",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercise-2"},[l("span",null,"Exercise 2")])],-1),R={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gUTogcHJpbnQgdGhlIG1heCB2YWx1ZSBvZiB0aGUgbGlzdCAoaGludDogeW91IGNhbiB1c2UgdGhlIG1heCBtZXRob2QpXG5mdW4gcHJpbnRNYXgoaXRlbXM6IEFycmF5PEludD4/KXtcbiAgICBcbn1cblxuZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPil7XG4gICAgdmFyIGl0ZW1zOiBBcnJheTxJbnQ+PyA9IG51bGxcblx0aXRlbXMgPSBhcnJheU9mKDEsIDIsIDIpXG5cdHByaW50TWF4KGl0ZW1zKVxuICAgIFxuICAgIC8vIFE6IHByaW50IHRoZSB2YWx1ZSBvZiB0aGUgc2Vjb25kIGl0ZW0gb2YgdGhlIGFycmF5XG5cblx0Ly8gUTogcHJpbnQgdGhlIHN1bSBvZiB0aGUgZmlyc3QgYW5kIGxhc3QgaXRlbSBvZiB0aGUgYXJyYXlcbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},v={class:"custom-container details"},x=l("summary",null,"Please open to see the solution(s)",-1),F={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gUTogcHJpbnQgdGhlIG1heCB2YWx1ZSBvZiB0aGUgbGlzdCAoaGludDogeW91IGNhbiB1c2UgdGhlIG1heCBtZXRob2QpXG5mdW4gcHJpbnRNYXgoaXRlbXM6IEFycmF5PEludD4/KXtcbiAgICBwcmludGxuKGl0ZW1zPy5tYXgoKSA/OiAwKVxuICAgIHByaW50bG4oaWYgKGl0ZW1zICE9IG51bGwpIGl0ZW1zLm1heCgpIGVsc2UgMClcbn1cblxuZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPil7XG4gICAgdmFyIGl0ZW1zOiBBcnJheTxJbnQ+PyA9IG51bGxcblx0Ly9pdGVtcyA9IGFycmF5T2YoMSwgMiwgMylcblx0cHJpbnRNYXgoaXRlbXMpXG4gICAgcHJpbnRNYXgobnVsbClcbiAgICBcbiAgICAvLyBROiBwcmludCB0aGUgdmFsdWUgb2YgdGhlIHNlY29uZCBpdGVtIG9mIHRoZSBhcnJheVxuICAgIC8vIC8vIFE6IHByaW50IHRoZSBzdW0gb2YgdGhlIGZpcnN0IGFuZCBsYXN0IGl0ZW0gb2YgdGhlIGFycmF5XG4gICAgaWYgKGl0ZW1zICE9IG51bGwpe1xuICAgICAgICBwcmludGxuKGl0ZW1zWzJdKVxuXHRcdHByaW50bG4oaXRlbXNbMl0gKyBpdGVtc1swXSlcbiAgICB9XG59In0=",target:"_blank",rel:"noopener noreferrer"},H=i('Kotlin allows to write concise OOP code and has the following features:
- Available common features: classes, inheritance, interfaces, and abstract classes.
- Native support of properties: do not define getters and setters unless needed.
- Just add
get()
and set(value)
functions next to the property declaration.
- Constructor arguments are defined next to the class name
class ClassName(arg1, atg2, )
- Prefixing a constructor arguments with
val
or var
makes it a property (val
makes it read-only). - The constructor name is
init
and does not require parameters. - The compiler checks that all non-nullable properties are initialized by the end of the constructor.
- ⚠️ The compiler does not check the initialization of
lateinit
properties. Thus, accessing them before while uninitialized causes an exception.
- Prefix classes with
open
to allow inheritance. - Kotlin enables the
public
access level by default. - The equality operator
==
calls equals()
(as opposed to Java which uses reference equality). - A companion object contains static methods and properties.
- Extensions add function and properties to existing classes.
- 💡 They replace inheritance in many situations.
- For example, we can add functions to the String class instead of creating a new
StringUtils
class.
- Sealed classes and interfaces cannot be extended or implemented by third parties.
Do not define accessors unless needed
As opposed to Java, Kotlin supports properties and allows to add accessors later without refactoring the code that calls these properties. Thus, by default, just define the name of properties without accessors and use them directly.
',5),w={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gY2xhc3Mgd2l0aCBkZWZhdWx0IGNvbnN0cnVjdG9yXG4vLyBvcGVuOiBhbGxvdyBpbmhlcml0YW5jZSAoY2xhc3NlcyBhbmQgcHJvcGVydGllcyBhcmUgZmluYWwgYnkgZGVmYXVsdClcbi8vIC8vIHZhbCBnZW5lcmF0ZXMgcmVhZC1vbmx5IHByb3BlcnRpZXNcbm9wZW4gY2xhc3MgUGxhbmV0KG9wZW4gdmFsIHJhZGl1czogTG9uZykge1xuICAgIC8vIHZhciBnZW5lcmF0ZXMgcHJvcGVydGllc1xuICAgIHZhciByb3RhdGlvblNwZWVkID0gMFxuICAgIFxuICAgIC8vIFdlIGNhbiBjdXN0b21pemUgdGhlIGdldHRlciBhbmQgdGhlIHNldHRlciBvZiB0aGUgcHJvcG9lcnR5XG4gICAgdmFsIHJhZGl1c0luTWV0ZXI6IExvbmcgXG4gICAgXHRnZXQoKSA9IHJhZGl1cyAqIDEwMDBcbn1cbmNsYXNzIEluaGFiaXRlZFBsYW5ldChvdmVycmlkZSB2YWwgcmFkaXVzOiBMb25nLCB2YXIgcG9wdWxhdGlvbjogTG9uZyk6IFBsYW5ldChyYWRpdXMpIHtcbiAgICBpbml0IHsgcHJpbnRsbihcImNvbnN0cnVjdG9yIGNhbGxlZFwiKSB9XG4gICAgXG4gICAgdmFyIHBvcHVsYXRpb25JbkJpbGxpb246IEludFxuICAgIGdldCgpID0gKHBvcHVsYXRpb24gLyAxXzAwMF8wMDBfMDAwKS50b0ludCgpXG4gICAgc2V0KHZhbHVlKSB7XG4gICAgICAgIHBvcHVsYXRpb24gPSB2YWx1ZS50b0xvbmcoKSAqIDFfMDAwXzAwMF8wMDBcbiAgICB9XG4gICAgXG4gICAgZnVuIGluY3JlYXNlUG9wdWxhdGlvbihhbW91bnQ6IExvbmcpe1xuICAgICAgICBwb3B1bGF0aW9uICs9IGFtb3VudFxuICAgIH0gICAgICAgXG59XG5cbi8vIGV4dGVuc2lvbnNcbmZ1biBTdHJpbmcuY291bnRMZXR0ZXJzKGxldHRlcnM6IEFycmF5PENoYXI+KTogSW50IFxuXHQ9IHRoaXMuZmlsdGVyIHsgbGV0dGVycy5jb250YWlucyhpdCkgfVxuICAgIFx0XHQubWFwIHsgMSB9LnN1bSgpXG4gICAgICAgICAgICBcbnZhbCBJbmhhYml0ZWRQbGFuZXQuaGFzSW5oYWJpdGFudHM6IEJvb2xlYW4gXG5cdGdldCgpID0gcG9wdWxhdGlvbiA+IDBcblxuZnVuIG1haW4oKXtcbiAgICB2YWwgZWFydGggPSBJbmhhYml0ZWRQbGFuZXQoNl8zNzEsIDdfNzUzXzAwMF8wMDBfMDAwKVxuICAgIHByaW50bG4oZWFydGgpXG4gICAgcHJpbnRsbihlYXJ0aC5yYWRpdXMpXG4gICAgcHJpbnRsbihlYXJ0aC5wb3B1bGF0aW9uKVxuICAgIGVhcnRoLnBvcHVsYXRpb24gKz0gMVxuICAgIHByaW50bG4oZWFydGgucG9wdWxhdGlvbilcbiAgICBwcmludGxuKGVhcnRoLnBvcHVsYXRpb25JbkJpbGxpb24pXG4gICAgXG4gICAgZWFydGguaW5jcmVhc2VQb3B1bGF0aW9uKDEwMClcbiAgICBwcmludGxuKGVhcnRoLnBvcHVsYXRpb24pXG4gICAgcHJpbnRsbihlYXJ0aC5wb3B1bGF0aW9uSW5CaWxsaW9uKVxuICAgIFxuICAgIHByaW50bG4oXCJIZWxsb1wiLmNvdW50TGV0dGVycyhhcnJheU9mKCdIJywgJ2UnKSkpXG4gICAgcHJpbnRsbihlYXJ0aC5oYXNJbmhhYml0YW50cylcbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},N=l("h3",{id:"data-class",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#data-class"},[l("span",null,"Data class")])],-1),S=l("strong",null,"final",-1),k={href:"https://kotlinlang.org/docs/data-classes.html",target:"_blank",rel:"noopener noreferrer"},E=i("equals()
and hashCode()
toString()
of the form "class(field=value, ...)"
componentN()
that correspond to the properties in their order of declaration.copy()
",1),L={href:"https://kotlinlang.org/docs/data-classes.html",target:"_blank",rel:"noopener noreferrer"},z=l("ul",null,[l("li",null,"The primary constructor needs to have at least one parameter."),l("li",null,"All primary constructor parameters need to be marked as val or var."),l("li",null,"They cannot be abstract, open, sealed, or inner (💡 but extensions are possible).")],-1),f={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gZGF0YSBjbGFzcyBhcmUgZmluYWwgY2xhc3NlcyBnZW5lcmF0ZSBtYW55IHVzZWZ1bCBib2lsZXJwbGF0ZSBjb2RlIChoYXNoQ29kZSwgZXF1YWxzLCBjb21wb25lbnROKCksIGNvcHkgYW5kIHRvU3RyaW5nKVxuZGF0YSBjbGFzcyBTcGFjZVNoaXAodmFsIG1heFNwZWVkOiBJbnQsIHZhciBjdXJyZW50U3BlZWQ6IEludCA9IDApXG5cbmZ1biBtYWluKCkge1xudmFsIHNoaXAxID0gU3BhY2VTaGlwKDEwMDAsIDApXG4gICAgdmFsIHNoaXAyID0gU3BhY2VTaGlwKDEwMDAsIDApXG4gICAgdmFsIHNoaXAzID0gU3BhY2VTaGlwKDEwMDAsIDEwKVxuICAgIHByaW50bG4oc2hpcDEpXG4gICAgcHJpbnRsbihzaGlwMSA9PSBzaGlwMilcbiAgICBwcmludGxuKHNoaXAxID09IHNoaXAzKVxuICAgIFxuICAgIHByaW50bG4oc2hpcDEuY29tcG9uZW50MSgpKVxuICAgIHByaW50bG4oc2hpcDEuY29tcG9uZW50MigpKVxuICAgIFxuICAgIC8vIGNvbXBvbmVudHMgZW5hYmxlIGRlc3RydWN0dXJpbmdcbiAgICB2YWwgKG1heFNwZWVkLCBjdXJyZW50U3BlZWQpID0gc2hpcDJcbiAgICBwcmludGxuKFwiTWF4IHNwZWVkOiAkbWF4U3BlZWQuIEN1cnJlbnQgc3BlZWQ6ICRjdXJyZW50U3BlZWRcIilcbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},j=l("h3",{id:"functional-programming",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#functional-programming"},[l("span",null,"Functional programming")])],-1),U=l("h4",{id:"general-concepts",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#general-concepts"},[l("span",null,"General concepts")])],-1),K={href:"https://www.geeksforgeeks.org/functional-programming-paradigm",target:"_blank",rel:"noopener noreferrer"},M=l("p",null,"Let's briefly explain these concepts:",-1),Q=l("li",null,"Immutable variables means that we cannot change the value of a variable or its properties once it has been created. If we want to do so, we must create a new instance with the new value.",-1),T=l("li",null,"Pure functions are functions that do not have side effects and will thus return always the same output given the same input.",-1),D=l("li",null,"Functions are first class citizens: they can be assigned to a variable or used in higher-order functions (passed as a function parameter to another function or returned from a function).",-1),_={href:"https://ericnormand.me/podcast/what-is-referential-transparency",target:"_blank",rel:"noopener noreferrer"},P=l("p",null,"💡 Pure functional languages provide these features natively and enforces them (at build time).",-1),O=l("h4",{id:"kotlin-and-functional-programming",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#kotlin-and-functional-programming"},[l("span",null,"Kotlin and functional programming")])],-1),q={href:"https://github.com/Kotlin/kotlinx.collections.immutable",target:"_blank",rel:"noopener noreferrer"},$={class:"custom-container warning"},ll=l("p",{class:"custom-container-title"},"listOf generates read-only lists, which are not immutable",-1),Il={href:"https://www.baeldung.com/kotlin/immutable-collections",target:"_blank",rel:"noopener noreferrer"},gl=i(`@Test
+fun givenReadOnlyList_whenCastToMutableList_checkNewElementsAdded(){
+ val list: List<String> = listOf("This", "Is", "Totally", "Immutable")
+ (list as MutableList<String>)[2] = "Not"
+ assertEquals(listOf("This", "Is", "Not", "Immutable"), list)
+}
+
`,1),cl={href:"https://arrow-kt.io/",target:"_blank",rel:"noopener noreferrer"},il=l("h4",{id:"declarative-programming",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#declarative-programming"},[l("span",null,"Declarative programming")])],-1),el=l("p",null,[I("Declarative programming is a famous style within functional programming. It consists of writing code as a chaining of function calls in this style "),l("code",null,"val result = f(x).g(y). ..."),I(". Higher order functions replace many situation where we would use loops. This favors readable code which is easy to debug an maintain.")],-1),al={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oKSB7XG4gICAgdmFsIGZhbnRhc3lOYW1lcyA9IGxpc3RPZihcIkNsZWJicmVyXCIsIFwiU25pbmp1clwiLCBcIk1vZGRuYWFjXCIsIFwiTGVlZG5hdFwiICxcIkdpZXJ3eXN0XCIsIFwiWmV2dXJpZ1wiLCBcIkNsYWFtcGFyYWlydFwiLCBcIkd1ZmFwcmFhbVwiLCBcIlJpZW1hcHJhc3RcIiwgXCJCaWxhbmpvbVwiKVxuICAgIFxuICAgIC8vIGZpbmQgbmFtZXMgdGhhdCBzdGFydCB3aXRoIFwiR1wiIG9yIGVuZCB3aXRoIFwidFwiIGFuZCByZXR1cm4gdGhlbSBhcyBjYXBpdGFsIGxldHRlcnNcbiAgICB2YWwgc2VsZWN0ZWROYW1lcyA9IGZhbnRhc3lOYW1lcy5maWx0ZXIgeyBpdC5zdGFydHNXaXRoKCdHJykgfHwgaXQuZW5kc1dpdGgoJ3QnKSB9Lm1hcCB7IGl0LnRvVXBwZXJDYXNlKCkgfVxuICAgIHByaW50bG4oXCJGaWx0ZXJlZCBuYW1lcyAke3NlbGVjdGVkTmFtZXMuam9pblRvU3RyaW5nKFwiLCBcIil9XCIpXG4gICAgXG4gICAgLy8gY291bnQgdGhlIG51bWJlciBvZiB2b3dlbHNcblx0Ly8gZmxhcG1hcCBhbGxvd3MgdG8gY29udmVydCBhIDJkIGFycmF5IHRvIGEgMWQgYXJyYXkgYnkgY29uY2F0ZW5hdGluZyB0aGUgZWxlbWVudHMgKGNhbGxlZCBhIGZsYXQgb3BlcmF0aW9uKVxuXHR2YWwgdm93ZWxDb3VudCA9IGZhbnRhc3lOYW1lcy5mbGF0TWFweyBpdC50b0xpc3QoKSB9LmZpbHRlcnsgXCJhaXVlb1wiLmNvbnRhaW5zKGl0KSB9Lm1hcCB7IDEgfS5zdW0oKVxuICAgIHZhbCB2b3dlbENvdW50MiA9IGZhbnRhc3lOYW1lcy5mbGF0TWFweyBpdC50b0xpc3QoKSB9XG4gICAgICAgIC5tYXB7IGlmIChcImFpdWVvXCIuY29udGFpbnMoaXQpKSAxIGVsc2UgMCB9XG4gICAgICAgIC5yZWR1Y2UgeyBhY2MsIGN1cnJlbnQgLT4gYWNjICsgY3VycmVudCB9XG4gICAgXG4gICAgcHJpbnRsbihcIlZvd2VsIGNvdW50OiAkdm93ZWxDb3VudFwiKVxuICAgIHByaW50bG4oXCJWb3dlbCBjb3VudDogJHZvd2VsQ291bnQyXCIpXG59In0=",target:"_blank",rel:"noopener noreferrer"},nl=l("h3",{id:"kotlin-and-java-interoperability",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#kotlin-and-java-interoperability"},[l("span",null,"Kotlin and Java interoperability")])],-1),bl=l("li",null,"Kotlin is designed with Java interoperability in mind.",-1),sl=l("li",null,"Kotlin code may require some annotations to be called from Java.",-1),tl=l("li",null,"It is possible to mix Java and Kotlin in the same project.",-1),ol=l("li",null,"JetBrain's IntelliJ and Android Studio can convert to Kotlin when pasting java code.",-1),dl=l("code",null,"@JvmRecord",-1),Gl={href:"https://kotlinlang.org/docs/jvm-records.html",target:"_blank",rel:"noopener noreferrer"},ul=l("li",null,[I("It is much more easier and natural to call Java from Kotlin. "),l("ul",null,[l("li",null,"For example: Java accessors are converted to Kotlin properties.")])],-1),ml={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGphdmEudXRpbC4qXG5mdW4gY29udmVydFRvQXJyYXlMaXN0KGl0ZW1zOiBMaXN0PEludD4pOiBBcnJheUxpc3Q8SW50PiB7XG4gICAgLy8gaW5zdGFudGlhdGUgYXMgaW4gSmF2YSB3aXRob3V0ICduZXcnXG4gICAgdmFsIGxpc3QgPSBBcnJheUxpc3Q8SW50PigpXG4gICAgLy8gJ2ZvcictbG9vcHMgd29yayBmb3IgSmF2YSBjb2xsZWN0aW9uczpcbiAgICBmb3IgKGl0ZW0gaW4gaXRlbXMpIHsgXG4gICAgICAgIGxpc3QuYWRkKGl0ZW0pXG4gICAgfVxuICAgIFxuICAgIGZvciAoaSBpbiAwLi5pdGVtcy5zaXplIC0gMSkge1xuICAgICAgICBsaXN0W2ldID0gaXRlbXNbaV1cbiAgICB9XG4gICAgcmV0dXJuIGxpc3Rcbn1cbmZ1biBtYWluKCl7XG4gICAgdmFsIGxpc3QgPSBjb252ZXJ0VG9BcnJheUxpc3QobGlzdE9mKDAsIDIsIC0xKSlcbiAgICBwcmludGxuKFwibGlzdDogJHtsaXN0fS4gdHlwZTogJHtsaXN0OjpjbGFzcy5xdWFsaWZpZWROYW1lfVwiKVxuICAgIC8vbGlzdDogWzAsIDIsIC0xXS4gdHlwZTogamF2YS51dGlsLkFycmF5TGlzdFxufSJ9",target:"_blank",rel:"noopener noreferrer"},hl=l("code",null,"List",-1),Cl=l("code",null,"ArrayList",-1),rl={href:"https://kotlinlang.org/docs/jvm-get-started.html",target:"_blank",rel:"noopener noreferrer"},Zl=l("h3",{id:"🧪-exercises-1",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#🧪-exercises-1"},[l("span",null,"🧪 Exercises")])],-1),pl=l("h4",{id:"exercise-3",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercise-3"},[l("span",null,"Exercise 3")])],-1),Bl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gRXh0ZW5zaW9ucyBhbGxvd3MgdG8gYWRkIGZ1bmN0aW9ucyBvciBwcm9wZXJ0aWVzIHRvIGFuIGV4aXN0aW5nIGNsYXNzIChldmVuIGNsYXNzZXMgaW1wb3J0ZWQgZnJvbSBvdGhlciBsaWJyYXJpZXMpXG4vLyBodHRwczovL2tvdGxpbmxhbmcub3JnL2RvY3MvcmVmZXJlbmNlL2V4dGVuc2lvbnMuaHRtbFxuXG5vcGVuIGNsYXNzIEFkdmVudHVyZXIodmFyIGhwOiBJbnQpe1xuICAgIHZhbCBtYXhIcCA9IGhwXG4gICAgZnVuIGF0dGFjaygpOlN0cmluZyA9IFwiSSBkZWFsdCAxMCBkYW1hZ2VcIlxufVxuLy8gRXh0ZW5zaW9uIGZ1bmN0aW9uIHN5bnRheDogZnVuIGNsYXNzTmFtZS5mdW5jdGlvbk5hbWUocHJhbXMpIDogcmV0dXJuVHlwZVxuZnVuIEFkdmVudHVyZXIucmVwb3J0U3RhdHVzKCk6U3RyaW5nIHtcbiAgICByZXR1cm4gXCJJIGhhdmUgJHtocH0gSFBcIlxufVxuXG5mdW4gQWR2ZW50dXJlci5hdHRhY2soKTpTdHJpbmcgPSBcIkkgZGVhbHQgMTAwMCBkYW1hZ2VcIlxuXG5jbGFzcyBCYXJiYXJpYW4oaHA6IEludCk6IEFkdmVudHVyZXIoaHApXG5cbmZ1biBCYXJiYXJpYW4ucmVwb3J0U3RhdHVzKCk6U3RyaW5nID0gXCJJIGFtIGEgYmFyYmFyaWFuIGFuZCBJIGhhdmUgJHtocH0gSFBcIlxuXG5mdW4gcHJpbnRTdGF0dXMoYWR2ZW50dXJlcjogQWR2ZW50dXJlcikge1xuICAgIHByaW50bG4oYWR2ZW50dXJlci5yZXBvcnRTdGF0dXMoKSlcbn1cblxuZnVuIG1haW4oKXtcbiAgICAvLyBUT0RPIGNyZWF0ZSBhbiBhZHZlbnR1cmVyIHdpdGggMjAwaHAgYW5kIHByaW50IHRoZSByZXN1bHQgb2YgdGhlIHJlcG9ydFN0YXR1cyBleHRlbnNpb24gbWV0aG9kXG5cbiAgICAvLyBUT0RPIHByaW50IHRoZSByZXN1bHQgb2YgdGhlIGF0dGFjayBmdW5jdGlvbi4gV2hpY2ggdmVyc2lvbiBpcyByZXRhaW5lZCAobWVtYmVyIGZ1biBvciBleHRlbnNpb24gZnVuID8pXG5cbiAgICAvLyBUT0RPIGRlZmluZSB0aGUgZXh0ZXNudGlvbiBmdW4gJ0FkdmVudHVyZXIuYXR0YWNrKGRhbWFnZTogSW50KTpTdHJpbmcnIHRoYXQgcHJpbnRzIHRoZSB2YWx1ZSBwYXNzZWQgYXMgYSBwYXJhbWV0ZXJcblxuICAgIC8vIFRPRE8gcHJpbnQgdGhlIHJlc3VsdHMgb2YgdGhpcyBuZXcgZXh0ZW5zaW9uIGZ1bmN0aW9uIHdpdGggcGFyYW1ldGVyIHZhbHVlcyA1MCwgMTAgYW5kIDIwMFxuXG4gICAgLy8gVE9ETyB3aGF0IGlzIHRoZSByZXN1bHQgb2YgcHJpbnRTdGF0dXMgb24gYSBCYXJiYXJpYW4gPyB3aGF0IGRvIHlvdSBjb25jbHVkZSA/XG4gICAgdmFsIGJhcmJhcmlhbiA9IEJhcmJhcmlhbig1MDApXG4gICAgcHJpbnRTdGF0dXMoYmFyYmFyaWFuKVxuXG4gICAgLy8gVE9ETyBjcmVhdGUgYW4gZXh0ZW5zaW9uIHByb3BlcnR5IFwibG9zdEhwXCIgdGhhdCBjb3JyZXNwb25kcyB0byBcIm1heEhwIC0gaHBcIiAuXG4gICAgLy8gIEJlY2F1c2Ugd2UgY2Fubm90IGFkZCBmaWVsZHMgdG8gYW4gZXhpc3RpbmcgY2xhc3MsIGV4dGVuc2lvbiBwcm9wZXJ0aWVzIGNhbiBiZSBkZWZpbmVkIHVzaW5nIGdldHRlcnMgYW5kIHNldHRlcnMuXG5cbiAgICAvKiAgdW5jb21tZW50IGFmdGVyIGRlZmluaW5nIHRoZSBwcm9wZXJ0eSAgXG4gICAgdmFsIGIgPSBCYXJiYXJpYW4oMzAwKVxuICAgIGIuaHAgLT0gMTAwXG4gICAgcHJpbnRsbihiLmxvc3RIcCkgLy8gMTAwXG4gICAgYi5sb3N0SHAgPSAyNTBcbiAgICBwcmludGxuKGIuaHApIC8vIDUwXG4gICAgKi9cbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},Xl={class:"custom-container details"},Wl=l("summary",null,"Please open to see the solution(s)",-1),yl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS45LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gRXh0ZW5zaW9ucyBhbGxvd3MgdG8gYWRkIGZ1bmN0aW9ucyBvciBwcm9wZXJ0aWVzIHRvIGFuIGV4aXN0aW5nIGNsYXNzIChldmVuIGNsYXNzZXMgaW1wb3J0ZWQgZnJvbSBvdGhlciBsaWJyYXJpZXMpXG4vLyBodHRwczovL2tvdGxpbmxhbmcub3JnL2RvY3MvcmVmZXJlbmNlL2V4dGVuc2lvbnMuaHRtbFxuXG5vcGVuIGNsYXNzIEFkdmVudHVyZXIodmFyIGhwOiBJbnQpe1xuICAgIHZhbCBtYXhIcCA9IGhwXG4gICAgZnVuIGF0dGFjaygpOlN0cmluZyA9IFwiSSBkZWFsdCAxMCBkYW1hZ2VcIlxufVxuLy8gRXh0ZW5zaW9uIGZ1bmN0aW9uIHN5bnRheDogZnVuIGNsYXNzTmFtZS5mdW5jdGlvbk5hbWUocHJhbXMpIDogcmV0dXJuVHlwZVxuZnVuIEFkdmVudHVyZXIucmVwb3J0U3RhdHVzKCk6U3RyaW5nIHtcbiAgICByZXR1cm4gXCJJIGhhdmUgJHtocH0gSFBcIlxufVxuXG5mdW4gQWR2ZW50dXJlci5hdHRhY2soKTpTdHJpbmcgPSBcIkkgZGVhbHQgMTAwMCBkYW1hZ2VcIlxuXG5jbGFzcyBCYXJiYXJpYW4oaHA6IEludCk6IEFkdmVudHVyZXIoaHApXG5cbmZ1biBCYXJiYXJpYW4ucmVwb3J0U3RhdHVzKCk6U3RyaW5nID0gXCJJIGFtIGEgYmFyYmFyaWFuIGFuZCBJIGhhdmUgJHtocH0gSFBcIlxuXG5mdW4gcHJpbnRTdGF0dXMoYWR2ZW50dXJlcjogQWR2ZW50dXJlcikge1xuICAgIHByaW50bG4oYWR2ZW50dXJlci5yZXBvcnRTdGF0dXMoKSlcbn1cblxuLy8gVE9ETyBjcmVhdGUgYW4gZXh0ZW5zaW9uIHByb3BlcnR5IFwibG9zdEhwXCIgdGhhdCBjb3JyZXNwb25kcyB0byBcIm1heEhwIC0gaHBcIiAuXG4vLyBFeHRlbnNpb24gcHJvcGVydGllcyBjYW5ub3Qgb25seSBiZSBkZWZpbmVkIGdsb2JhbGx5XG52YXIgQmFyYmFyaWFuLmxvc3RIcDpJbnRcbiAgICBnZXQoKSA9IG1heEhwIC0gaHBcbiAgICBzZXQgKHZhbHVlKSB7IGhwID0gbWF4SHAgLSB2YWx1ZX1cblxuZnVuIG1haW4oKXtcbiAgICAvLyBUT0RPIGNyZWF0ZSBhbiBhZHZlbnR1cmVyIHdpdGggMjAwaHAgYW5kIHByaW50IHRoZSByZXN1bHQgb2YgdGhlIHJlcG9ydFN0YXR1cyBleHRlbnNpb24gbWV0aG9kXG4gICAgdmFsIGFkdmVudHVyZXIgPSBBZHZlbnR1cmVyKDIwMClcbiAgICBwcmludChhZHZlbnR1cmVyLnJlcG9ydFN0YXR1cygpKVxuXG4gICAgLy8gVE9ETyBwcmludCB0aGUgcmVzdWx0IG9mIHRoZSBhdHRhY2sgZnVuY3Rpb24uIFdoaWNoIHZlcnNpb24gaXMgcmV0YWluZWQgKG1lbWJlciBmdW4gb3IgZXh0ZW5zaW9uIGZ1biA/KVxuICAgIHByaW50KGFkdmVudHVyZXIuYXR0YWNrKCkpIC8vIG1lbWJlciBmdW5jdGlvbiB0YWtlcyBwcmVjZWRlbmNlXG5cbiAgICAvLyBUT0RPIGRlZmluZSB0aGUgZXh0ZXNudGlvbiBmdW4gJ0FkdmVudHVyZXIuYXR0YWNrKGRhbWFnZTogSW50KTpTdHJpbmcnIHRoYXQgcHJpbnRzIHRoZSB2YWx1ZSBwYXNzZWQgYXMgYSBwYXJhbWV0ZXJcbiAgICBmdW4gQWR2ZW50dXJlci5hdHRhY2soZGFtYWdlOiBJbnQpID0gXCIkZGFtYWdlXCJcblxuICAgIC8vIFRPRE8gcHJpbnQgdGhlIHJlc3VsdHMgb2YgdGhpcyBuZXcgZXh0ZW5zaW9uIGZ1bmN0aW9uIHdpdGggcGFyYW1ldGVyIHZhbHVlcyA1MCwgMTAgYW5kIDIwMFxuXHRhZHZlbnR1cmVyLmF0dGFjayg1MClcbiAgICBhZHZlbnR1cmVyLmF0dGFjaygxMClcbiAgICBhZHZlbnR1cmVyLmF0dGFjaygyMDApXG4gICAgXG4gICAgLy8gVE9ETyB3aGF0IGlzIHRoZSByZXN1bHQgb2YgcHJpbnRTdGF0dXMgb24gYSBCYXJiYXJpYW4gPyB3aGF0IGRvIHlvdSBjb25jbHVkZSA/XG4gICAgdmFsIGJhcmJhcmlhbiA9IEJhcmJhcmlhbig1MDApXG4gICAgcHJpbnRTdGF0dXMoYmFyYmFyaWFuKVxuXG4gICAgLy8gVE9ETyBjcmVhdGUgYW4gZXh0ZW5zaW9uIHByb3BlcnR5IFwibG9zdEhwXCIgdGhhdCBjb3JyZXNwb25kcyB0byBcIm1heEhwIC0gaHBcIiAuXG4gICAgLy8gIEJlY2F1c2Ugd2UgY2Fubm90IGFkZCBmaWVsZHMgdG8gYW4gZXhpc3RpbmcgY2xhc3MsIGV4dGVuc2lvbiBwcm9wZXJ0aWVzIGNhbiBiZSBkZWZpbmVkIHVzaW5nIGdldHRlcnMgYW5kIHNldHRlcnMuXG5cdC8vIEV4dGVuc2lvbiBwcm9wZXJ0aWVzIGNhbm5vdCBvbmx5IGJlIGRlZmluZWQgZ2xvYmFsbHlcblx0XG4gICAgdmFsIGIgPSBCYXJiYXJpYW4oMzAwKVxuICAgIGIuaHAgLT0gMTAwXG4gICAgcHJpbnRsbihiLmxvc3RIcCkgLy8gMTAwXG4gICAgYi5sb3N0SHAgPSAyNTBcbiAgICBwcmludGxuKGIuaHApIC8vIDUwXG5cbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},Al=l("h4",{id:"exercise-4",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercise-4"},[l("span",null,"Exercise 4")])],-1),Vl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGphdmEudGltZS5Mb2NhbERhdGVcbmltcG9ydCBqYXZhLnRpbWUuUGVyaW9kXG5cbi8vIC8vIFE6IGNyZWF0ZSBhbiBlbnVtZXJhdGlvbiBjbGFzcyBjYWxsZWQgXCJDYXRDb2xvclwiIHRoYXQgaGFzIHRoZXNlIHZhbHVlczogV0hJVEUsIENSRUFNLCBCTEFDSywgUkVELGFuZCBCUk9XTlxuXG4vLyBROiBjcmVhdGUgYSBkYXRhY2xhc3MgXCJDYXRcIiB0aGF0IGhhcyAzIGltbXV0YWJsZSBwcm9wZXJ0aWVzOiBuYW1lOiBTdHJpbmcsIGJpcnRoRGF0ZTogTG9jYWxEYXRlIGFuZCBjb2xvcjogQ2F0Q29sb3JcblxuZnVuIG1haW4oKXtcbiAgICAvLyBROiBhZGQgYSBjYXQgd2l0aCBjcmVhbSBjb2xvciBhbmQgYW5vdGhlciBvbmUgd2l0aCBibGFjayBjb2xvclxuICAgIHZhbCBjYXRzID0gbGlzdE9mKFxuICAgICAgQ2F0KFwiQmVsbGFcIiwgTG9jYWxEYXRlLnBhcnNlKFwiMjAyMC0xMC0xMlwiKSwgQ2F0Q29sb3IuV0hJVEUpLFxuICAgICAgQ2F0KFwiU2ltYmFcIiwgTG9jYWxEYXRlLnBhcnNlKFwiMjAxMC0wMS0zMVwiKSwgQ2F0Q29sb3IuQlJPV04pLFxuICAgICAgQ2F0KFwiS2l0dHlcIiwgTG9jYWxEYXRlLnBhcnNlKFwiMjAxNy0wNS0wNlwiKSwgQ2F0Q29sb3IuV0hJVEUpXG4gICAgKVxuICAgIFxuICAgIC8vIFBsZWFzZSB1c2UgYSBkZWNsYXJhdGl2ZSBzdHlsZSBpbiB0aGlzIGV4ZXJjaXNlXG5cbiAgICAvLyBROiBwcmludCB0aGUgbmFtZXMgb2Ygd2hpdGUgY2F0c1xuXG4gICAgLy8gUTogcHJpbnQgdGhlIG5hbWUgYW5kIGFnZSAoYW5kIGp1c3QgdGhlIG5hbWUgYW5kIGFnZSkgb2YgY2F0cyB0aGF0IGFyZSBhdCBsZWFzdCA1IHllYXJzIG9sZC5cbiAgICAvLyBIaW50OiBpbXBvcnQgamF2YS50aW1lLlBlcmlvZCBhbmQgdXNlIFBhaXIgY2xhc3NcbiAgICAgICBcbiAgICBcbiAgICAvLyBMZXQncyBzdXBwb3NlIHRoYXQgd2UgaGF2ZSBhbiBhcnJheSBvZiBvd25lcnMgd2hlcmUgZWFjaCBlbGVtZW50IGlzIHRoZSBvd25lciBvZiB0aGUgY29ycmVzcG9uZGluZyBpbmRleCBpbiB0aGUgY2F0cyB0YWJsZS4gRm9yIGV4YW1wbGUgQW5kYW5vciBpcyB0aGUgb3duZXIgb2YgS2l0dHlcbiAgICB2YWwgb3duZXJzID0gbGlzdE9mKFwiSXJ5ZW5cIiwgXCJJcnllblwiLCBcIkFuZGFub3JcIiwgXCJBbmRhbm9yXCIsIFwiR2FuZGFsZlwiKVxuICAgIFxuICAgIC8vIFE6IHVzaW5nIHRoZSB6aXAgbWV0aG9kLCBwcmludCB0aGUgbmFtZSBvZiBjYXRzIG93bmVkIGJ5IFwiSXJ5ZW5cIlxufSJ9",target:"_blank",rel:"noopener noreferrer"},Yl={class:"custom-container details"},Jl=l("summary",null,"Please open to see the solution(s)",-1),Rl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGphdmEudGltZS5Mb2NhbERhdGVcbmltcG9ydCBqYXZhLnRpbWUuUGVyaW9kXG5cbi8vIC8vIFE6IGNyZWF0ZSBhbiBlbnVtZXJhdGlvbiBjbGFzcyBjYWxsZWQgXCJDYXRDb2xvclwiIHRoYXQgaGFzIHRoZXNlIHZhbHVlczogV0hJVEUsIENSRUFNLCBCTEFDSywgUkVELGFuZCBCUk9XTlxuZW51bSBjbGFzcyBDYXRDb2xvciB7XG4gICAgV0hJVEUsIENSRUFNLCBCTEFDSywgUkVELCBCUk9XTlxufVxuXG4vLyBROiBjcmVhdGUgYSBkYXRhY2xhc3MgXCJDYXRcIiB0aGF0IGhhcyAzIGltbXV0YWJsZSBwcm9wZXJ0aWVzOiBuYW1lOiBTdHJpbmcsIGJpcnRoRGF0ZTogTG9jYWxEYXRlIGFuZCBjb2xvcjogQ2F0Q29sb3Jcbi8vIEhpbnQ6IGltcG9ydCBqYXZhLnRpbWUuTG9jYWxEYXRlXG5kYXRhIGNsYXNzIENhdCh2YWwgbmFtZTogU3RyaW5nLCB2YWwgYmlydGhEYXRlOiBMb2NhbERhdGUsIHZhbCBjb2xvcjogQ2F0Q29sb3IpXG5cbmZ1biBtYWluKCl7XG4gICAgLy8gUTogYWRkIGEgY2F0IHdpdGggY3JlYW0gY29sb3IgYW5kIGFub3RoZXIgb25lIHdpdGggYmxhY2sgY29sb3JcbiAgICB2YWwgY2F0cyA9IGxpc3RPZihcbiAgICAgIENhdChcIkJlbGxhXCIsIExvY2FsRGF0ZS5wYXJzZShcIjIwMjAtMTAtMTJcIiksIENhdENvbG9yLldISVRFKSxcbiAgICAgIENhdChcIlNpbWJhXCIsIExvY2FsRGF0ZS5wYXJzZShcIjIwMTAtMDEtMzFcIiksIENhdENvbG9yLkJST1dOKSxcbiAgICAgIENhdChcIktpdHR5XCIsIExvY2FsRGF0ZS5wYXJzZShcIjIwMTctMDUtMDZcIiksIENhdENvbG9yLldISVRFKSxcbiAgICAgIENhdChcIk1pYW91XCIsIExvY2FsRGF0ZS5wYXJzZShcIjIwMTEtMDItMjVcIiksIENhdENvbG9yLkNSRUFNKSxcbiAgICAgIENhdChcIk55YW5cIiwgTG9jYWxEYXRlLnBhcnNlKFwiMjAxNC0wNy0xMVwiKSwgQ2F0Q29sb3IuQkxBQ0spXG4gICAgKVxuICAgIFxuICAgIC8vIFBsZWFzZSB1c2UgYSBkZWNsYXJhdGl2ZSBzdHlsZSBpbiB0aGlzIGV4ZXJjaXNlXG5cbiAgICAvLyBROiBwcmludCB0aGUgbmFtZXMgb2Ygd2hpdGUgY2F0c1xuICAgIHZhbCB3aGl0ZUNhdE5hbWVzID0gY2F0cy5maWx0ZXIgeyBpdC5jb2xvciA9PSBDYXRDb2xvci5XSElURSB9Lm1hcCB7IGl0Lm5hbWUgfVxuICAgIHByaW50bG4oXCJXaGl0ZSBjYXRzOiAke3doaXRlQ2F0TmFtZXMuam9pblRvU3RyaW5nKFwiLCBcIil9XCIpXG5cbiAgICAvLyBROiBwcmludCB0aGUgbmFtZSBhbmQgYWdlIChhbmQganVzdCB0aGUgbmFtZSBhbmQgYWdlKSBvZiBjYXRzIHRoYXQgYXJlIGF0IGxlYXN0IDUgeWVhcnMgb2xkLlxuICAgIC8vIEhpbnQ6IGltcG9ydCBqYXZhLnRpbWUuUGVyaW9kIGFuZCB1c2UgUGFpciBjbGFzc1xuICAgIHZhbCBNb3JlVGhhbjV5b0NhdHMgPSBjYXRzLmZpbHRlciB7IFBlcmlvZC5iZXR3ZWVuKGl0LmJpcnRoRGF0ZSwgTG9jYWxEYXRlLm5vdygpKS5nZXRZZWFycygpID49IDUgfVxuICAgIFx0Lm1hcCB7IFBhaXIoaXQubmFtZSwgUGVyaW9kLmJldHdlZW4oaXQuYmlydGhEYXRlLCBMb2NhbERhdGUubm93KCkpLmdldFllYXJzKCkpIH1cbiAgICAgICAgLm1hcCB7IFwiKG5hbWU6ICR7aXQuZmlyc3R9IC0gYWdlOiAke2l0LnNlY29uZH0pXCIgfVxuICAgIHByaW50bG4oXCJXaGl0ZSBjYXRzOiAke01vcmVUaGFuNXlvQ2F0cy5qb2luVG9TdHJpbmcoXCIsIFwiKX1cIilcbiAgICAgICAgXG4gICAgXG4gICAgLy8gTGV0J3Mgc3VwcG9zZSB0aGF0IHdlIGhhdmUgYW4gYXJyYXkgb2Ygb3duZXJzIHdoZXJlIGVhY2ggZWxlbWVudCBpcyB0aGUgb3duZXIgb2YgdGhlIGNvcnJlc3BvbmRpbmcgaW5kZXggaW4gdGhlIGNhdHMgdGFibGUuIEZvciBleGFtcGxlIEFuZGFub3IgaXMgdGhlIG93bmVyIG9mIEtpdHR5XG4gICAgdmFsIG93bmVycyA9IGxpc3RPZihcIklyeWVuXCIsIFwiSXJ5ZW5cIiwgXCJBbmRhbm9yXCIsIFwiQW5kYW5vclwiLCBcIkdhbmRhbGZcIilcbiAgICBcbiAgICAvLyBROiB1c2luZyB0aGUgemlwIG1ldGhvZCwgcHJpbnQgdGhlIG5hbWUgb2YgY2F0cyBvd25lZCBieSBcIklyeWVuXCJcblx0dmFsIGlyeWVuQ2F0cyA9IChvd25lcnMgemlwIGNhdHMpLmZpbHRlciB7IGl0LmZpcnN0ID09IFwiSXJ5ZW5cIn0ubWFwIHsgaXQuc2Vjb25kLm5hbWUgfVxuICAgIHByaW50bG4oXCJJcnllbidzIGNhdHMgYXJlOiAke2lyeWVuQ2F0cy5qb2luVG9TdHJpbmcoXCIsIFwiKX1cIilcbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},vl=l("h4",{id:"exercise-5",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercise-5"},[l("span",null,"Exercise 5")])],-1),xl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS45LjIwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoib3BlbiBjbGFzcyBBZHZlbnR1cmVyKHZhciBocDogSW50KXtcbiAgICB2YWwgbWF4SHAgPSBocFxuICAgIGZ1biBhdHRhY2soKTpTdHJpbmcgPSBcIkkgZGVhbHQgMTAgZGFtYWdlXCJcbn1cblxuLy8gLy8gLy8gVGhlICd0aGlzJyBrZXl3b3JkIGluc2lkZSBhbiBleHRlbnNpb24gY29ycmVzcG9uZHMgdG8gdGhlIHJlY2VpdmVyIG9iamVjdCAodGhlIG9iamVjdCB0aGF0IGNhbGxzIHRoZSBleHRlbnNpb24pXG4vLyBUT0RPIGNlZWF0ZSBhbiBleHRlbnNpb24gZnVuY3Rpb24gJ21ha2VRdWVzdGlvbicgb2YgdGhlIFN0cmluZyBjbGFzcyB0aGF0IHJldHVybiBhIG5ldyBTdHJpbmcgdGhhdCBjb250YWlucyB0aGUgb3JpZ2luYWwgU3RyaW5nICsgJz8nIGF0IHRoZSBlbmRcblxuLy8gcHJpbnRsbihcIkhvdyBkbyB5b3UgZG9cIi5tYWtlUXVlc3Rpb24oKSkgLy8gc2hvdWxkIHByaW50IFwiSG93IGRvIHlvdSBkbyA/XCJcblxuY2xhc3MgTWFnZShocDogSW50KTogQWR2ZW50dXJlcihocCl7XG4gICAgY29tcGFuaW9uIG9iamVjdCB7XG4gICAgICAgIHZhciBjb3VudCA9IDBcbiAgICB9XG4gICAgaW5pdCB7XG4gICAgICAgIE1hZ2UuY291bnQgKz0gMVxuICAgIH1cbn1cbi8vIFRPRE8gZGVmaW5lIGFuIGV4dGVuc2lvbiBmdW5jdGlvbiBvbiB0aGUgY29tcGFuaW9uIG9iamVjdCB0aGF0IHByaW50cyB0aGUgbnVtYmVyIG9mIGNyZWF0ZWQgbWFnZXMuXG4vLyBoaW50OiBcInRoaXNcIiBhbGxvd3MgdG8gcmVmZXJlbmNlIHRoZSBjb21wYW5pb24gb2JqZWN0XG5cbnZhbCBtYWdlID0gTWFnZSgxMDApXG52YWwgbWFnZTIgPSBNYWdlKDIwMClcbnByaW50KFwiTWFnZSBjb3VudDogXCIpXG4vLyBNYWdlLnByaW50Q291bnQoKSAvLyAyXG5cbi8vIFRPRE8gY3JhdGUgYW4gZXh0ZW5zaW9uIGZ1bmN0aW9uIFwiZXZlbkNvdW50KCk6SW50XCIgb24gTGlzdDxJbnQ+IHRoYXQgcmV0dXJuIHRoZSBudW1iZXIgb2YgZXZlbiB2YWx1ZXNcblxuLy9wcmludGxuKGxpc3RPZigxLCAyLCAzLCA0LCA1KS5ldmVuQ291bnQoKSkgLy8gcHJpbnRzIDJcblxuLy8gVE9ETyBjcmVhdGUgYSBnZW5lcmljIGV4dGVuc2lvbiBmdW5jdGlvbiBcImNvbmNhdGVuYXRlKCk6U3RyaW5nXCIgb24gTGlzdDxUPiB0aGF0IGNvbmNhdGVuYXRlcyB0aGUgdG9TdHJpbmcgdmFsdWUgb2YgYWxsIG9mIHRoZSBlbGVtZW50cyBvZiB0aGUgbGlzdFxuXG4vLyBwcmludGxuKGxpc3RPZihcIkhlbGxvIFwiLCBcIldvcmxkXCIsIFwiTGluZVwiKS5jb25jYXRlbmF0ZSgpKSAvLyBzaG91bGQgcHJpbnQgXCJIZWxsbyBXb3JsZExpbmVcIlxuLy8gcHJpbnRsbihsaXN0T2YoMTAwLCAyMDAsIDMwMCkuY29uY2F0ZW5hdGUoKSkgLy8gc2hvdWxkIHByaW50IFwiMTAwMjAwMzAwXCJcblxuLy8gVE9ETyBjcmVhdGUgYSBnZW5lcmljIGV4dGVuc2lvbiBmdW5jdGlvbiBcIkxpc3Q/Lm1ha2VOb3ROdWxsKCk6TGlzdCcgIHRoYXQgcmV0dXJucyBhbiBlbXB0eSBsaXN0IGlmIHRoZSByZWNlaXZlciBpcyBudWxsLCBvciB0aGUgb3JpZ2luYWwgbGlzdCBvcmhlcndpc2VcblxuLy8gcHJpbnRsbihsaXN0T2YoMSwgMiwgMykubWFrZU5vdE51bGwoKSkgLy8gc2hvdWxkIHByaW50IFsxLCAyLCAzXVxuLy8gdmFsIGw6IExpc3Q8SW50Pj8gPSBudWxsXG4vLyBwcmludGxuKGwubWFrZU5vdE51bGwoKSkgLy8gc2hvdWxkIHByaW50IFtdIn0=",target:"_blank",rel:"noopener noreferrer"},Fl={class:"custom-container details"},Hl=l("summary",null,"Please open to see the solution(s)",-1),wl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS45LjIwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoib3BlbiBjbGFzcyBBZHZlbnR1cmVyKHZhciBocDogSW50KXtcbiAgICB2YWwgbWF4SHAgPSBocFxuICAgIGZ1biBhdHRhY2soKTpTdHJpbmcgPSBcIkkgZGVhbHQgMTAgZGFtYWdlXCJcbn1cbi8vIFRoZSAndGhpcycga2V5d29yZCBpbnNpZGUgYW4gZXh0ZW5zaW9uIGNvcnJlc3BvbmRzIHRvIHRoZSByZWNlaXZlciBvYmplY3QgKHRoZSBvYmplY3QgdGhhdCBjYWxscyB0aGUgZXh0ZW5zaW9uKVxuLy8gVE9ETyBjZWVhdGUgYW4gZXh0ZW5zaW9uIGZ1bmN0aW9uICdtYWtlUXVlc3Rpb24nIG9mIHRoZSBTdHJpbmcgY2xhc3MgdGhhdCByZXR1cm4gYSBuZXcgU3RyaW5nIHRoYXQgY29udGFpbnMgdGhlIG9yaWdpbmFsIFN0cmluZyArICc/JyBhdCB0aGUgZW5kXG5mdW4gU3RyaW5nLm1ha2VRdWVzdGlvbigpID0gXCIke3RoaXN9ID9cIlxuXG4vLyBUT0RPIGRlZmluZSBhbiBleHRlbnNpb24gZnVuY3Rpb24gb24gdGhlIGNvbXBhbmlvbiBvYmplY3QgdGhhdCBwcmludHMgdGhlIG51bWJlciBvZiBjcmVhdGVkIG1hZ2VzLlxuY2xhc3MgTWFnZShocDogSW50KTogQWR2ZW50dXJlcihocCl7XG4gICAgY29tcGFuaW9uIG9iamVjdCB7XG4gICAgICAgIHZhciBjb3VudCA9IDBcbiAgICB9XG4gICAgaW5pdCB7XG4gICAgICAgIE1hZ2UuY291bnQgKz0gMVxuICAgIH1cbn1cbi8vIGhpbnQ6IFwidGhpc1wiIGFsbG93cyB0byByZWZlcmVuY2UgdGhlIGNvbXBhbmlvbiBvYmplY3RcbmZ1biBNYWdlLkNvbXBhbmlvbi5wcmludENvdW50KCkgPSBwcmludGxuKHRoaXMuY291bnQpXG5cblxuZnVuIG1haW4oKXtcbiAgICAvLyBwcmludGxuKFwiSG93IGRvIHlvdSBkb1wiLm1ha2VRdWVzdGlvbigpKSBzaG91bGQgcHJpbnQgXCJIb3cgZG8geW91IGRvID9cIlxuICAgIHByaW50bG4oXCJIb3cgZG8geW91IGRvXCIubWFrZVF1ZXN0aW9uKCkpXG4gICAgXG4gICAgdmFsIG1hZ2UgPSBNYWdlKDEwMClcbiAgICB2YWwgbWFnZTIgPSBNYWdlKDIwMClcbiAgICBwcmludChcIk1hZ2UgY291bnQ6IFwiKVxuICAgIE1hZ2UucHJpbnRDb3VudCgpIC8vIDJcblxuICAgIC8vIFRPRE8gY3JhdGUgYW4gZXh0ZW5zaW9uIGZ1bmN0aW9uIFwiZXZlbkNvdW50KCk6SW50XCIgb24gTGlzdDxJbnQ+IHRoYXQgcmV0dXJucyB0aGUgbnVtYmVyIG9mIGV2ZW4gdmFsdWVzXG5cbiAgICAvL3ByaW50bG4obGlzdE9mKDEsIDIsIDMsIDQsIDUpLmV2ZW5Db3VudCgpKSAvLyBwcmludHMgMlxuXG4gICAgLy8gVE9ETyBjcmVhdGUgYSBnZW5lcmljIGV4dGVuc2lvbiBmdW5jdGlvbiBcImNvbmNhdGVuYXRlKCk6U3RyaW5nXCIgb24gTGlzdDxUPiB0aGF0IGNvbmNhdGVuYXRlcyB0aGUgdG9TdHJpbmcgdmFsdWUgb2YgYWxsIG9mIHRoZSBlbGVtZW50cyBvZiB0aGUgbGlzdFxuICAgIGZ1biA8VD4gTGlzdDxUPi5jb25jYXRlbmF0ZSgpOiBTdHJpbmcgPSB0aGlzLm1hcCB7IHYgLT4gdi50b1N0cmluZygpIH0ucmVkdWNlIHsgYWNjLCBzIC0+IGFjYyArIHMgfVxuICAgIHByaW50bG4obGlzdE9mKFwiSGVsbG8gXCIsIFwiV29ybGRcIiwgXCJMaW5lXCIpLmNvbmNhdGVuYXRlKCkpIC8vIHNob3VsZCBwcmludCBcIkhlbGxvIFdvcmxkTGluZVwiXG4gICAgcHJpbnRsbihsaXN0T2YoMTAwLCAyMDAsIDMwMCkuY29uY2F0ZW5hdGUoKSkgLy8gc2hvdWxkIHByaW50IFwiMTAwMjAwMzAwXCJcblxuICAgIC8vIFRPRE8gY3JlYXRlIGEgZ2VuZXJpYyBleHRlbnNpb24gZnVuY3Rpb24gXCJMaXN0Py5tYWtlTm90TnVsbCgpOkxpc3QnICB0aGF0IHJldHVybiBhbiBlbXB0eSBsaXN0IGlmIHRoZSByZWNlaXZlciBpcyBudWxsLCBvciB0aGUgb3JpZ2luYWwgbGlzdCBvcmhlcndpc2VcbiAgICBmdW4gPFQ+IExpc3Q8VD4/Lm1ha2VOb3ROdWxsKCk6IExpc3Q8VD57XG4gICAgICAgIHJldHVybiBsaXN0T2Y8VD4oKVxuICAgIH1cbiAgICBwcmludGxuKGxpc3RPZigxLCAyLCAzKS5tYWtlTm90TnVsbCgpKSAvLyBzaG91bGQgcHJpbnQgWzEsIDIsIDNdXG4gICAgdmFsIGw6IExpc3Q8SW50Pj8gPSBudWxsXG4gICAgcHJpbnRsbihsLm1ha2VOb3ROdWxsKCkpIC8vIHNob3VsZCBwcmludCBbXVxufSJ9",target:"_blank",rel:"noopener noreferrer"},Nl=l("h4",{id:"exercise-6",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercise-6"},[l("span",null,"Exercise 6")])],-1),Sl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS45LjIwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoib3BlbiBjbGFzcyBEcmluayh2YXIgdm9sdW1lOiBJbnQpe1xuICAgIG9wZW4gZnVuIGNvbnN1bWUoY29uc3VtZWRWb2x1bWU6IEludCl7XG4gICAgICAgIHRoaXMudm9sdW1lIC09IGNvbnN1bWVkVm9sdW1lXG4gICAgfVxufVxuLyogVE9ETyB1c2luZyB0aGUgb2JqZWN0IGRlY2xhcmF0aW9uIHN5bmN0YXgsIGNyZWF0ZSBhIHNpbmdsZXRvbiBEcmlua0Rpc3BlbnNlciB0aGF0IGhhcyB0aGVzZSBtZXRob2RzOiBwdXREcmluayhEcmluayksXG4gIExpc3Q8RHJpbms+IGFsbEF2YWlsYWJsZURyaW5rcygpIGFuZCBwaWNrTGFzdERyaW5rKCk6RHJpbmsgKi9cbiAgXG4vLyAgU3ludGF4OiBvYmplY3QgU2luZ2xldG9uTmFtZSB7IC4uLiB9IH1cblxuLy8gVE9ETyB1bmNvbW1lbnQgdG8gdGVzdCB5b3UgY29kZVxuLy9Ecmlua0Rpc3BlbnNlci5wdXREcmluayhEcmluaygxMDApKVxuLy9Ecmlua0Rpc3BlbnNlci5wdXREcmluayhEcmluayg1MCkpXG4vL0RyaW5rRGlzcGVuc2VyLnB1dERyaW5rKERyaW5rKDcwKSlcbi8vcHJpbnRsbihEcmlua0Rpc3BlbnNlci5hbGxBdmFpbGFibGVEcmlua3MoKS5zaXplKSAvLyAzXG4vL0RyaW5rRGlzcGVuc2VyLnB1dERyaW5rKHBpY2tMYXN0RHJpbmsoKSlcbi8vcHJpbnRsbihEcmlua0Rpc3BlbnNlci5hbGxBdmFpbGFibGVEcmlua3MoKS5zaXplKSAvLyAyXG5cbi8vIFRPRE8gY3JlYXRlIGEgY2xhc3MgbmFtZWQgRHJpbmtNYWtlci4gV2Ugd2FudCB0byBoYXZlIGEgc3RhdGljIG1ldGhvZCBcImNyZWF0ZURyaW5rKCk6IE9iamVjdEV4cHJlc3Npb25zMDEuRHJpbmtcIiB0aGF0IGNyZWF0ZXMgYSBuZXcgRHJpbmsuXG4vLyAgIEFuIG9iamVjdCBkZWNsYXJhdGlvbiBpbnNpZGUgYSBjbGFzcyBjYW4gYmUgbWFya2VkIGFzIGNvbXBhbmlvblxuLy8gICBTeW50YXg6XG4vKlxuY2xhc3MgQ2xhc3NOYW1lIHtcbiAgICBjb21wYW5pb24gb2JqZWN0IChvcHRpb25uYWwgbmFtZSkge1xuICAgICAgICAuLi5cbiAgICB9XG59XG4qL1xuXG5cbi8vIFRPRE8gdW5jb21tZW50IHRvIHRlc3Rcbi8vdmFsIGRyaW5rID0gRHJpbmtNYWtlci5jcmVhdGVEcmluaygpXG4vL3ZhbCBkcmluazIgPSBEcmlua01ha2VyLkNvbXBhbmlvbi5jcmVhdGVEcmluaygpXG5cbi8vIFRPRE8gd2hhdCBpcyB0aGUgdHlwZSBvZiBjb21wYW5pb25PYmplY3Rcbi8vdmFsIGNvbXBhbmlvbk9iamVjdCA9IERyaW5rTWFrZXJcbi8vY29tcGFuaW9uT2JqZWN0LmNyZWF0ZURyaW5rKClcblxuLy8gVE9ETyBtYWtlIHRoZSBlYXJsaWVyIGNvbXBhbmlvbiBvYmplY3QgaW1wbGVtZW50IHRoZSBmb2xsb3dpbmcgQ291bnRlciBpbnRlcmZhY2UgYW5kIGNvdW50IHRoZSBudW1iZXIgb2YgY3JlYXRlZCBEcmlua3NcbmludGVyZmFjZSBDb3VudGVye1xuICAgIHZhciBjb3VudDogSW50XG59XG4vLyBUT0RPIHVuY29tbWVudCB0byB0ZXN0XG4vL3ByaW50bG4oRHJpbmtNYWtlci5jb3VudCkgLy8gMyJ9",target:"_blank",rel:"noopener noreferrer"},kl={class:"custom-container details"},El=l("summary",null,"Please open to see the solution(s)",-1),Ll={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gVE9ETyB1c2luZyB0aGUgb2JqZWN0IGRlY2xhcmF0aW9uIHN5bmN0YXgsIGNyZWF0ZSBhIHNpbmdsZXRvbiBEcmlua0Rpc3BlbnNlciB0aGF0IGhhcyB0aGVzZSBtZXRob2RzOiBwdXREcmluayhEcmluayksIExpc3Q8RHJpbms+IGFsbEF2YWlsYWJsZURyaW5rcygpIGFuZCBwaWNrTGFzdERyaW5rKCk6RHJpbmtcbi8vICBTeW50YXg6IG9iamVjdCBTaW5nbGV0b25OYW1lIHsgLi4uIH0gfVxuXG4vLyBUT0RPIHVuY29tbWVudCB0byB0ZXN0IHlvdSBjb2RlXG4vL0RyaW5rRGlzcGVuc2VyLnB1dERyaW5rKE9iamVjdEV4cHJlc3Npb25zMDEuRHJpbmsoMTAwKSlcbi8vRHJpbmtEaXNwZW5zZXIucHV0RHJpbmsoT2JqZWN0RXhwcmVzc2lvbnMwMS5Ecmluayg1MCkpXG4vL0RyaW5rRGlzcGVuc2VyLnB1dERyaW5rKE9iamVjdEV4cHJlc3Npb25zMDEuRHJpbmsoNzApKVxuLy9wcmludGxuKERyaW5rRGlzcGVuc2VyLmFsbEF2YWlsYWJsZURyaW5rcygpLnNpemUpIC8vIDNcbi8vRHJpbmtEaXNwZW5zZXIucHV0RHJpbmsoT2JqZWN0RXhwcmVzc2lvbnMwMS5waWNrTGFzdERyaW5rKCkpXG4vL3ByaW50bG4oRHJpbmtEaXNwZW5zZXIuYWxsQXZhaWxhYmxlRHJpbmtzKCkuc2l6ZSkgLy8gMlxuXG4vLyBUT0RPIGNyZWF0ZSBhIGNsYXNzIG5hbWVkIERyaW5rTWFrZXIuIFdlIHdhbnQgdG8gaGF2ZSBhIHN0YXRpYyBtZXRob2QgXCJjcmVhdGVEcmluaygpOiBPYmplY3RFeHByZXNzaW9uczAxLkRyaW5rXCIgdGhhdCBjcmVhdGVzIGEgbmV3IERyaW5rLlxuLy8gICBBbiBvYmplY3QgZGVjbGFyYXRpb24gaW5zaWRlIGEgY2xhc3MgY2FuIGJlIG1hcmtlZCBhcyBjb21wYW5pb25cbi8vICAgU3ludGF4OlxuLypcbmNsYXNzIENsYXNzTmFtZSB7XG4gICAgY29tcGFuaW9uIG9iamVjdCAob3B0aW9ubmFsIG5hbWUpIHtcbiAgICAgICAgLi4uXG4gICAgfVxufVxuKi9cbmNsYXNzIERyaW5rTWFrZXIge1xuICAgIGNvbXBhbmlvbiBvYmplY3Q6IENvdW50ZXJ7XG4gICAgICAgIHByaXZhdGUgdmFyIG5iQ3JlYXRlZCA9IDBcbiAgICAgICAgb3ZlcnJpZGUgdmFyIGNvdW50OiBJbnRcbiAgICAgICAgICAgIGdldCgpID0gbmJDcmVhdGVkXG4gICAgICAgICAgICBzZXQodmFsdWUpIHsgbmJDcmVhdGVkID0gdmFsdWV9XG5cbiAgICAgICAgZnVuIGNyZWF0ZURyaW5rKCkgOiBPYmplY3RFeHByZXNzaW9uczAxLkRyaW5re1xuICAgICAgICAgICAgY291bnQgKz0gMVxuICAgICAgICAgICAgcmV0dXJuIE9iamVjdEV4cHJlc3Npb25zMDEuRHJpbmsoMTAwKVxuICAgICAgICB9XG4gICAgfVxufVxuXG4vLyBUT0RPIHVuY29tbWVudCB0byB0ZXN0XG4vL3ZhbCBkcmluayA9IERyaW5rTWFrZXIuY3JlYXRlRHJpbmsoKVxuLy92YWwgZHJpbmsyID0gRHJpbmtNYWtlci5Db21wYW5pb24uY3JlYXRlRHJpbmsoKVxuXG4vLyBUT0RPIHdoYXQgaXMgdGhlIHR5cGUgb2YgY29tcGFuaW9uT2JqZWN0XG4vL3ZhbCBjb21wYW5pb25PYmplY3QgPSBEcmlua01ha2VyXG4vL2NvbXBhbmlvbk9iamVjdC5jcmVhdGVEcmluaygpXG5cbi8vIFRPRE8gbWFrZSB0aGUgZWFybGllciBjb21wYW5pb24gb2JqZWN0IGltcGxlbWVudCB0aGUgZm9sbG93aW5nIENvdW50ZXIgaW50ZXJmYWNlIGFuZCBjb3VudCB0aGUgbnVtYmVyIG9mIGNyZWF0ZWQgRHJpbmtzXG5pbnRlcmZhY2UgQ291bnRlcntcbiAgICB2YXIgY291bnQ6IEludFxufVxuLy8gVE9ETyB1bmNvbW1lbnQgdG8gdGVzdFxuLy9wcmludGxuKERyaW5rTWFrZXIuY291bnQpIC8vIDMifQ==",target:"_blank",rel:"noopener noreferrer"},zl=l("h4",{id:"exercise-7",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercise-7"},[l("span",null,"Exercise 7")])],-1),fl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoicGFja2FnZSBjb20ud29ybGRsaW5lLmxlYXJuaW5nLmtvdGxpbi5vYmplY3RzQW5kQ29tcGFuaW9uc1xuXG4vLyBPYmplY3RzIGluIGtvdGxpbiBodHRwczovL2tvdGxpbmxhbmcub3JnL2RvY3MvcmVmZXJlbmNlL29iamVjdC1kZWNsYXJhdGlvbnMuaHRtbFxub3BlbiBjbGFzcyBEcmluayh2YXIgdm9sdW1lOiBJbnQpe1xuICAgIG9wZW4gZnVuIGNvbnN1bWUoY29uc3VtZWRWb2x1bWU6IEludCl7XG4gICAgICAgIHRoaXMudm9sdW1lIC09IGNvbnN1bWVkVm9sdW1lXG4gICAgfVxufVxuaW50ZXJmYWNlIENvbG9yZWR7XG4gICAgdmFsIGNvbG9yOlN0cmluZ1xufVxuXG4vLyBhbm9ueW1vdXMgb2JqZWN0cyBoYXZlIGFjY2VzcyB0byB2YXJpYWJsZXMgaW4gdGhlIGVuY2xvc2luZyBzY29wZVxuZnVuIGNyZWF0ZURyaW5rQW5kQ291bnQoKTogRHJpbmt7XG4gICAgdmFyIG5iQ29uc3VtcHRpb24gPSAwXG4gICAgcmV0dXJuIG9iamVjdCA6IERyaW5rKDEwMCl7XG4gICAgICAgIG92ZXJyaWRlIGZ1biBjb25zdW1lKGNvbnN1bWVkVm9sdW1lOiBJbnQpIHtcbiAgICAgICAgICAgIHN1cGVyLmNvbnN1bWUoY29uc3VtZWRWb2x1bWUpXG4gICAgICAgICAgICBuYkNvbnN1bXB0aW9uICs9IDFcbiAgICAgICAgICAgIHByaW50bG4obmJDb25zdW1wdGlvbilcbiAgICAgICAgfVxuICAgIH1cbn1cblxuZnVuIG1haW4oKSB7XG4gICAgLy8gQ3JlYXRpbmcgYW4gaW5zdGFuY2Ugb2YgRHJpbmtcbiAgICB2YWwgc21hbGxCb3R0bGUgPSBEcmluaygxMDApXG4gICAgLy8gVE9ETyBjcmVhdGUgYSBiaWdCb3R0bGUgd2l0aCBhIHZvbHVtZSBvZiA1MDBcblxuICAgIC8vIEtvdGxpbiBhbGxvd3MgdG8gcXVpY2tseSBjcmVhdGUgYW4gYW5vbnltb3VzIG9iamVjdCB0aGF0IGltcGxlbWVudHMgYW5kIGV4aXN0aW5nIGNsYXNzIGFuZC9vciBpbnRlcmZhY2VzXG4gICAgdmFsIHNtYWxsQ2FyYm9uYXRlZERyaW5rQm90dGxlID0gb2JqZWN0OiBEcmluaygxMDApIHtcbiAgICAgICAgb3ZlcnJpZGUgZnVuIGNvbnN1bWUoY29uc3VtZWRWb2x1bWU6IEludCkge1xuICAgICAgICAgICAgc3VwZXIuY29uc3VtZShjb25zdW1lZFZvbHVtZSlcbiAgICAgICAgICAgIHByaW50bG4oXCJQU0hISEhISFwiKVxuICAgICAgICB9XG4gICAgfVxuICAgIHNtYWxsQ2FyYm9uYXRlZERyaW5rQm90dGxlLmNvbnN1bWUoMjApXG5cbiAgICAvLyBUT0RPIGNyZWF0ZWQgYW4gYW5vbnltb3VzIG9iamVjdCBzbWFsbENhcmJvbmF0ZWRSZWREcmlua0JvdHRsZSB0aGF0IGlzIGJvdGggYSBEcmluayBhbmQgQ29sb3JlZFxuICAgIC8vIFRPRE8gQ2FuIGtvdGxpbiBpbmZlciB0aGUgdHlwZSBvZiBzbWFsbENhcmJvbmF0ZWRSZWREcmlua0JvdHRsZSA/XG5cbiAgICAvLyBUT0RPIG1ha2UgdGhpcyBjb2RlIHByaW50IFwiUmVkXCJcbiAgICAvLyBwcmludGxuKHNtYWxsQ2FyYm9uYXRlZFJlZERyaW5rQm90dGxlLmNvbG9yKVxuICAgIFxuICAgIFxuICAgIC8vIFRPRE8gd2hhdCBpcyBwcmludGVkIGFmdGVyIGNhbGxpbmcgdGhlIGZvbGxvd2luZyBjb2RlID9cbiAgICB2YWwgZHJpbmtUaGF0Q291bnRzID0gY3JlYXRlRHJpbmtBbmRDb3VudCgpXG4gICAgZHJpbmtUaGF0Q291bnRzLmNvbnN1bWUoMTApXG4gICAgZHJpbmtUaGF0Q291bnRzLmNvbnN1bWUoMjApXG5cbiAgICAvLyBUT0RPIFRyeSB0byBndWVzcyB3aGF0IGlzIHByaW50ZWQgaWYgd2UgZ2V0IGFub3RoZXIgZHJpbmsgZnJvbSAnY3JlYXRlRHJpbmtBbmRDb3VudCgpJyBhbmQgY2FsbCBjb25zdW1lID9cblxuICAgIFxuICAgIC8vIEtvdGxpbiBhbHNvIGFsbG93cyB0byBjcmVhdGUgYW5vbnltb3VzIG9iamVjdHMgd2l0aG91dCBzdXBlcnR5cGVzXG4gICAgZnVuIG1peChkcmlua1RvTWl4OiBEcmluayl7XG4gICAgXHR2YWwgbWl4dHVyZSA9IG9iamVjdCB7XG4gICAgICAgICAgICB2YWwgZHJpbmsgPSBkcmlua1RvTWl4XG4gICAgICAgICAgICB2YWwgaW5ncmVkaWVudCA9IFwiU2FsdFwiXG4gICAgXHR9XG4gICAgICAgIC8vIFRPRE8gdHJ5IHRvIHByaW50IHRoZSByZXN1bHQgb2YgbWl4dHVyZS5pbmdyZWRpZW50ICsgbWl4dHVyZS5kcmlua1RvTWl4XG4gICAgICAgIHByaW50bG4oKVxuICAgIH1cbiAgICBtaXgoc21hbGxCb3R0bGUpXG4gICAgXG4gICAgXG4gICAgLy8gTGV0J3MgdHJ5IHRvIHJldHVybiBhbiBhZGhvYyBhbm9ueW1vdXMgb2JqZWN0XG4gICAgZnVuIHByZXBhcmVOZXdNaXgoZHJpbmtUb01peDogRHJpbmspID0gb2JqZWN0IHtcbiAgICAgICAgdmFsIGRyaW5rID0gZHJpbmtUb01peFxuICAgICAgICB2YWwgaW5ncmVkaWVudCA9IFwiU2FsdFwiXG4gICAgfVxuICAgIHZhbCBuZXdNaXggPSBwcmVwYXJlTmV3TWl4KHNtYWxsQm90dGxlKVxuICAgIC8vIFRPRE8gdHlwZSB0byBwcmludCB0aGUgY29tcG9uZW50cyBvZiBuZXdNaXguIElzIGl0IHBvc3NpYmxlIHRvIGFjY2VzcyB0aGUgY29udGVudHMgb2YgdGhlIGFub255bW91cyBvYmplY3Qgb3V0c2lkZSB0aGUgbG9jYWwgc2NvcGUgP1xuXG4gICAgLy8gVE9ETyB3aGF0IGhhcHBlbnMgaWYgd2UgcmVtb3ZlIHRoZSBwcml2YXRlIHF1YWxpZmllciB0byBwcmVwYXJlID9cbiAgICBjbGFzcyBEcmlua01peGVyIHtcbiAgICAgICAgcHJpdmF0ZSBmdW4gcGVyYXByZShkcmlua1RvTWl4OiBEcmluaykgPSBvYmplY3Qge1xuICAgICAgICAgICAgdmFsIGRyaW5rID0gZHJpbmtUb01peFxuICAgICAgICAgICAgdmFsIGluZ3JlZGllbnQgPSBcIlNhbHRcIlxuICAgICAgICB9XG5cbiAgICAgICAgZnVuIG1peCgpe1xuICAgICAgICAgICAgdmFsIHByZXBhcmF0aW9uID0gcGVyYXByZShEcmluaygxMDApKVxuICAgICAgICAgICAgcHJpbnRsbihcIiR7cHJlcGFyYXRpb24uZHJpbmt9IGFuZCAke3ByZXBhcmF0aW9uLmluZ3JlZGllbnR9XCIpXG4gICAgICAgIH1cblx0fVxufSJ9",target:"_blank",rel:"noopener noreferrer"},jl={class:"custom-container details"},Ul=l("summary",null,"Please open to see the solution(s)",-1),Kl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gT2JqZWN0cyBpbiBrb3RsaW4gaHR0cHM6Ly9rb3RsaW5sYW5nLm9yZy9kb2NzL3JlZmVyZW5jZS9vYmplY3QtZGVjbGFyYXRpb25zLmh0bWxcbm9wZW4gY2xhc3MgRHJpbmsodmFyIHZvbHVtZTogSW50KXtcbiAgICBvcGVuIGZ1biBjb25zdW1lKGNvbnN1bWVkVm9sdW1lOiBJbnQpe1xuICAgICAgICB0aGlzLnZvbHVtZSAtPSBjb25zdW1lZFZvbHVtZVxuICAgIH1cbn1cbmludGVyZmFjZSBDb2xvcmVke1xuICAgIHZhbCBjb2xvcjpTdHJpbmdcbn1cblxuLy8gYW5vbnltb3VzIG9iamVjdHMgaGF2ZSBhY2Nlc3MgdG8gdmFyaWFibGVzIGluIHRoZSBlbmNsb3Npbmcgc2NvcGVcbmZ1biBjcmVhdGVEcmlua0FuZENvdW50KCk6IERyaW5re1xuICAgIHZhciBuYkNvbnN1bXB0aW9uID0gMFxuICAgIHJldHVybiBvYmplY3QgOiBEcmluaygxMDApe1xuICAgICAgICBvdmVycmlkZSBmdW4gY29uc3VtZShjb25zdW1lZFZvbHVtZTogSW50KSB7XG4gICAgICAgICAgICBzdXBlci5jb25zdW1lKGNvbnN1bWVkVm9sdW1lKVxuICAgICAgICAgICAgbmJDb25zdW1wdGlvbiArPSAxXG4gICAgICAgICAgICBwcmludGxuKG5iQ29uc3VtcHRpb24pXG4gICAgICAgIH1cbiAgICB9XG59XG5cbmZ1biBtYWluKCkge1xuICAgIC8vIENyZWF0aW5nIGFuIGluc3RhbmNlIG9mIERyaW5rXG4gICAgdmFsIHNtYWxsQm90dGxlID0gRHJpbmsoMTAwKVxuICAgIC8vIFRPRE8gY3JlYXRlIGEgYmlnQm90dGxlIHdpdGggYSB2b2x1bWUgb2YgNTAwXG5cbiAgICAvLyBLb3RsaW4gYWxsb3dzIHRvIHF1aWNrbHkgY3JlYXRlIGFuIGFub255bW91cyBvYmplY3QgdGhhdCBpbXBsZW1lbnRzIGFuZCBleGlzdGluZyBjbGFzcyBhbmQvb3IgaW50ZXJmYWNlc1xuICAgIHZhbCBzbWFsbENhcmJvbmF0ZWREcmlua0JvdHRsZSA9IG9iamVjdDogRHJpbmsoMTAwKSB7XG4gICAgICAgIG92ZXJyaWRlIGZ1biBjb25zdW1lKGNvbnN1bWVkVm9sdW1lOiBJbnQpIHtcbiAgICAgICAgICAgIHN1cGVyLmNvbnN1bWUoY29uc3VtZWRWb2x1bWUpXG4gICAgICAgICAgICBwcmludGxuKFwiUFNISEhISEhcIilcbiAgICAgICAgfVxuICAgIH1cbiAgICBzbWFsbENhcmJvbmF0ZWREcmlua0JvdHRsZS5jb25zdW1lKDIwKVxuXG4gICAgLy8gVE9ETyBjcmVhdGVkIGFuIGFub255bW91cyBvYmplY3Qgc21hbGxDYXJib25hdGVkUmVkRHJpbmtCb3R0bGUgdGhhdCBpcyBib3RoIGEgRHJpbmsgYW5kIENvbG9yZWRcbiAgICAvLyBUT0RPIENhbiBrb3RsaW4gaW5mZXIgdGhlIHR5cGUgb2Ygc21hbGxDYXJib25hdGVkUmVkRHJpbmtCb3R0bGUgP1xuICAgIHZhbCBzbWFsbENhcmJvbmF0ZWRSZWREcmlua0JvdHRsZTogQ29sb3JlZCA9IG9iamVjdDogRHJpbmsoMTAwKSwgQ29sb3JlZCB7XG4gICAgICAgIG92ZXJyaWRlIHZhbCBjb2xvcjogU3RyaW5nXG4gICAgICAgICAgICBnZXQoKSA9IFwiUmVkXCJcblxuICAgICAgICBvdmVycmlkZSBmdW4gY29uc3VtZShjb25zdW1lZFZvbHVtZTogSW50KSB7XG4gICAgICAgICAgICBzdXBlci5jb25zdW1lKGNvbnN1bWVkVm9sdW1lKVxuICAgICAgICAgICAgcHJpbnRsbihcIlBTSEhISEhIXCIpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBUT0RPIG1ha2UgdGhpcyBjb2RlIHByaW50IFwiUmVkXCJcbiAgICAvLyBwcmludGxuKHNtYWxsQ2FyYm9uYXRlZFJlZERyaW5rQm90dGxlLmNvbG9yKVxuICAgIFxuICAgIFxuICAgIC8vIFRPRE8gd2hhdCBpcyBwcmludGVkIGFmdGVyIGNhbGxpbmcgdGhlIGZvbGxvd2luZyBjb2RlID9cbiAgICB2YWwgZHJpbmtUaGF0Q291bnRzID0gY3JlYXRlRHJpbmtBbmRDb3VudCgpXG4gICAgZHJpbmtUaGF0Q291bnRzLmNvbnN1bWUoMTApXG4gICAgZHJpbmtUaGF0Q291bnRzLmNvbnN1bWUoMjApXG5cbiAgICAvLyBUT0RPIFRyeSB0byBndWVzcyB3aGF0IGlzIHByaW50ZWQgaWYgd2UgZ2V0IGFub3RoZXIgZHJpbmsgZnJvbSAnY3JlYXRlRHJpbmtBbmRDb3VudCgpJyBhbmQgY2FsbCBjb25zdW1lID9cblxuICAgIFxuICAgIC8vIEtvdGxpbiBhbHNvIGFsbG93cyB0byBjcmVhdGUgYW5vbnltb3VzIG9iamVjdHMgd2l0aG91dCBzdXBlcnR5cGVzXG4gICAgZnVuIG1peChkcmlua1RvTWl4OiBEcmluayl7XG4gICAgXHR2YWwgbWl4dHVyZSA9IG9iamVjdCB7XG4gICAgICAgICAgICB2YWwgZHJpbmsgPSBkcmlua1RvTWl4XG4gICAgICAgICAgICB2YWwgaW5ncmVkaWVudCA9IFwiU2FsdFwiXG4gICAgXHR9XG4gICAgICAgIC8vIFRPRE8gdHJ5IHRvIHByaW50IHRoZSByZXN1bHQgb2YgbWl4dHVyZS5pbmdyZWRpZW50ICsgbWl4dHVyZS5kcmlua1RvTWl4XG4gICAgICAgIHByaW50bG4oKVxuICAgIH1cbiAgICBtaXgoc21hbGxCb3R0bGUpXG4gICAgXG4gICAgXG4gICAgLy8gTGV0J3MgdHJ5IHRvIHJldHVybiBhbiBhZGhvYyBhbm9ueW1vdXMgb2JqZWN0XG4gICAgZnVuIHByZXBhcmVOZXdNaXgoZHJpbmtUb01peDogRHJpbmspID0gb2JqZWN0IHtcbiAgICAgICAgdmFsIGRyaW5rID0gZHJpbmtUb01peFxuICAgICAgICB2YWwgaW5ncmVkaWVudCA9IFwiU2FsdFwiXG4gICAgfVxuICAgIHZhbCBuZXdNaXggPSBwcmVwYXJlTmV3TWl4KHNtYWxsQm90dGxlKVxuICAgIC8vIFRPRE8gdHlwZSB0byBwcmludCB0aGUgY29tcG9uZW50cyBvZiBuZXdNaXguIElzIGl0IHBvc3NpYmxlIHRvIGFjY2VzcyB0aGUgY29udGVudHMgb2YgdGhlIGFub255bW91cyBvYmplY3Qgb3V0c2lkZSB0aGUgbG9jYWwgc2NvcGUgP1xuXG4gICAgLy8gVE9ETyB3aGF0IGhhcHBlbnMgaWYgd2UgcmVtb3ZlIHRoZSBwcml2YXRlIHF1YWxpZmllciB0byBwcmVwYXJlID9cbiAgICBjbGFzcyBEcmlua01peGVyIHtcbiAgICAgICAgcHJpdmF0ZSBmdW4gcGVyYXByZShkcmlua1RvTWl4OiBEcmluaykgPSBvYmplY3Qge1xuICAgICAgICAgICAgdmFsIGRyaW5rID0gZHJpbmtUb01peFxuICAgICAgICAgICAgdmFsIGluZ3JlZGllbnQgPSBcIlNhbHRcIlxuICAgICAgICB9XG5cbiAgICAgICAgZnVuIG1peCgpe1xuICAgICAgICAgICAgdmFsIHByZXBhcmF0aW9uID0gcGVyYXByZShEcmluaygxMDApKVxuICAgICAgICAgICAgcHJpbnRsbihcIiR7cHJlcGFyYXRpb24uZHJpbmt9IGFuZCAke3ByZXBhcmF0aW9uLmluZ3JlZGllbnR9XCIpXG4gICAgICAgIH1cblx0fVxufSJ9",target:"_blank",rel:"noopener noreferrer"},Ml=l("h2",{id:"📖-further-reading",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#📖-further-reading"},[l("span",null,"📖 Further reading")])],-1),Ql={href:"https://play.kotlinlang.org/koans/overview",target:"_blank",rel:"noopener noreferrer"},Tl={href:"https://sderosiaux.medium.com/why-referential-transparency-matters-7c179424dab5",target:"_blank",rel:"noopener noreferrer"};function Dl(_l,Pl){const g=a("ExternalLinkIcon");return n(),b("div",null,[t,l("p",null,[l("a",o,[I("▶️ this code"),c(g)]),I(" highlights the above features.")]),d,l("p",null,[l("a",G,[I("▶️ this code"),c(g)]),I(" highlights the above features.")]),u,l("p",null,[l("a",m,[I("▶️ this code"),c(g)]),I(" illustrates null safety and how to use optional types.")]),h,C,l("p",null,[I("Enumerations allow to work with a group of values in a type-safe fashion. Unlike Java enums, "),l("a",r,[I("Kotlin enums are classes"),c(g)]),I(". Kotlin enum classes provide these features:")]),Z,l("p",null,[l("a",p,[I("▶️ this code"),c(g)]),I(" illustrated the features above. For further reading please consult "),l("a",B,[I("the official documentation"),c(g)]),I(".")]),X,W,l("p",null,[l("a",y,[I("Please click on this link to view the exercise"),c(g)])]),l("details",A,[V,l("p",null,[l("a",Y,[I("Solution"),c(g)])])]),J,l("p",null,[l("a",R,[I("Please click on this link to view the exercise"),c(g)])]),l("details",v,[x,l("p",null,[l("a",F,[I("Solution"),c(g)])])]),H,l("p",null,[l("a",w,[I("▶️ this code"),c(g)]),I(" illustrates some features.")]),N,l("p",null,[I("Data classes are "),S,I(" (cannot be inherited from) classes that provide "),l("a",k,[I("standard functionality"),c(g)]),I(":")]),E,l("p",null,[I("However, they have the "),l("a",L,[I("following constraints"),c(g)]),I(":")]),z,l("p",null,[l("a",f,[I("▶️ this code"),c(g)]),I(" illustrates some features.")]),j,U,l("p",null,[I("Functional programming revolves around "),l("a",K,[I("these concepts"),c(g)]),I(": pure functions, recursion, referential transparency, immutable variables, functions as first-class citizens, and higher-order functions.")]),M,l("ul",null,[Q,T,D,l("li",null,[l("a",_,[I("Referential transparency"),c(g)]),I(": means that an expression can be replaced by its result without changing the behavior of the program. Transparency refers to the fact that the implementation of the expression is irrelevant.")])]),P,O,l("p",null,[I("Kotlin is not a pure functional languages but it supports some features. For example, Kotlin does not have compile time verification of pure functions, but it provides immutable collections through the "),l("a",q,[I("kotlinx.collections.immutable"),c(g)]),I(" library.")]),l("div",$,[ll,l("p",null,[l("a",Il,[I("A read-only list"),c(g)]),I(" cannot add or remove elements, but it can change the underlying data.")]),gl]),l("p",null,[I("The "),l("a",cl,[I("Arrow-kt"),c(g)]),I(" library add more functional programming features.")]),il,el,l("p",null,[l("a",al,[I("▶️ this code"),c(g)]),I(" show an example of list manipulation using declarative programming.")]),nl,l("ul",null,[bl,sl,tl,ol,l("li",null,[I("Kotlin generates Java records by annotating a data class with "),dl,I(" and targeting JVM 16, among other requirement "),l("a",Gl,[I("listed here"),c(g)]),I(".")]),ul]),l("p",null,[l("a",ml,[I("▶️ this code"),c(g)]),I(" shows how to convert a Kotlin "),hl,I(" to a Java "),Cl,I(".")]),l("p",null,[I("The official documentation provides exhaustive documentation on "),l("a",rl,[I("Kotlin and JVM integration"),c(g)])]),Zl,pl,l("p",null,[l("a",Bl,[I("Please click on this link to view the exercise"),c(g)])]),l("details",Xl,[Wl,l("p",null,[l("a",yl,[I("Solution"),c(g)])])]),Al,l("p",null,[l("a",Vl,[I("Please click on this link to view the exercise"),c(g)])]),l("details",Yl,[Jl,l("p",null,[l("a",Rl,[I("Solution"),c(g)])])]),vl,l("p",null,[l("a",xl,[I("Please click on this link to view the exercise"),c(g)])]),l("details",Fl,[Hl,l("p",null,[l("a",wl,[I("Solution"),c(g)])])]),Nl,l("p",null,[l("a",Sl,[I("Please click on this link to view the exercise"),c(g)])]),l("details",kl,[El,l("p",null,[l("a",Ll,[I("Solution"),c(g)])])]),zl,l("p",null,[l("a",fl,[I("Please click on this link to view the exercise"),c(g)])]),l("details",jl,[Ul,l("p",null,[l("a",Kl,[I("Solution"),c(g)])])]),Ml,l("ul",null,[l("li",null,[l("a",Ql,[I("Kotlin Koans"),c(g)])]),l("li",null,[l("a",Tl,[I("Referential transparency"),c(g)])])])])}const ql=e(s,[["render",Dl],["__file","index.html.vue"]]),$l=JSON.parse('{"path":"/en/kotlin-features/","title":"📚 Kotlin language features","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Basic features","slug":"basic-features","link":"#basic-features","children":[{"level":3,"title":"Basic constructs (variables, control flow)","slug":"basic-constructs-variables-control-flow","link":"#basic-constructs-variables-control-flow","children":[]},{"level":3,"title":"Functions","slug":"functions","link":"#functions","children":[]},{"level":3,"title":"Null safety","slug":"null-safety","link":"#null-safety","children":[]},{"level":3,"title":"Enumerations","slug":"enumerations","link":"#enumerations","children":[]},{"level":3,"title":"🧪 Exercises","slug":"🧪-exercises","link":"#🧪-exercises","children":[]}]},{"level":2,"title":"Intermediate features","slug":"intermediate-features","link":"#intermediate-features","children":[{"level":3,"title":"Object oriented programming","slug":"object-oriented-programming","link":"#object-oriented-programming","children":[]},{"level":3,"title":"Data class","slug":"data-class","link":"#data-class","children":[]},{"level":3,"title":"Functional programming","slug":"functional-programming","link":"#functional-programming","children":[]},{"level":3,"title":"Kotlin and Java interoperability","slug":"kotlin-and-java-interoperability","link":"#kotlin-and-java-interoperability","children":[]},{"level":3,"title":"🧪 Exercises","slug":"🧪-exercises-1","link":"#🧪-exercises-1","children":[]}]},{"level":2,"title":"📖 Further reading","slug":"📖-further-reading","link":"#📖-further-reading","children":[]}],"git":{"updatedTime":1702378796000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":10},{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":2},{"name":"Ibrahim Gharbi","email":"brah.gharbi@gmail.com","commits":1},{"name":"yostane","email":"1958676+yostane@users.noreply.github.com","commits":1}]},"filePathRelative":"en/kotlin-features/README.md"}');export{ql as comp,$l as data};
diff --git a/assets/index.html-B1GaKHtm.js b/assets/index.html-B1GaKHtm.js
new file mode 100644
index 00000000..32acfbd8
--- /dev/null
+++ b/assets/index.html-B1GaKHtm.js
@@ -0,0 +1 @@
+import{_ as s,a as d,b as p,c as u}from"./qrcode-mixtit24-D6tnJWnS.js";import{_ as c,r as a,o as m,c as h,a as e,b as l,d as n,w as o,e as r}from"./app-jR2rC7Ae.js";const f={},k=r('
am2023 logo',4),g={href:"https://awl.li/am23-kt",target:"_blank",rel:"noopener noreferrer"},_=e("figure",null,[e("img",{src:s,alt:"qr code",width:"350",tabindex:"0",loading:"lazy"}),e("figcaption",null,"qr code")],-1),b=e("h3",{id:"agenda",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#agenda"},[e("span",null,"Agenda")])],-1),v={href:"https://worldline.github.io/learning-kotlin-multiplatform/",target:"_blank",rel:"noopener noreferrer"},A=e("h2",{id:"_2023-jnation-let-s-discover-the-possibilities-of-kotlin",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#_2023-jnation-let-s-discover-the-possibilities-of-kotlin"},[e("span",null,"(2023) JNation : Let's discover the possibilities of Kotlin")])],-1),K=e("h3",{id:"link-1",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#link-1"},[e("span",null,"Link")])],-1),w={href:"https://awl.li/jnation23-kt",target:"_blank",rel:"noopener noreferrer"},x=e("figure",null,[e("img",{src:"data:image/gif;base64,R0lGODdhkgCSAIAAAAAAAP///ywAAAAAkgCSAAAC/4yPqcvtD6OctNqLs968+w+G4kiW5omm6sq27gvH8kwD9o3jho3ogb8D9G4O3o94MAZzyKAzyUQqj9Ecpcp8LoVUrrZb5AKPw+rwixV7u+nrWmFsg8n07bkOhauz+Ls535cwBTEouBdlRzc1FvcW2MhX+PQ36XgnASllOVdmBbi4qdcpaajXWLl1OpGpaupIqbhGKgo1xlDoo8qq5pbKi8eo2Xl7SJuZSMyHHJsburCLWisrLE28HN08y9bEyUyNedgaaP2ZZm7bfS1Nquupbew+De53Xu96/+wJ7/tF6KwNzRCuTQPhKcvXjha5eQ0AhnvFrR+gS+UO4hMnkaK/c//YfiWM1TEkyJEfS9pbddKkSJUkPbpc+dKbHBED0dGrFxHYL1/B0DhzUTPnTZwQ6Y3SNzJjiGPbFOqMtgznMG9h2oEa2muOxaflfILiWI1qQ2o9K/4cSzHbN4TyzLoq+xWRWaYeroaN9I+bXXSwxM2UWawD0z9wd05Muk5vW59pi4rFMFhuYYl7IR60y/hwwccVKvNEmiiyxpZHJydWW/ldqa7btjYLvZh13MuG5dBWtxELbNSK2T3Ei/tuYKOkL4Dl/XmqQd2cLw6XHTNrR4w6I4NVjviv38DAO7cVvXr3b7nYt/etzT2eBdt5CT+n/D02Zq5Kd1GvKrl9fnWbodL//z/fUK+h5NRCWuU0H3XaoXeeU1sxNJqDmvTHVVC3VUfeI9PYtAGHAy5XoGZCCRfcZ54tNeKHq/FF0Ib6VeJQg+N4x5JvdHVjIYMZmsccjBm6UWOLN5Z1lFc76ijjX+BsZmOLIUolYmNShiXgWck41qSB8F0HoHxeMglaBFxaF9uV55lmYnzE+ajaW0eKF6FAPTbFWpprZveQBlYpFpWTOPLJlo/4+XeclfmwGV+AblE544Acwmake5C912WJf073JoYXjvYogelU+ml5MS0YKVIUWjooZ3HZiWdmU7oK6qYZOAqoeN2dCN+roCIaJ5CGbeliGFoWxyqh5nAaZqqn//7p0LAfFYsnqXK2eeiKmfKDGKK3jgjpp8tS6+x4jdG6nKShkuitY7169ySlPMLSZ6loRruWnuoueieX8WpqKnqlKoXfnq5ZNh6F2+maoJDMgUunfQgSDG1mCSNc5mnqoaTiVGol16qGpQG6arpFGnexc2SSW6eISQYrcmKTYhVetZb6FqfD9RWsJskwrxvysLBWeR9g864HMss9Mwr0Wrx5eCx0nV6Zr9IP09ya1A43uTK2ky4I74xu6jtXvfy6x7CZR3ZNLVEpiuuxxXiVbfE+jDZbNYvOzSw1PvYObCTPOH9LFrcHIgu3qMbue3fV/iXe6caqGrrvwZcGKnR0lP/faLJLigpLp8ZFM72nm8pSeqfW7HYuXGprhz7ti81ZK7rOdf+oIdmBFxqj5gp3TXSafLeOXOVvu46524ITHczmGPY9ecLPWjt88Mj3VjHzIc97/a9h24y6r+9OuDDbN584PNRzV4+x1fGwx/H4OZdPOeJei4m+zw2jP5vlMA9JfV31y9y6n6kscOSg1dGe9gAW2Y1fcose+CLnItoYMFkJnNrD/gW7+8EPe3Pa1oMqqJqASAxL4JPVxKKEtMr5yh+mw9WipAWrE/JHfPQrV5iu5q8KEexC+UMgrzyVNDCZ63C1y1QPjxe23sUMU8ZyXgRJhy4foi5onEOWoOg1rif/4o11uXHh9EaoKOWNTl5FG5lpCoeuK+bqMEuynLTyB0GAjQVthqIb04gYQy9hR4BdvGEd8xKc58lwVy2TY+xq5i4tCo+G3zujHw3JlmWpcYACW1+/yEi+R87PQEHxXPbAoyQ5TTJxpoOkhPR2oE+mJ2KifJ3nRrYus30seYn6GxSh1UgLQqmNR6Tl/prWuA4iyY9q29vnFFZKfHUsjxTjnv8Kk6XuKVNyzEShrZDIyzylUHX8g8ksc8YxEW5yjiVk2ZTg582xlTFugIllAOM3ziHOz3HFe+XgljgrChKJnCrKXbeCyTpJkqyXFYPmDrXHwycO7X6mbCUuIaZMbOUo/2pZeycVT2e4VkrQklUk5AkDis0KorKTU8wP3apZTZDCjWri5CI7k/lGhVLPoEps4CVfdyZd3pSgH9tZPj1JwoSC8oHkHCUAdfWy1AV1p0Ql0xjPhUh3mo+WghwmS90oTDL2lI3p6xPKmqjPUcnUhTndJwu5OlWxwrKoHtUjWlHIMDFqVK0uA2EOUfqfU46Tcavrq/uKmbKYflOql4PiBzNmPMEaj6LXGsECZ0jSx851i/rLK4oMZsHMheifgvuqNdPHvlSa9IJiqSQHc6qtkPrudjiDqe4wldDBRnFOZy3gKoFH198B9LUidajsbNutrZpkgbs1arvQ+MMjwgRw7JuZ6Du/KSOGdCecYmVu+Jy71tXK6qzb2ipxQpjI4BZ2owLl7sWUm0HwzlC8maPqvbLS3d4mbbWoWiRZ/wjf855FiJa0nXkV29C4AnKaudUmYxGbWjDWCogA7GZxxAlWBKtQh2alX6H+qh/iLhjAs7Ph9EKLtQyTdp8cDiWEaYDiFKt4xSxusYtfDOMYy3jGNK6xjW+M4xzreAMFAAA7",alt:"qr code",width:"350",tabindex:"0",loading:"lazy"}),e("figcaption",null,"qr code")],-1),y=e("h3",{id:"agenda-1",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#agenda-1"},[e("span",null,"Agenda")])],-1),q={href:"https://worldline.github.io/learning-kotlin-multiplatform/",target:"_blank",rel:"noopener noreferrer"},W=e("h2",{id:"_2023-mobile-devops-summit",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#_2023-mobile-devops-summit"},[e("span",null,"(2023) Mobile DevOps summit")])],-1),S=e("li",null,[l("Title: "),e("strong",null,"From Android to Multiplatform: leveraging the full potential of Kotlin")],-1),j={href:"https://awl.li/mds23-kt",target:"_blank",rel:"noopener noreferrer"},P=e("figure",null,[e("img",{src:d,alt:"qr code",width:"200",tabindex:"0",loading:"lazy"}),e("figcaption",null,"qr code")],-1),C=e("h3",{id:"agenda-2",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#agenda-2"},[e("span",null,"Agenda")])],-1),L=r('- Titre : Développement front et back en Kotlin. Une visite guidée de KMP
qr code',4),M=e("strong",null,"Développement backend",-1),F={href:"https://speakerdeck.com/yostane/kotlin-pour-le-developpement-backend",target:"_blank",rel:"noopener noreferrer"},J=e("strong",null,"Développement frontend",-1),Z=e("strong",null,"Développement fullstack",-1),H=e("strong",null,"Autres fonctionnalités",-1);function O(D,N){const i=a("ExternalLinkIcon"),t=a("RouteLink");return m(),h("div",null,[k,e("p",null,[l("Workshop link: "),e("a",g,[l("awl.li/am23-kt"),n(i)])]),_,b,e("ul",null,[e("li",null,[n(t,{to:"/en/backend-development/#ktor"},{default:o(()=>[l("Backend development: Rest API with Ktor")]),_:1})]),e("li",null,[l("Optional: "),n(t,{to:"/en/backend-development/#ktor"},{default:o(()=>[l("Backend development: Rest API with nodeJS and Kotlin/JS")]),_:1})]),e("li",null,[n(t,{to:"/en/front-development/#kotlin-js-and-kotlin-wasm"},{default:o(()=>[l("Front-end development: Kotlin/WASM and Kotlin/JS webapp")]),_:1})]),e("li",null,[n(t,{to:"/en/front-development/#compose"},{default:o(()=>[l("Front-end development: cross-platform Hello world App with Compose multiplatform")]),_:1})]),e("li",null,[e("a",v,[l("Front-end development: Cross-platform Quiz App with Compose multiplatform"),n(i)])]),e("li",null,[n(t,{to:"/en/other-technologies/#pw-add-a-ktor-server-app"},{default:o(()=>[l("Full-stack development: Quiz App with Compose multiplatform and Ktor server")]),_:1})])]),A,K,e("p",null,[l("Workshop link: "),e("a",w,[l("awl.li/jnation23-kt"),n(i)])]),x,y,e("ul",null,[e("li",null,[n(t,{to:"/en/presentation/#prerequisites"},{default:o(()=>[l("Prerequisutes")]),_:1})]),e("li",null,[l("Selection of Kotlin's languages features: "),n(t,{to:"/en/kotlin-features/#null-safety"},{default:o(()=>[l("null safety")]),_:1}),l(" and "),n(t,{to:"/en/kotlin-features/#functions"},{default:o(()=>[l("functions")]),_:1})]),e("li",null,[n(t,{to:"/en/backend-development/#ktor"},{default:o(()=>[l("Backend development: Rest API with Ktor")]),_:1})]),e("li",null,[l("Optional: "),n(t,{to:"/en/backend-development/#ktor"},{default:o(()=>[l("Backend development: Rest API with nodeJS and Kotlin/JS")]),_:1})]),e("li",null,[n(t,{to:"/en/front-development/#kotlin-js-and-kotlin-wasm"},{default:o(()=>[l("Front-end development: Kotlin/WASM and Kotlin/JS webapp")]),_:1})]),e("li",null,[n(t,{to:"/en/front-development/#compose"},{default:o(()=>[l("Front-end development: cross-platform Hello world App with Compose multiplatform")]),_:1})]),e("li",null,[e("a",q,[l("Front-end development: Cross-platform Quiz App with Compose multiplatform"),n(i)])]),e("li",null,[n(t,{to:"/en/other-technologies/#pw-add-a-ktor-server-app"},{default:o(()=>[l("Full-stack development: Quiz App with Compose multiplatform and Ktor server")]),_:1})])]),W,e("ul",null,[S,e("li",null,[l("Link: "),e("a",j,[l("awl.li/mds23-kt"),n(i)])])]),P,C,e("ul",null,[e("li",null,[n(t,{to:"/en/other-technologies/#pw-add-a-ktor-server-app"},{default:o(()=>[l("Initiate a cross-platform app")]),_:1})]),e("li",null,[l("Live examples of other Kotlin possibilities: "),e("ul",null,[e("li",null,[n(t,{to:"/en/backend-development/#ktor"},{default:o(()=>[l("Backend development: Rest API with Ktor")]),_:1})]),e("li",null,[n(t,{to:"/en/backend-development/#ktor"},{default:o(()=>[l("Backend development: Rest API with nodeJS and Kotlin/JS")]),_:1})]),e("li",null,[n(t,{to:"/en/front-development/#kotlin-js-and-kotlin-wasm"},{default:o(()=>[l("Front-end development: Kotlin/WASM and Kotlin/JS webapp")]),_:1})])])])]),L,e("ul",null,[e("li",null,[n(t,{to:"/en/presentation/#prerequisites"},{default:o(()=>[l("Prérequis")]),_:1})]),e("li",null,[l("Fonctionnalités notables: "),n(t,{to:"/en/kotlin-features/#null-safety"},{default:o(()=>[l("null safety")]),_:1}),l(" et "),n(t,{to:"/en/kotlin-features/#functions"},{default:o(()=>[l("les fonctions")]),_:1})]),e("li",null,[M,e("ul",null,[e("li",null,[e("a",F,[l("Présentation d'introduction à Kotlin pour le développement backend"),n(i)])]),e("li",null,[n(t,{to:"/en/backend-development/#spring-framework"},{default:o(()=>[l("API Rest avec Spring boot")]),_:1})]),e("li",null,[n(t,{to:"/en/backend-development/#ktor"},{default:o(()=>[l("API Rest avec Ktor")]),_:1})])])]),e("li",null,[J,e("ul",null,[e("li",null,[n(t,{to:"/en/front-development/#compose"},{default:o(()=>[l('Application "Hello World" avec Compose Multiplatform')]),_:1})])])]),e("li",null,[Z,e("ul",null,[e("li",null,[n(t,{to:"/en/other-technologies/#pw-add-a-ktor-server-app"},{default:o(()=>[l("Application de quiz avec Ktor + Compose Multiplatform")]),_:1})])])]),e("li",null,[H,e("ul",null,[e("li",null,[n(t,{to:"/en/front-development/#kotlin-js-and-kotlin-wasm"},{default:o(()=>[l("Kotlin/WASM")]),_:1})]),e("li",null,[n(t,{to:"/en/backend-development/#nodejs"},{default:o(()=>[l("Développement NodeJS en Kotlin")]),_:1})]),e("li",null,[n(t,{to:"/en/kotlin-features-advanced/#concurrency-and-coroutines"},{default:o(()=>[l("Coroutines")]),_:1})])])])])])}const Y=c(f,[["render",O],["__file","index.html.vue"]]),I=JSON.parse(`{"path":"/en/workshops/","title":"📅 Workshops","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"(2023) Android makers : Kotlin Beyond Android","slug":"_2023-android-makers-kotlin-beyond-android","link":"#_2023-android-makers-kotlin-beyond-android","children":[{"level":3,"title":"Link","slug":"link","link":"#link","children":[]},{"level":3,"title":"Agenda","slug":"agenda","link":"#agenda","children":[]}]},{"level":2,"title":"(2023) JNation : Let's discover the possibilities of Kotlin","slug":"_2023-jnation-let-s-discover-the-possibilities-of-kotlin","link":"#_2023-jnation-let-s-discover-the-possibilities-of-kotlin","children":[{"level":3,"title":"Link","slug":"link-1","link":"#link-1","children":[]},{"level":3,"title":"Agenda","slug":"agenda-1","link":"#agenda-1","children":[]}]},{"level":2,"title":"(2023) Mobile DevOps summit","slug":"_2023-mobile-devops-summit","link":"#_2023-mobile-devops-summit","children":[{"level":3,"title":"Agenda","slug":"agenda-2","link":"#agenda-2","children":[]}]},{"level":2,"title":"(2024) MiXit","slug":"_2024-mixit","link":"#_2024-mixit","children":[{"level":3,"title":"Agenda","slug":"agenda-3","link":"#agenda-3","children":[]}]}],"git":{"updatedTime":1713801068000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":10},{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":2},{"name":"yostane","email":"1958676+yostane@users.noreply.github.com","commits":1}]},"filePathRelative":"en/workshops/README.md"}`);export{Y as comp,I as data};
diff --git a/assets/index.html-B8Y7MGp7.js b/assets/index.html-B8Y7MGp7.js
new file mode 100644
index 00000000..207ffd0b
--- /dev/null
+++ b/assets/index.html-B8Y7MGp7.js
@@ -0,0 +1 @@
+import{_ as a,r as l,o as r,c as o,a as e,b as t,d as i,e as s}from"./app-jR2rC7Ae.js";const c={},d={class:"custom-container tip"},m=e("p",{class:"custom-container-title"},"TIP",-1),u={href:"/",target:"_blank",rel:"noopener noreferrer"},h={href:"/",target:"_blank",rel:"noopener noreferrer"},p=s('- Connaissance de base en language de programmation orienté object comme Java
- Préparez votre environnement de développement et installez des éléments avant la session (voir la section Outillage)
',3),_={href:"https://kotlinlang.org/docs/home.html",target:"_blank",rel:"noopener noreferrer"},g={href:"https://github.com/worldline/learning-kotlin",target:"_blank",rel:"noopener noreferrer"};function f(b,v){const n=l("ExternalLinkIcon");return r(),o("div",null,[e("div",d,[m,e("p",null,[t("This training is also available in "),e("a",u,[t("English"),i(n)]),t(" / Cette formation est aussi disponible en "),e("a",h,[t("Anglais"),i(n)])])]),p,e("ul",null,[e("li",null,[e("a",_,[t("Documentation officielle Kotlin"),i(n)])]),e("li",null,[e("a",g,[t("Dépôt Github de la formation"),i(n)])])])])}const x=a(c,[["render",f],["__file","index.html.vue"]]),q=JSON.parse(`{"path":"/fr/","title":"","lang":"fr-FR","frontmatter":{"home":true,"heroImage":"./kotlin_logo.png","tagline":"A beginner's guide to a modern programming language","actions":[{"text":"Débuter →","link":"/fr/presentation/","type":"primary"}],"features":[{"title":"Bien commencer avec Kotlin","details":"lorem"},{"title":"Syntaxe de base","details":"lorem"},{"title":"Exemples","details":"lorem"}],"footer":"Worldline, 2021"},"headers":[{"level":2,"title":"Prerequisites","slug":"prerequisites","link":"#prerequisites","children":[]},{"level":2,"title":"Liens utiles","slug":"liens-utiles","link":"#liens-utiles","children":[]}],"git":{"updatedTime":1660920327000,"contributors":[{"name":"W112314","email":"quentin.carpentier@worldline.com","commits":1},{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":1}]},"filePathRelative":"fr/index.md"}`);export{x as comp,q as data};
diff --git a/assets/index.html-BBhIJwI8.js b/assets/index.html-BBhIJwI8.js
new file mode 100644
index 00000000..552d844d
--- /dev/null
+++ b/assets/index.html-BBhIJwI8.js
@@ -0,0 +1,13 @@
+import{_ as c,r as l,o as i,c as o,a as e,b as n,d as t,e as a}from"./app-jR2rC7Ae.js";const u={},r=e("h1",{id:"📚-fonctionnalites-avancees-de-kotlin",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#📚-fonctionnalites-avancees-de-kotlin"},[e("span",null,"📚 Fonctionnalités avancées de Kotlin")])],-1),p=e("h2",{id:"proprietes-deleguees",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#proprietes-deleguees"},[e("span",null,"Propriétés déléguées")])],-1),d=e("p",null,[n("Kotlin permet de déléguer le getter et le setter d'une propriété à un autre objet, appelé délégué. C'est une classe qui définit les méthodes "),e("code",null,"getValue"),n(" et "),e("code",null,"setValue"),n(".")],-1),g=e("p",null,"Kotlin fournit des délégués standard tels que des propriétés paresseuses et des propriétés observables.",-1),I={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGtvdGxpbi5yYW5kb20uUmFuZG9tXG5pbXBvcnQga290bGluLnByb3BlcnRpZXMuRGVsZWdhdGVzXG5pbXBvcnQga290bGluLnJlZmxlY3QuS1Byb3BlcnR5XG5cbmNsYXNzIFN1cGVySGVybyB7XG4gICAgdmFsIHJhbmRvbVN0cmVuZ3RoOiBJbnQgYnkgbGF6eSB7XG4gICAgICAgIHByaW50bG4oXCJjb21wdXRlZFwiKVxuICAgICAgICBSYW5kb20ubmV4dEludCg1MCwgMTAwKVxuICAgIH1cbiAgICBcbiAgICB2YXIgcG93ZXJOYW1lOiBTdHJpbmcgYnkgRGVsZWdhdGVzLm9ic2VydmFibGUoXCJMYXNlclwiKSB7IF8sIG9sZCwgbmV3IC0+XG4gICAgICAgIHByaW50bG4oXCJDaGFuZ2VkIHBvd2VyIGZyb20gJG9sZCB0byAkbmV3XCIpXG4gICAgfVxuICAgIFxuICAgIHZhciBjb2RlTmFtZTogU3RyaW5nIGJ5IENhcGl0YWxpemVEZWxlZ2F0ZSgpXG59XG5cbmNsYXNzIENhcGl0YWxpemVEZWxlZ2F0ZXtcbiAgICB2YXIgdXBwZXJjYXNlZCA9IFwiXCJcbiAgICBvcGVyYXRvciBmdW4gZ2V0VmFsdWUodGhpc1JlZjogQW55PywgcHJvcGVydHk6IEtQcm9wZXJ0eTwqPik6IFN0cmluZyB7XG4gICAgICAgIHByaW50bG4oXCIkdGhpc1JlZiwgdGhhbmsgeW91IGZvciBkZWxlZ2F0aW5nICcke3Byb3BlcnR5Lm5hbWV9JyB0byBtZSFcIilcbiAgICAgICAgcmV0dXJuIHVwcGVyY2FzZWRcbiAgICB9XG5cbiAgICBvcGVyYXRvciBmdW4gc2V0VmFsdWUodGhpc1JlZjogQW55PywgcHJvcGVydHk6IEtQcm9wZXJ0eTwqPiwgdmFsdWU6IFN0cmluZykge1xuICAgICAgICBwcmludGxuKFwiJyR2YWx1ZScgaGFzIGJlZW4gYXNzaWduZWQgdG8gJyR7cHJvcGVydHkubmFtZX0nIGluICR0aGlzUmVmLlwiKVxuICAgICAgICB1cHBlcmNhc2VkID0gdmFsdWUudXBwZXJjYXNlKClcbiAgICB9XG59XG5cbmZ1biBtYWluKCkge1xuICAgdmFsIHN1cGVyTGFuZGVyID0gU3VwZXJIZXJvKClcbiAgIHByaW50bG4oc3VwZXJMYW5kZXIucmFuZG9tU3RyZW5ndGgpXG4gICBwcmludGxuKHN1cGVyTGFuZGVyLnJhbmRvbVN0cmVuZ3RoKVxuICAgXG4gICBzdXBlckxhbmRlci5wb3dlck5hbWUgPSBcInNwZWVkXCJcbiAgICBcbiAgICBzdXBlckxhbmRlci5jb2RlTmFtZSA9IFwic3VwZXJsYW5kZXJcIlxuICAgIHByaW50bG4oc3VwZXJMYW5kZXIuY29kZU5hbWUpXG59In0=",target:"_blank",rel:"noopener noreferrer"},b=a('Kotlin fournit un modèle de concurrence de haut niveau appelé Coroutines. Le développeur peut déléguer la gestion des threads au compilateur et à l'exécution et utiliser des constructions de niveau supérieur aux threads pour exprimer des opérations asynchrones.
Les coroutines de Kotlin tournent autour de ces concepts :
- Une coroutine est une instance de calcul suspendable.
- Kotlin a de nombreuses méthodes pour créer une coroutine telle que
launch
.
- Une coroutine doit exister dans une portée de coroutine.
- Par exemple,
runBlocking
crée une portée de coroutine dans laquelle les coroutines peuvent être lancées.
- Une coroutine peut exécuter des fonctions de suspension qui peuvent suspendre la coroutine mais ne bloquent pas le thread.
- Par exemple : le
delay
suspend la coroutine mais ne bloque pas le thread sur lequel elle s'exécute. - Les fonctions de suspension sont des opérations qui peuvent prendre du temps telles que les requêtes http et les appels au système de fichiers.
- Le qualificateur
suspend
définit une fonction de suspension. Il s'exécute dans une coroutine et peut appeler d'autres fonctions de suspension. Flow
permet de générer une liste de valeurs asynchrones.Deferred
et Channel
transfèrent respectivement une valeur unique et un flux de valeurs entre coroutines.
',4),m={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGtvdGxpbnguY29yb3V0aW5lcy4qXG5cbnN1c3BlbmQgZnVuIHJ1blNvbWV0aGluZygpIHtcbiAgICBwcmludGxuKFwic3RhcnRpbmdcIilcbiAgICBkZWxheSg1MDApXG4gICAgcHJpbnRsbihcImZpbmlzaGVkXCIpXG59XG5cbi8vIHJ1bkJsb2NraW5nIGNyZWF0ZXMgYSBDb3JvdXRpbmVTY29wZVxuZnVuIG1haW4oKSA9IHJ1bkJsb2NraW5nIHsgXG4gICAgLy8gbGF1bmNoIGEgbmV3IGNvcm91dGluZVxuICAgIGxhdW5jaCB7IFxuICAgICAgICAvLyBTdXNwZW5kIHRoZSBjb3JvdXRpbmUgYnV0IGRvZXMgbm90IGJsb2NrIHRoZSB0aHJlYWRcbiAgICAgICAgZGVsYXkoMTAwMEwpIFxuICAgICAgICBwcmludGxuKFwiV29ybGQhXCIpIFxuICAgIH1cbiAgICAgICAgXG4gICAgLy8gbWFpbiBjb3JvdXRpbmUgY29udGludWVzIHdoaWxlIGEgcHJldmlvdXMgb25lIGlzIGRlbGF5ZWRcbiAgICBwcmludGxuKFwiSGVsbG9cIikgXG4gICAgXG4gICAgdmFsIGpvYiA9IGxhdW5jaCB7XG4gICAgICAgIHJ1blNvbWV0aGluZygpXG4gICAgfVxuICAgIGpvYi5qb2luKClcbiAgICBydW5Tb21ldGhpbmcoKVxuICAgIFxufSJ9",target:"_blank",rel:"noopener noreferrer"},C={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGtvdGxpbnguY29yb3V0aW5lcy4qXG5pbXBvcnQga290bGlueC5jb3JvdXRpbmVzLmZsb3cuKlxuXG5mdW4gZ2VucmF0ZUFzeW5jUmFuZG9tVmFsdWVzKCk6IEZsb3c8SW50PiA9IGZsb3cgeyBcbiAgICBwcmludGxuKFwiRmxvdyBzdGFydGVkXCIpXG4gICAgZm9yIChpIGluIDAuLjEwKSB7XG4gICAgICAgIGRlbGF5KDEwMClcbiAgICAgICAgZW1pdChpKVxuICAgIH1cbn1cblxuLy8gVGhpcyBjYW4gY2F1c2UgcHJvYmxlbXMgaW4gbm90IGhhbmRsZWRlZCBjb3JyZWN0bHlcbmZ1biBnZW5yYXRlQXN5bmNSYW5kb21WYWx1ZXNJbmZpbml0ZSgpOiBGbG93PEludD4gPSBmbG93IHsgXG4gICAgcHJpbnRsbihcIkZsb3cgc3RhcnRlZFwiKVxuICAgIHZhciBpID0gMFxuICAgIHdoaWxlICh0cnVlKSB7XG4gICAgICAgIGRlbGF5KDEwMClcbiAgICAgICAgZW1pdChpKVxuICAgICAgICBpICs9IDFcbiAgICB9XG59XG5cblxuXG4vLyBydW5CbG9ja2luZyBjcmVhdGVzIGEgQ29yb3V0aW5lU2NvcGVcbmZ1biBtYWluKCkgPSBydW5CbG9ja2luZzxVbml0PiB7XG4gICAgbGF1bmNoe1xuICAgICAgICBnZW5yYXRlQXN5bmNSYW5kb21WYWx1ZXMoKS5jb2xsZWN0IHtcbiAgICAgICBcdFx0cHJpbnRsbihcIjFzdCBmbG93OiBHb3QgJGl0XCIpXG4gICBcdFx0fVxuICAgIH1cbiAgIFxuICAgbGF1bmNoe1xuICAgICAgIHdpdGhUaW1lb3V0T3JOdWxsKDEwMDApe1xuICAgICAgICAgICBnZW5yYXRlQXN5bmNSYW5kb21WYWx1ZXMoKS5jb2xsZWN0IHtcbiAgICAgICAgICAgcHJpbnRsbihcIjJuZCBmbG93OiBHb3QgJGl0XCIpXG4gICAgICAgICB9XG4gICAgICAgfVxuICAgfVxufSJ9",target:"_blank",rel:"noopener noreferrer"},G={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGtvdGxpbnguY29yb3V0aW5lcy4qXG5pbXBvcnQga290bGlueC5jb3JvdXRpbmVzLmNoYW5uZWxzLipcblxuZnVuIG1haW4oKSA9IHJ1bkJsb2NraW5nIHtcbiAgICBcbiAgICAvLyBhc3luYyBsYXVuY2hlcyBhIGNvcm91dGluZXMgYW5kIHJldHVybnMgYSBEZWZlcnJlZDxJbnQ+XG4gICAgdmFsIGRlZmVycmVkID0gYXN5bmMge1xuICAgICAgICA0MlxuICAgIH1cbiAgICBwcmludGxuKGRlZmVycmVkLmF3YWl0KCkpXG5cbiAgICB2YWwgY2hhbm5lbCA9IENoYW5uZWw8SW50PigpXG4gICAgbGF1bmNoIHtcbiAgICAgICAgLy8gdGhpcyBtaWdodCBiZSBoZWF2eSBDUFUtY29uc3VtaW5nIGNvbXB1dGF0aW9uIG9yIGFzeW5jIGxvZ2ljLCB3ZSdsbCBqdXN0IHNlbmQgZml2ZSBzcXVhcmVzXG4gICAgICAgIGZvciAoeCBpbiAxLi41KSBjaGFubmVsLnNlbmQoeCAqIHgpXG4gICAgfVxuICAgIC8vIGhlcmUgd2UgcHJpbnQgZml2ZSByZWNlaXZlZCBpbnRlZ2VyczpcbiAgICByZXBlYXQoNSkgeyBwcmludGxuKGNoYW5uZWwucmVjZWl2ZSgpKSB9XG4gICAgcHJpbnRsbihcIkRvbmUhXCIpXG5cbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},h=a(`Comme vu précédemment, les extensions de fonctions ajoute du comportement à des classes existantes sans utiliser l'héritage. À l'intérieur de la définition de l'extension de fonction, nous pouvons référencer implicitement le récepteur d'extension (this
).
fun String.countCharacters() = length
+println("hello".countCharacters())
+
Nous pouvons définir cette extension avec une fonction littérale (ou lambda) au lieu d'une fonction classique (déclarée avec fun
).
var extFn: String.() -> Int
+extFn = { length }
+println("hello".extFn())
+println(extFn("hello"))
+
extFn
est une fonction littérale (lambda) qui a accès au récepteur (this
). C'est pourquoi on l'appelle une fonction littérale avec récepteur.
extFn("hello")
ou extFn("hello")
appelle l'extension comme prévu par les fonctions d'extension.
Le type d'une fonction littérale avec récepteur est funName: ReceiverType.(arg1Type, arg2Type, etc.) -> ReturnType
et est appelé avec funName(receiverValue, arg1Value, etc.)
ou receiverValue.funName(arg1Value, etc.) .)
. Cependant, ce n'est pas l'aspect le plus intéressant.
La partie importante est extFn = { length }
qui peut être placée comme argument de fonction dans une fonction d'ordre supérieur. Le développeur qui appelle la fonction d'ordre supérieur doit définir extFn
, qui à son tour a accès au récepteur. Cela permet un style de programmation assez intéressant.
`,9),k={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGtvdGxpbi5yYW5kb20uUmFuZG9tXG5cbmZ1biBtYWluKCkge1xuICAgIC8vIGZuIGlzIGEgZnVuY3Rpb24gbGl0ZXJhbHMgd2l0aCByZWNlaXZlciB0aGF0IHJldHVybnMgYW4gaW50XG4gICAgZnVuIGRvU29tZXRoaW5nV2l0aFJhbmRvbUludChmbjogSW50LigpIC0+IEludCk6IEludCB7XG4gICAgXHR2YWwgcmFuZG9tSW50ID0gUmFuZG9tLm5leHRJbnQoMzIsIDk4KVxuICAgIFx0cmV0dXJuIHJhbmRvbUludC5mbigpXG5cdH1cbiAgICAgXG4gICAgdmFsIHJlc3VsdCA9IGRvU29tZXRoaW5nV2l0aFJhbmRvbUludCB7XG4gICAgICAgIC8vIFRoZSByZWNlaXZlciBpcyBpbXBsaWNpdGx5IGF2YWlsYWJsZSBpbiB0aGlzIGxhbWJkYVxuICAgICAgICBwcmludGxuKFwidGhpczogJHRoaXNcIilcbiAgICAgICAgcHJpbnRsbihcInBsdXM6ICR7cGx1cygxMDApfVwiKVxuICAgICAgICBtaW51cyg1MDApXG4gICAgfVxuICAgIHByaW50bG4oXCJyZXN1bHQgOiAkcmVzdWx0XCIpXG59In0=",target:"_blank",rel:"noopener noreferrer"},Z={href:"https://kotlinlang.org/docs/type-safe-builders.htmlhttps://kotlinlang.org/docs/type-safe-builders.html",target:"_blank",rel:"noopener noreferrer"},X=e("strong",null,"Les Type-safe builders",-1),A={class:"custom-container tip"},x=e("p",{class:"custom-container-title"},"Le pattern monteur (Builder)",-1),W={href:"https://refactoring.guru/design-patterns/builder",target:"_blank",rel:"noopener noreferrer"},v=a(`
+val text = StringBuilder("Temp")
+ .append(1)
+ .append(true)
+ .append("friend")
+ .toString()
+
`,1),B={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiY2xhc3MgTWlsa3tcbiAgICB2YXIgYnJhbmQ6IFN0cmluZyA9IFwiXCJcbiAgICB2YXIgcXVhbnRpdHk6IEludCA9IDBcbiAgICBmdW4gYnJhbmQoaGFuZGxlcjogTWlsay4oKS0+IFN0cmluZyl7XG4gICAgICAgIGJyYW5kID0gaGFuZGxlcigpXG4gICAgfVxuICAgIGZ1biBxdWFudGl0eShoYW5kbGVyOiBNaWxrLigpLT5JbnQpe1xuICAgICAgICBxdWFudGl0eSA9IGhhbmRsZXIoKVxuICAgIH1cbiAgICBvdmVycmlkZSBmdW4gdG9TdHJpbmcoKSA9IFwiJHF1YW50aXR5IEwgb2YgbWlsayBmcm9tICRicmFuZFwiXG59XG5cbi8vIHR5cGVzYWZlIGJ1aWxkZXIgZW50cnkgcG9pbnRcbmZ1biBwcm9kdWNlTWlsayhpbml0OiBNaWxrLigpIC0+IFVuaXQpOiBNaWxrIHtcbiAgICB2YWwgbWlsayA9IE1pbGsoKVxuICAgIG1pbGsuaW5pdCgpXG4gICAgcmV0dXJuIG1pbGtcbn1cblxuZnVuIG1haW4oKXtcbiAgICB2YWwgbWlsayA9IHByb2R1Y2VNaWxrIHtcbiAgICAgICAgYnJhbmQgeyBcIlN1cGVyIG1pbGtcIiB9XG4gICAgICAgIHF1YW50aXR5IHsgMTAwIH1cbiAgICB9XG4gICAgcHJpbnRsbihtaWxrKVxufSJ9",target:"_blank",rel:"noopener noreferrer"},V={href:"https://kotlinlang.org/docs/type-safe-builders.html",target:"_blank",rel:"noopener noreferrer"},y=a('- open the java-integration-exercise projects in the materials folder.
- Have a look at the Java class we provided you in the
src/main/java/com/worldline/learning/kotlin/java2kotlin
package. (yes, that's the Pokemon class) - Convert that Java class in Kotlin using IntelliJ's awesome copy-pasta tool! (just copy paste the java code in a kotlin file, one is provided at
src/main/kotlin/com/worldline/learning/kotlin/java2kotlin
) - Have a look at the generated Kotlin code, and note the major differences you spot!
',3);function J(f,F){const s=l("ExternalLinkIcon");return i(),o("div",null,[r,p,d,g,e("p",null,[e("a",I,[n("▶️ this code"),t(s)]),n(" illustrates delegate properties.")]),b,e("p",null,[e("a",m,[n("▶️ this code"),t(s)]),n(" show how to create a coroutine and suspend function and how to use them.")]),e("p",null,[e("a",C,[n("▶️ this code"),t(s)]),n(" illustrated flows.")]),e("p",null,[e("a",G,[n("▶️ this code"),t(s)]),n(" illustrates channels and deferred.")]),h,e("p",null,[e("a",k,[n("▶️ ce code"),t(s)]),n(" montre un example.")]),e("p",null,[e("a",Z,[X,t(s)]),n(" combinent les monteurs bien nommées et les fonctions littérales avec récepteur pour créer des monteur avec un typage statique et sécurisé. La syntaxe particulière possible avec technique permet de définir une sorte de sous-langage aussi appelé DSL (domain specific language).")]),e("div",A,[x,e("p",null,[n("Est une technique permettant "),e("a",W,[n("de construire des objects avec une syntaxe élégante"),t(s)]),n(".")]),v]),e("p",null,[e("a",B,[n("Ce code montre"),t(s)]),n(" un type-safe builder basique.")]),e("p",null,[e("a",V,[n("Kotlin docs"),t(s)]),n(" fournit un exemple plus avancé d'un monteur de documents HTML.")]),y])}const w=c(u,[["render",J],["__file","index.html.vue"]]),R=JSON.parse('{"path":"/fr/kotlin-features-advanced/","title":"📚 Fonctionnalités avancées de Kotlin","lang":"fr-FR","frontmatter":{},"headers":[{"level":2,"title":"Propriétés déléguées","slug":"proprietes-deleguees","link":"#proprietes-deleguees","children":[]},{"level":2,"title":"Concurrence et coroutines","slug":"concurrence-et-coroutines","link":"#concurrence-et-coroutines","children":[]},{"level":2,"title":"Littéral de fonction avec récepteur et constructeurs de type sécurisé","slug":"litteral-de-fonction-avec-recepteur-et-constructeurs-de-type-securise","link":"#litteral-de-fonction-avec-recepteur-et-constructeurs-de-type-securise","children":[]},{"level":2,"title":"Exercises","slug":"exercises","link":"#exercises","children":[{"level":3,"title":"Exercise 1","slug":"exercise-1","link":"#exercise-1","children":[]}]}],"git":{"updatedTime":1696262637000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":2},{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":1}]},"filePathRelative":"fr/kotlin-features-advanced/README.md"}');export{w as comp,R as data};
diff --git a/assets/index.html-BEuoD-gh.js b/assets/index.html-BEuoD-gh.js
new file mode 100644
index 00000000..294bb0aa
--- /dev/null
+++ b/assets/index.html-BEuoD-gh.js
@@ -0,0 +1 @@
+import{_ as i}from"./kmp_codelab-CiTPMWjt.js";import{_ as s,r,o as a,c as l,a as e,b as n,d as o}from"./app-jR2rC7Ae.js";const c={},m=e("h1",{id:"🛠-construisons-une-app-multiplateforme",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#🛠-construisons-une-app-multiplateforme"},[e("span",null,"🛠 Construisons une app multiplateforme !")])],-1),d=e("p",null,"En combinant KMP, KMM et Compose, il est possible de développer des applications mobiles et de bureau multiplateformes en utilisant uniquement Kotlin.",-1),p=e("h4",{id:"prerequis",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#prerequis"},[e("span",null,"Prérequis")])],-1),u=e("li",null,"Connaissance de base du développement en Kotlin (notamment la nullabilité, les fonctions en ligne et les fonctions lambda)",-1),_={href:"https://developer.android.com/studio",target:"_blank",rel:"noopener noreferrer"},h=e("strong",null,"Giraffe",-1),f=e("li",null,"Une bonne connectivité",-1),b={class:"custom-container tip"},g=e("p",{class:"custom-container-title"},"TIP",-1),v={href:"https://kotlinlang.org/docs/multiplatform-mobile-setup.html#next-step",target:"_blank",rel:"noopener noreferrer"},k={id:"🚀-demarrer-la-session-pratique-ici-🚀",tabindex:"-1"},x={class:"header-anchor",href:"#🚀-demarrer-la-session-pratique-ici-🚀"},E={href:"https://worldline.github.io/learning-kotlin-multiplatform/",target:"_blank",rel:"noopener noreferrer"},q={href:"https://worldline.github.io/learning-kotlin-multiplatform/",target:"_blank",rel:"noopener noreferrer"},B=e("img",{src:i,alt:"kmp codelab",tabindex:"0",loading:"lazy"},null,-1),C=e("figcaption",null,"kmp codelab",-1);function I(P,w){const t=r("ExternalLinkIcon");return a(),l("div",null,[m,d,p,e("ul",null,[u,e("li",null,[e("a",_,[n("IDE Android Studio"),o(t)]),n(" avec la version stable la plus récente, version "),h,n(" ou supérieure")]),f]),e("div",b,[g,e("p",null,[n("Pour plus d'informations sur votre environnement de développement (DEV) et les installations, veuillez consulter la documentation liée à JetBrains "),e("a",v,[n("ici"),o(t)]),n(".")])]),e("h1",k,[e("a",x,[e("span",null,[e("a",E,[n(" ____ 🚀 Demarrer la session pratique ici 🚀___"),o(t)])])])]),e("figure",null,[e("a",q,[B,o(t)]),C])])}const M=s(c,[["render",I],["__file","index.html.vue"]]),N=JSON.parse('{"path":"/fr/other-technologies/","title":"🛠 Construisons une app multiplateforme !","lang":"fr-FR","frontmatter":{},"headers":[],"git":{"updatedTime":1696262637000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":4},{"name":"Ibrahim Gharbi","email":"brah.gharbi@gmail.com","commits":2},{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":1}]},"filePathRelative":"fr/other-technologies/README.md"}');export{M as comp,N as data};
diff --git a/assets/index.html-BGxKf9ec.js b/assets/index.html-BGxKf9ec.js
new file mode 100644
index 00000000..410dbcbd
--- /dev/null
+++ b/assets/index.html-BGxKf9ec.js
@@ -0,0 +1,35 @@
+import{_ as o}from"./kotlin-wasm-webapp-R4_9ho9v.js";import{_ as l,r as i,o as p,c as r,a as e,b as n,d as a,e as t}from"./app-jR2rC7Ae.js";const u="/learning-kotlin/assets/kotlin-wasm-flag-BKaaN9Pq.png",c="/learning-kotlin/assets/wasm-build-conf-edit-CmamvRv7.png",d="/learning-kotlin/assets/wasm-run-configuration-x_w9-EC1.png",m="/learning-kotlin/assets/compose-multiplaform-web-wH6XfCHb.gif",k="/learning-kotlin/assets/compose-multiplaform-Dfyu_rxB.gif",b={},f=e("h1",{id:"📚-developpement-frontend",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#📚-developpement-frontend"},[e("span",null,"📚 Développement frontend")])],-1),h=e("p",null,"Kotlin supporte une large sélection de frameworks frontaux sur toutes les plateformes : mobile, desktop et web. Vous trouverez ci-dessous un aperçu des possibilités que vous pouvez faire directement à partir d'IntelliJ :",-1),v={href:"https://openjfx.io/",target:"_blank",rel:"noopener noreferrer"},g={href:"https://tornadofx.io/",target:"_blank",rel:"noopener noreferrer"},_={href:"https://www.jetbrains.com/lp/compose-mpp/",target:"_blank",rel:"noopener noreferrer"},w={href:"https://ktor.io/docs/creating-interactive-website.html",target:"_blank",rel:"noopener noreferrer"},x={href:"https://freemarker.apache.org/",target:"_blank",rel:"noopener noreferrer"},C=e("li",null,"Avec KotlinJS, les développeurs peuvent créer des applications React, nodsjs ou vanilla JS en utilisant Kotlin.",-1),S=e("li",null,"Kotlin WASM se compile en Web Assembly. Il peut compléter KotlinJS pour les tâches à forte intensité de calcul.",-1),M={href:"https://www.jetbrains.com/lp/compose-mpp/",target:"_blank",rel:"noopener noreferrer"},K={href:"https://developer.android.com/jetpack/compose",target:"_blank",rel:"noopener noreferrer"},q=e("strong",null,"layout XML",-1),A={href:"https://www.jetbrains.com/lp/compose-mpp/",target:"_blank",rel:"noopener noreferrer"},y=e("p",null,[n("Comme nous pouvons le voir, Kotlin propose plusieurs options. L'option la plus séduisante en terme de partage de code est Compose Multiplatform. Ceci est possible notamment grâce à "),e("strong",null,"KMP")],-1),z=e("h2",{id:"kmp",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#kmp"},[e("span",null,"KMP")])],-1),j=e("li",null,"KMP (Kotlin Multiplatform) permet de partager une base de code unique sur plusieurs cibles.",-1),W={href:"https://blog.jetbrains.com/kotlin/2021/08/compose-multiplatform-goes-alpha/",target:"_blank",rel:"noopener noreferrer"},J=e("figure",null,[e("img",{src:"https://kotlinlang.org/docs/images/kotlin-multiplatform.png",alt:"KMP",tabindex:"0",loading:"lazy"}),e("figcaption",null,"KMP")],-1),I={href:"https://kotlinlang.org/docs/multiplatform-full-stack-app.html",target:"_blank",rel:"noopener noreferrer"},D={href:"https://kotlinlang.org/docs/multiplatform-library.html",target:"_blank",rel:"noopener noreferrer"},P=e("li",null,"KMM est une ancienne dénomination qui est dépréciée.",-1),B=t('Dans la suite de ce chapitre, nous explorerons les différentes possibilités individuellement et on fera un projet KMP dans le chapitre suivant.
- Kotlin/JS peut également cibler le web et même utiliser des frameworks web (tels que react) dans Kolitn.
- Kotlin WASM est une autre possibilité de cibler le web, mais il génère WASM au lieu de code JS pur.
- Il peut être utilisé par exemple pour développer des bibliothèques à forte intensité de calcul.
- Nous pourrons peut-être faire encore plus à l'avenir grâce à l'évolution de toutes ces technologies (Kotlin, WASM et Kotlin/WASM). - Par exemple, [WASI] (https://wasi.dev/) permet à WASM de communiquer avec le système d'exploitation. - Cela signifie que je pourrais voir des projets Kotlin/WASM à l'avenir qui peuvent cibler à la fois le navigateur et le système d'exploitation.
- Continuons à observer 😄.
',4),L=t("Les assistants de création de projet Kotlin/WASM et Kotlin/JS sur IntelliJ fonctionnent de manière similaire: - L'IDE génère un fichier Kotlin qui sera compilé par la suite en WASM et/ou JS. Kotlin/JS ne génère que du JS tandis que Kotin/WASM génère à la fois du JS et du WASM.
- Dans les deux cas, le point d'entrée du code généré est un fichier JS appelé nom_du_module.js.
- L'IDE génère également dans le dossier des ressources un fichier index.html dont le but est de charger le JS généré (le fichier nom_du_module.js).
- La tâche
wasmBrowserDevelopmentRun
ou jsWasmBrowserDevelopmentRun
lancera un serveur local qui hébergera à la fois les fichiers index.html et les fichiers JS et WASM générés.
",1),N=e("strong",null,"kotlin.wasm.wizard",-1),T={href:"https://github.com/worldline/learning-kotlin/tree/main/material/kotlin-wasm-starter",target:"_blank",rel:"noopener noreferrer"},E=t('
- Vérifiez qu'on est sur la dernière version de Kotlin dans build.gradle.kts (l'assistant peut le configurer à une version antérieure).
- Ouvrez src/wasmMain/kotlin/sample.kt et cliquez sur le bouton lancer qui apparaît à côté de la fonction
main
. - Si la compilation échoue parce que l'IDE a utilisé la mauvaise tâche gradle, veuillez la changer en
wasmBrowserDevelopmentRun
et essayez de l'exécuter à nouveau.


',3),O={href:"http://localhost:8080/",target:"_blank",rel:"noopener noreferrer"},R=e("li",null,"⚠️ Il se peut que vous deviez activer certains drapeaux sur votre navigateur pour que l'application fonctionne. Si vous voyez une page blanche, veuillez lire les journaux du navigateur pour vérifier les instructions.",-1),V=e("figure",null,[e("img",{src:o,alt:"",tabindex:"0",loading:"lazy"}),e("figcaption")],-1),F=e("li",null,[n("Le fichier wasm généré est disponible dans "),e("strong",null,"build/js/packages/nom_du_projet/kotlin")],-1),G=e("li",null,[n("Nous pouvons soit installer [WABT (The WebAssembly Binary Toolkit ou wabbit)] (https://github.com/WebAssembly/wabt) et utiliser l'outil "),e("em",null,"wasm2wattool"),n(),e("code",null,"wasm2wat --enable-all -v .\\kotlin-wasm-demo-wasm.wasm -o wasm.wat"),n(",")],-1),X={href:"https://webassembly.github.io/wabt/demo/wasm2wat/",target:"_blank",rel:"noopener noreferrer"},H=e("li",null,"❗ Cependant, je n'ai pas réussi à le faire fonctionner",-1),U=e("h3",{id:"🧪-application-web-kotlinjs",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#🧪-application-web-kotlinjs"},[e("span",null,"🧪 Application web KotlinJS")])],-1),Y=e("p",null,"L'assistant Kotlin/JS crée une application très similaire à celle de Kotlin/WASM. Dans un prochain PW, nous créerons une application complète avec Ktor et Kotlin/JS.",-1),$=e("h2",{id:"compose",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#compose"},[e("span",null,"Compose")])],-1),Q={href:"https://blog.jetbrains.com/kotlin/2021/08/compose-multiplatform-goes-alpha/",target:"_blank",rel:"noopener noreferrer"},Z={class:"custom-container warning"},ee=e("p",{class:"custom-container-title"},"Compose multiplatform vs Jetpack Compose",-1),ne={href:"https://developer.android.com/jetpack/compose/tutorial",target:"_blank",rel:"noopener noreferrer"},se=t(`Compose Web vs Compose for Web Canvas
- La surface de l'API de Compose Web est différente des autres cibles de Compose car elle travaille directement avec le DOM.
- Compose for Web Canvas a la même surface d'API que celle du Desktop, Android et iOS car il dessine sur un Canvas et ne manipule pas le DOM.
Cela signifie que le premier a un meilleur support web et que le second a plus de code réutilisable.
- Créez un nouveau projet IntelliJ -> Compose Multiplaform.
- Choisissez "Single platform" -> "Web" et remplissez les autres champs.
- Choisissez Finish
- IntelliJ peut prendre un certain temps pour préparer le projet et peut demander d'installer des plugins supplémentaires.
- Lancez le serveur de développement de l'application web à l'aide de la commande
./gradlew jsBrowserRun --continuous
. - Modifiez
Main.kt
comme suit et lancez l'application. - Ouvrez cette adresse :
localhost:8080
.
fun main() {
+ renderComposable(rootElementId = "root") {
+ Div({ style { padding(25.px) } }) {
+ var expanded by remember { mutableStateOf(false) }
+ Button(
+ attrs = {
+ onClick { expanded = !expanded }
+ }
+ ) { Text("Cliquez sur moi") }
+ Div({ style { display(if (expanded) DisplayStyle.Block else DisplayStyle.None) } }) {
+ Text("Cliquez sur moi !")
+ }
+ }
+ }
+}
+
compose multiplatform demo- Créez un nouveau projet sur IntelliJ -> Compose Multiplatform.
- Choisissez "multiple platforms" et remplissez les autres champs. Choisissez ensuite Finish.
- IntelliJ commence à préparer le projet et peut demander l'installation de plugins.
- Une fois le projet prêt, lancez l'application Android en utilisant le bouton vert run.
- Lancez l'application desktop en exécutant la fonction principale du projet desktop (qui devrait se trouver dans
Main.kt
). - Modifiez
App.kt
dans le projet principal comme suit et lancez l'application.
@Composable
+fun App() {
+ val platformName = getPlatformName()
+ Card {
+ var expanded by remember { mutableStateOf(false) }
+ Column(Modifier.clickable { expanded = !expanded }) {
+ Texte(
+ text="Cliquez sur moi !",
+ style = MaterialTheme.typography.h2
+ )
+ AnimatedVisibility(expanded){
+ Texte(
+ text = "Bonjour, \${platformName} 🎊",
+ style = MaterialTheme.typography.h1
+ )
+ }
+ }
+ }
+}
+
compose multiplatform demo',10),ae={href:"https://seb.deleuze.fr/the-huge-potential-of-kotlin-wasm/",target:"_blank",rel:"noopener noreferrer"};function te(oe,le){const s=i("ExternalLinkIcon");return p(),r("div",null,[f,h,e("ul",null,[e("li",null,[n("Côté bureau "),e("ul",null,[e("li",null,[n("Grâce au support de la JVM, Kotlin supporte "),e("a",v,[n("JavaFX"),a(s)]),n(". 💡 Il existe même un équivalent en Kotlin appelé "),e("a",g,[n("tornadofx"),a(s)]),n(".")]),e("li",null,[e("a",_,[n("Compose Multiplatform"),a(s)]),n(" apporte l'API Jetpack Compose sur le bureau, le web et le mobile.")])])]),e("li",null,[n("Sur le web "),e("ul",null,[e("li",null,[e("a",w,[n("Ktor"),a(s)]),n(" peut utiliser des moteurs de modèles "),e("a",x,[n("tels que FreeMarker"),a(s)]),n(" pour créer des pages de serveur.")]),C,S,e("li",null,[e("a",M,[n("Compose Multiplatform"),a(s)]),n(" apporte deux options sur le web: Compose web et Compose for Web Canvas.")])])]),e("li",null,[n("Sur les mobiles "),e("ul",null,[e("li",null,[n("Les développeurs Android utilisent "),e("a",K,[n("Jetpack Compose"),a(s)]),n(" ou l'ancienne méthode de "),q,n(".")]),e("li",null,[e("a",A,[n("Compose Multiplatform"),a(s)]),n(" supporte Android de façon stable et iOS de façon expérimentale.")])])])]),y,z,e("ul",null,[j,e("li",null,[e("a",W,[n("KMP"),a(s)]),n(" s'appuie sur Kotlin native et d'autres fonctionnalités de Kotlin pour aider les développeurs à créer des projets destinés à plusieurs plates-formes en utilisant une base de code Kotlin commune.")])]),J,e("ul",null,[e("li",null,[n("De nombreuses combinaisons de cibles et de cas d'utilisation sont possibles : "),e("ul",null,[e("li",null,[e("a",I,[n("Full-Stack web apps"),a(s)]),n(" : Un projet qui contient un backend et une application web tout en partageant une logique commune.")]),e("li",null,[e("a",D,[n("Bibliothèques multiplateformes"),a(s)])])])]),P]),B,e("ul",null,[L,e("li",null,[n("Créons une application Kotlin/WASM. Tout d'abord, activez l'assistant Kotlin/WASM en activant "),N,n(` dans le registre d'IntelliJ (ouvrez le registre en appuyant deux fois sur shift et en tapant "registry" dans la boîte de recherche). Alternativement, clonez `),e("a",T,[n("ce projet"),a(s)]),n(".")])]),E,e("ul",null,[e("li",null,[n("Le serveur de développement devrait démarrer et vous pouvez ouvrir votre application web sur "),e("a",O,[n("http://localhost:8080/"),a(s)])]),R]),V,e("ul",null,[F,e("li",null,[n("WASM étant un format binaire, nous devons d'abord le convertir au format texte. "),e("ul",null,[G,e("li",null,[n("ou utiliser un convertisseur en ligne "),e("a",X,[n("comme celui-ci"),a(s)])]),H])])]),U,Y,$,e("p",null,[e("a",Q,[n("Compose multiplatform"),a(s)]),n(" est une famille de frameworks d'interface utilisateur déclaratifs pour Android (Jetpack Compose), le bureau (Compose Desktop) et le web (Compose Web). Il dispose d'un support expérimental pour iOS et Web Canvas.")]),e("div",Z,[ee,e("p",null,[n("Bien que très similaire, Compose multiplatform est différent de Jetpack Compose car ce dernier n'est compatible qu'avec Android. Google fournit un "),e("a",ne,[n("JetPack compose tutorial"),a(s)]),n(" pour le développement Android.")])]),se,e("ul",null,[e("li",null,[e("a",ae,[n("L'énorme potentiel de Kotlin/Wasm"),a(s)])])])])}const re=l(b,[["render",te],["__file","index.html.vue"]]),ue=JSON.parse('{"path":"/fr/front-development/","title":"📚 Développement frontend","lang":"fr-FR","frontmatter":{},"headers":[{"level":2,"title":"KMP","slug":"kmp","link":"#kmp","children":[]},{"level":2,"title":"Kotlin/JS et Kotlin/WASM","slug":"kotlin-js-et-kotlin-wasm","link":"#kotlin-js-et-kotlin-wasm","children":[{"level":3,"title":"🧪 Application web Kotlin/WASM","slug":"🧪-application-web-kotlin-wasm","link":"#🧪-application-web-kotlin-wasm","children":[]},{"level":3,"title":"🧪 Application web KotlinJS","slug":"🧪-application-web-kotlinjs","link":"#🧪-application-web-kotlinjs","children":[]}]},{"level":2,"title":"Compose","slug":"compose","link":"#compose","children":[{"level":3,"title":"🧪 Compose Web","slug":"🧪-compose-web","link":"#🧪-compose-web","children":[]},{"level":3,"title":"🧪 Compose desktop + Android app","slug":"🧪-compose-desktop-android-app","link":"#🧪-compose-desktop-android-app","children":[]}]},{"level":2,"title":"Pour aller plus loin","slug":"pour-aller-plus-loin","link":"#pour-aller-plus-loin","children":[]}],"git":{"updatedTime":1696263241000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":5},{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":2},{"name":"Ibrahim Gharbi","email":"brah.gharbi@gmail.com","commits":1}]},"filePathRelative":"fr/front-development/README.md"}');export{re as comp,ue as data};
diff --git a/assets/index.html-BOBxFfL8.js b/assets/index.html-BOBxFfL8.js
new file mode 100644
index 00000000..2fc8f3dc
--- /dev/null
+++ b/assets/index.html-BOBxFfL8.js
@@ -0,0 +1 @@
+import{_ as l}from"./kotlin-used-for-Bdlavnqs.js";import{_ as r,r as s,o as a,c as h,a as e,b as t,d as o,e as i}from"./app-jR2rC7Ae.js";const d="/learning-kotlin/assets/kotlin-decision-tree-4i7nEr1Z.svg",u={},c=e("h1",{id:"🚀-presentation-of-kotlin",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#🚀-presentation-of-kotlin"},[e("span",null,"🚀 Presentation of Kotlin")])],-1),p=e("p",null,"Kotlin is a modern programming language developed by JetBrains.",-1),m=e("h2",{id:"some-features",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#some-features"},[e("span",null,"Some features")])],-1),f=e("li",null,"Many server frameworks officially support Kotlin such as Spring and Quarkus.",-1),g=e("li",null,"It is the first-class language for writing Android.",-1),_={href:"https://www.graalvm.org/",target:"_blank",rel:"noopener noreferrer"},b=e("li",null,"KMP allows to share code between different platforms.",-1),k=e("li",null,[t("Kotlin can be considered as both "),e("em",null,"fullstack"),t(" and "),e("em",null,"corss-platform"),t(" language.")],-1),w=e("li",null,"Inter-operable with Java.",-1),y=e("li",null,"Provides modern features such as compile-time null safety and data classes.",-1),v=e("li",null,"Supports Object oriented programming and functional programming.",-1),K={href:"https://github.com/Kotlin/KEEP",target:"_blank",rel:"noopener noreferrer"},S=e("h2",{id:"history",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#history"},[e("span",null,"History")])],-1),x=e("ul",null,[e("li",null,"February 15, 2016: Kotlin 1.0 was released."),e("li",null,"January 04, 2017: Spring introduced Kotlin support in Spring 5."),e("li",null,"At Google I/O 2017, Google announced first-class support for Kotlin on Android."),e("li",null,"On 7 May 2019, Google announced that Kotlin the preferred language for Android app developers."),e("li",null,"June 2022: Kotlin 1.7 was released with the version of the new K2 compiler.")],-1),J=e("h2",{id:"some-numbers-and-facts",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#some-numbers-and-facts"},[e("span",null,"Some numbers and facts")])],-1),A={href:"https://medium.com/predict/8-stats-and-fact-you-should-know-about-kotlin-in-2021-b41133a1834",target:"_blank",rel:"noopener noreferrer"},P=e("ul",null,[e("li",null,"As of 2022, Kotlin is used by 7.8% of the industry experts."),e("li",null,"According to StackOverflow, Kotlin was the 4th most beloved language in 2020 with 62.9% votes."),e("li",null,"Kotlin is now listed among the top 20 programming languages by Redmonk."),e("li",null,"Kotlin is among the top 3 languages that most businesses are planning to migrate their apps to in 2022."),e("li",null,"Kotlin is used by global companies like Google, NetFlix, Amazon, Trello, and more."),e("li",null,"Pinterest and Uber are among the companies that migrated their apps to Kotlin from Java.")],-1),E=e("figure",null,[e("img",{src:l,alt:"kotlin used for what?",tabindex:"0",loading:"lazy"}),e("figcaption",null,"kotlin used for what?")],-1),I=e("p",null,"Please find more statics here:",-1),j={href:"https://www.jetbrains.com/lp/kotlin-census-2020/",target:"_blank",rel:"noopener noreferrer"},O={href:"https://www.jetbrains.com/lp/devecosystem-2021/kotlin/",target:"_blank",rel:"noopener noreferrer"},B=i('Here are some arguments that motivate switching from Java (version 17 LTS at the time of writing) to Kotlin.
- Kotlin supports more targets than Java.
- Kotlin has compile time null-safety (Java Optionals are runtime wrappers for nullable values and null annotations have less features).
- Kotlin strings support interpolation.
- Casting in Kotlin is smart.
- Kotlin functional programming features are better. It even allows to define Type-Safe builders and DSLs.
- Kotlin can be mixed with Java code, thus helping the migration process.
You can read more arguments in these articles:
',4),V={href:"https://codersera.com/blog/java-vs-kotlin/",target:"_blank",rel:"noopener noreferrer"},M={href:"https://www.geeksforgeeks.org/8-reasons-why-you-should-switch-to-kotlin-from-java",target:"_blank",rel:"noopener noreferrer"},T=i('
decision tree',3),W=e("li",null,[e("em",null,"Recommended"),t(": IntelliJ IDEA (Both community or ultimate are OK),")],-1),q=e("em",null,"or",-1),D={href:"https://gitpod.io/#https://github.com/worldline/learning-kotlin",target:"_blank",rel:"noopener noreferrer"},F=e("em",null,"material",-1),G=e("li",null,[e("em",null,"or"),t(" VSCode (with Kotlin and Java plugins) + JDK 17 (install via scoop.sh on windows or SDK man on Unix) + gradle")],-1),N=e("h2",{id:"📖-further-reading",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#📖-further-reading"},[e("span",null,"📖 Further reading")])],-1),L={href:"https://en.wikipedia.org/wiki/Kotlin_(programming_language)",target:"_blank",rel:"noopener noreferrer"},R={href:"https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0",target:"_blank",rel:"noopener noreferrer"},C={href:"https://medium.com/predict/8-stats-and-fact-you-should-know-about-kotlin-in-2021-b41133a1834",target:"_blank",rel:"noopener noreferrer"},z={href:"https://dev.to/aashiya123/why-should-you-learn-kotlin-in-2021-57e2",target:"_blank",rel:"noopener noreferrer"},H={href:"https://kotlin.link/",target:"_blank",rel:"noopener noreferrer"},U={href:"https://kotlin.libhunt.com/",target:"_blank",rel:"noopener noreferrer"};function Y(Q,Z){const n=s("ExternalLinkIcon");return a(),h("div",null,[c,p,m,e("ul",null,[e("li",null,[t("Kotlin compiles to many targets: the JVM, JS, WASM, Android, iOS, Desktop, native code, etc. "),e("ul",null,[f,g,e("li",null,[t("Kotlin can compile to native with Kotlin native or with "),e("a",_,[t("GraalVM"),o(n)]),t(".")]),b])]),k,w,y,v,e("li",null,[e("a",K,[t("Kotlin KEEP"),o(n)]),t(" is its open source evolution process.")])]),S,x,J,e("p",null,[e("a",A,[t("Source: Amyra Sheldon"),o(n)])]),P,E,I,e("ul",null,[e("li",null,[e("a",j,[t("Kotlin Census 2020"),o(n)])]),e("li",null,[e("a",O,[t("JetBrains' Kotlin state in 2021"),o(n)])])]),B,e("ul",null,[e("li",null,[e("a",V,[t("Java Vs Kotlin: Which One Is Better To Learn In 2022?"),o(n)])]),e("li",null,[e("a",M,[t("8 Reasons Why You Should Switch To Kotlin From Java"),o(n)])])]),T,e("ul",null,[W,e("li",null,[q,t(" GitPod by opening "),e("a",D,[t("https://gitpod.io/#https://github.com/worldline/learning-kotlin"),o(n)]),t(" to create a workspace and then browser to the "),F,t(" folder,")]),G]),N,e("ul",null,[e("li",null,[e("a",L,[t("Kotlin on Wikipedia"),o(n)])]),e("li",null,[e("a",R,[t("Introducing Kotlin support in Spring Framework 5.0"),o(n)])]),e("li",null,[e("a",C,[t("8 stats and facts you should know about Kotlin in 2022"),o(n)])]),e("li",null,[e("a",z,[t("Why should you learn Kotlin in 2022?"),o(n)])]),e("li",null,[e("a",H,[t("Kotlin is awesome"),o(n)])]),e("li",null,[e("a",U,[t("Awesome Kotlin"),o(n)])])])])}const ee=r(u,[["render",Y],["__file","index.html.vue"]]),te=JSON.parse('{"path":"/en/presentation/","title":"🚀 Presentation of Kotlin","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Some features","slug":"some-features","link":"#some-features","children":[]},{"level":2,"title":"History","slug":"history","link":"#history","children":[]},{"level":2,"title":"Some numbers and facts","slug":"some-numbers-and-facts","link":"#some-numbers-and-facts","children":[]},{"level":2,"title":"Why switch from Java to Kotlin","slug":"why-switch-from-java-to-kotlin","link":"#why-switch-from-java-to-kotlin","children":[]},{"level":2,"title":"A decision tree to help you decide if you should use Kotlin","slug":"a-decision-tree-to-help-you-decide-if-you-should-use-kotlin","link":"#a-decision-tree-to-help-you-decide-if-you-should-use-kotlin","children":[]},{"level":2,"title":"Prerequisites","slug":"prerequisites","link":"#prerequisites","children":[]},{"level":2,"title":"📖 Further reading","slug":"📖-further-reading","link":"#📖-further-reading","children":[]}],"git":{"updatedTime":1704373434000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":10},{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":3}]},"filePathRelative":"en/presentation/README.md"}');export{ee as comp,te as data};
diff --git a/assets/index.html-CDMPFPby.js b/assets/index.html-CDMPFPby.js
new file mode 100644
index 00000000..c86407ad
--- /dev/null
+++ b/assets/index.html-CDMPFPby.js
@@ -0,0 +1 @@
+import{_ as r}from"./logo_worldline-dinT9MYm.js";import{_ as o,r as s,o as l,c,a as e,b as t,d as a,e as i}from"./app-jR2rC7Ae.js";const d={},h=i('
avatarWe design payments technology that powers the growth of millions of businesses around the world. Engineering the next frontiers in payments technology
- Leader in payment and secured transactions
- Over 50 billion transactions/year
- 7000+ engineers in over 40 countries
- A huge & diverse tech-stack
',5),p={class:"custom-container tip"},u=e("p",{class:"custom-container-title"},"TIP",-1),m={href:"/fr/",target:"_blank",rel:"noopener noreferrer"},f={href:"/fr/",target:"_blank",rel:"noopener noreferrer"},g=i('- Basic knowledge of object-oriented language like Java
- Prepare your development environment and install stuff before the session (see Tooling section)
',3),_={href:"https://kotlinlang.org/docs/home.html",target:"_blank",rel:"noopener noreferrer"},b={href:"https://github.com/worldline/learning-kotlin",target:"_blank",rel:"noopener noreferrer"};function k(v,w){const n=s("ExternalLinkIcon");return l(),c("div",null,[h,e("div",p,[u,e("p",null,[t("This training is also available in "),e("a",m,[t("French"),a(n)]),t(" / Cette formation est aussi disponible en "),e("a",f,[t("Français"),a(n)])])]),g,e("ul",null,[e("li",null,[e("a",_,[t("Official Kotlin documentation"),a(n)])]),e("li",null,[e("a",b,[t("GitHub repository for this training"),a(n)])])])])}const W=o(d,[["render",k],["__file","index.html.vue"]]),P=JSON.parse(`{"path":"/en/","title":"Welcome","lang":"en-US","frontmatter":{"home":true,"heroImage":"./kotlin_logo.png","tagline":"A beginner's guide to a modern programming language","actions":[{"text":"Get started →","link":"/en/presentation/","type":"primary"}],"features":[{"title":"Language features","details":"null safety, extensions, lambdas, Java interoperability and more"},{"title":"Backend development","details":"With Ktor, spring and NodeJS"},{"title":"Frontend development","details":"Compose multiplatform, Kotlin/JS, Kotlin/WASM and JVM frameworks"},{"title":"Cross-platform development","details":"With KMP and Compose multiplatform"},{"title":"Advanced Kotlin","details":"Coroutines, delegates, Function literal with receiver, DSLs and more"},{"title":"Practical exercises and solutions","details":"All chapters have a set of exercises"}],"footer":"Worldline, 2023"},"headers":[{"level":2,"title":"Who we are","slug":"who-we-are","link":"#who-we-are","children":[]},{"level":2,"title":"Prerequisites","slug":"prerequisites","link":"#prerequisites","children":[]},{"level":2,"title":"Useful links","slug":"useful-links","link":"#useful-links","children":[]}],"git":{"updatedTime":1693839440000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":6}]},"filePathRelative":"en/index.md"}`);export{W as comp,P as data};
diff --git a/assets/index.html-C_I161o0.js b/assets/index.html-C_I161o0.js
new file mode 100644
index 00000000..67ffd5fa
--- /dev/null
+++ b/assets/index.html-C_I161o0.js
@@ -0,0 +1 @@
+import{_ as r}from"./logo_worldline-dinT9MYm.js";import{_ as o,r as s,o as l,c,a as e,b as t,d as i,e as a}from"./app-jR2rC7Ae.js";const d={},h=a('
avatarWe design payments technology that powers the growth of millions of businesses around the world. Engineering the next frontiers in payments technology
- Leader in payment and secured transactions
- Over 50 billion transactions/year
- 7000+ engineers in over 40 countries
- A huge & diverse tech-stack
',5),m={class:"custom-container tip"},p=e("p",{class:"custom-container-title"},"TIP",-1),u={href:"/fr/",target:"_blank",rel:"noopener noreferrer"},f={href:"/fr/",target:"_blank",rel:"noopener noreferrer"},g=a('- Basic knowledge of object-oriented language like Java
- Prepare your development environment and install stuff before the session (see Tooling section)
',3),_={href:"https://kotlinlang.org/docs/home.html",target:"_blank",rel:"noopener noreferrer"},b={href:"https://github.com/worldline/learning-kotlin",target:"_blank",rel:"noopener noreferrer"};function k(v,w){const n=s("ExternalLinkIcon");return l(),c("div",null,[h,e("div",m,[p,e("p",null,[t("This training is also available in "),e("a",u,[t("French"),i(n)]),t(" / Cette formation est aussi disponible en "),e("a",f,[t("Français"),i(n)])])]),g,e("ul",null,[e("li",null,[e("a",_,[t("Official Kotlin documentation"),i(n)])]),e("li",null,[e("a",b,[t("Github repository for this training"),i(n)])])])])}const W=o(d,[["render",k],["__file","index.html.vue"]]),q=JSON.parse(`{"path":"/","title":"Welcome","lang":"en-US","frontmatter":{"home":true,"heroImage":"./kotlin_logo.png","tagline":"A beginner's guide to a modern programming language","actions":[{"text":"Get started →","link":"/en/presentation/","type":"primary"}],"features":[{"title":"Language features","details":"null safety, extensions, lambdas, Java interoperability and more"},{"title":"Backend development","details":"With Ktor, spring and NodeJS"},{"title":"Frontend development","details":"Compose multiplatform, Kotlin/JS, Kotlin/WASM and JVM frameworks"},{"title":"Cross-platform development","details":"With KMP and Compose multiplatform"},{"title":"Advanced Kotlin","details":"Coroutines, delegates, Function literal with receiver, DSLs and more"},{"title":"Practical exercises and solutions","details":"All chapters have a set of exercises"}],"footer":"Worldline, 2023"},"headers":[{"level":2,"title":"Who we are","slug":"who-we-are","link":"#who-we-are","children":[]},{"level":2,"title":"Prerequisites","slug":"prerequisites","link":"#prerequisites","children":[]},{"level":2,"title":"Useful links","slug":"useful-links","link":"#useful-links","children":[]}],"git":{"updatedTime":1693583972000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":4},{"name":"W112314","email":"quentin.carpentier@worldline.com","commits":2}]},"filePathRelative":"index.md"}`);export{W as comp,q as data};
diff --git a/assets/index.html-Cd7wtrxg.js b/assets/index.html-Cd7wtrxg.js
new file mode 100644
index 00000000..3bcbf4de
--- /dev/null
+++ b/assets/index.html-Cd7wtrxg.js
@@ -0,0 +1 @@
+import{_ as o}from"./kotlin-used-for-Bdlavnqs.js";import{_ as r,r as s,o as a,c as u,a as e,b as n,d as l,e as i}from"./app-jR2rC7Ae.js";const c={},d=e("h1",{id:"🚀-presentation-de-kotlin",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#🚀-presentation-de-kotlin"},[e("span",null,"🚀 Présentation de Kotlin")])],-1),p=e("p",null,"Kotlin est un langage de programmation moderne développé par JetBrains.",-1),h=e("h2",{id:"certaines-fonctionnalites",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#certaines-fonctionnalites"},[e("span",null,"Certaines fonctionnalités")])],-1),f=i("Kotlin est capable de compiler vers différentes cibles : la JVM, JS, Android, iOS, Les OS de bureau, etc. - Pris en charge officiellement par de grands frameworks back-end, tels que Spring et Quarkus,
- Langage préféré pour le développement Android,
- Kotlin Native compile en code natif,
- Kotlin peut être considéré comme un langage fullstack
Interopérable avec Java.Fournit des fonctionnalités modernes telles que la null safety à la compilation.Prend en charge la programmation orientée objet et la programmation fonctionnelle.",4),m={href:"https://github.com/Kotlin/KEEP",target:"_blank",rel:"noopener noreferrer"},g=e("h2",{id:"histoire",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#histoire"},[e("span",null,"Histoire")])],-1),_=e("ul",null,[e("li",null,"15 février 2016 : sortie de Kotlin 1.0."),e("li",null,"4 janvier 2017 : Spring a introduit le support de Kotlin dans Spring 5."),e("li",null,"Lors de Google I/O 2017, Google a annoncé une prise en charge officielle de Kotlin sur Android."),e("li",null,"Le 7 mai 2019, Google a annoncé que Kotlin était le langage préféré des développeurs d'applications Android."),e("li",null,"Juin 2022 : Kotlin 1.7 est sorti avec la version du nouveau compilateur K2.")],-1),v=e("h2",{id:"quelques-chiffres-et-faits",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#quelques-chiffres-et-faits"},[e("span",null,"Quelques chiffres et faits")])],-1),k={href:"https://medium.com/predict/8-stats-and-fact-you-should-know-about-kotlin-in-2021-b41133a1834",target:"_blank",rel:"noopener noreferrer"},b=e("li",null,"En 2022, Kotlin est utilisé par 7,8 % des experts de l'industrie.",-1),K=e("li",null,"Selon StackOverflow, Kotlin était le 4ème langage le plus apprécié en 2020 avec 62,9 % des votes.",-1),q=e("li",null,"Kotlin est désormais répertorié parmi les 20 meilleurs langages de programmation par Redmonk.",-1),w=e("li",null,"Kotlin fait partie des 3 principaux langages vers lesquels la plupart des entreprises prévoient de migrer leurs applications en 2022.",-1),x=e("li",null,"Kotlin est utilisé par des entreprises mondiales telles que Google, Netflix, Amazon, Trello, etc.",-1),S={href:"https://www.youtube.com/watch?v=o14wGByBRAQ",target:"_blank",rel:"noopener noreferrer"},y=e("figure",null,[e("img",{src:o,alt:"kotlin utilisé pour quoi ?",tabindex:"0",loading:"lazy"}),e("figcaption",null,"kotlin utilisé pour quoi ?")],-1),J=e("p",null,"Veuillez trouver plus de statiques ici :",-1),P={href:"https://www.jetbrains.com/lp/kotlin-census-2020/",target:"_blank",rel:"noopener noreferrer"},L={href:"https://www.jetbrains.com/lp/devecosystem-2021/kotlin/",target:"_blank",rel:"noopener noreferrer"},j=i('Voici quelques arguments qui motivent le passage de Java (version 17 LTS au moment de la rédaction) à Kotlin.
- Kotlin prend en charge plus de cibles que Java.
- Kotlin protège des références null à la compilation (les
Optional
Java sont ne sont pas de protections à la compilation). - Les chaînes de caractères Kotlin prennent en charge l'interpolation.
- Les fonctionnalités de programmation fonctionnelle de Kotlin sont meilleures. Il permet même de définir des constructeurs et des DSL (Domain Specific Language) dont le typage est sécurisé (type-safe).
- Kotlin peut être mélangé avec du code Java, facilitant ainsi le processus de migration.
Vous pouvez lire plus d'arguments dans ces articles :
',4),E={href:"https://codersera.com/blog/java-vs-kotlin/",target:"_blank",rel:"noopener noreferrer"},A=e("li",null,"[8 raisons pour lesquelles vous devriez passer à Kotlin depuis Java] (https://www.geeksforgeeks.org/8-reasons-why-you-should-switch-to-kotlin-from-java)",-1),V=e("h2",{id:"sources-et-plus-de-lecture",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#sources-et-plus-de-lecture"},[e("span",null,"Sources et plus de lecture")])],-1),B={href:"https://en.wikipedia.org/wiki/Kotlin_(programming_language)",target:"_blank",rel:"noopener noreferrer"},N={href:"https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0",target:"_blank",rel:"noopener noreferrer"},z={href:"https://medium.com/predict/8-stats-and-fact-you-should-know-about-kotlin-in-2021-b41133a1834",target:"_blank",rel:"noopener noreferrer"},G={href:"https://dev.to/aashiya123/why-should-you-learn-kotlin-in-2021-57e2",target:"_blank",rel:"noopener noreferrer"};function O(R,I){const t=s("ExternalLinkIcon");return a(),u("div",null,[d,p,h,e("ul",null,[f,e("li",null,[n("Processus d'évolution open source: "),e("a",m,[n("Kotlin KEEP"),l(t)]),n(".")])]),g,_,v,e("p",null,[e("a",k,[n("Source : Amyra Sheldon"),l(t)])]),e("ul",null,[b,K,q,w,x,e("li",null,[n("Plusieurs entreprises dont "),e("a",S,[n("Google"),l(t)]),n(", Pinterest et Uber ont migré leurs applications de Java vers Kotlin.")])]),y,J,e("ul",null,[e("li",null,[e("a",P,[n("Recensement Kotlin 2020"),l(t)])]),e("li",null,[e("a",L,[n("État Kotlin de JetBrains en 2021"),l(t)])])]),j,e("ul",null,[e("li",null,[e("a",E,[n("Java Vs Kotlin : Lequel vaut-il mieux apprendre en 2022 ?"),l(t)])]),A]),V,e("ul",null,[e("li",null,[e("a",B,[n("Kotlin sur Wikipedia"),l(t)])]),e("li",null,[e("a",N,[n("Présentation de la prise en charge de Kotlin dans Spring Framework 5.0"),l(t)])]),e("li",null,[e("a",z,[n("8 statistiques et faits à connaître sur Kotlin en 2022"),l(t)])]),e("li",null,[e("a",G,[n("Pourquoi devriez-vous apprendre Kotlin en 2022 ?"),l(t)])])])])}const C=r(c,[["render",O],["__file","index.html.vue"]]),D=JSON.parse('{"path":"/fr/presentation/","title":"🚀 Présentation de Kotlin","lang":"fr-FR","frontmatter":{},"headers":[{"level":2,"title":"Certaines fonctionnalités","slug":"certaines-fonctionnalites","link":"#certaines-fonctionnalites","children":[]},{"level":2,"title":"Histoire","slug":"histoire","link":"#histoire","children":[]},{"level":2,"title":"Quelques chiffres et faits","slug":"quelques-chiffres-et-faits","link":"#quelques-chiffres-et-faits","children":[]},{"level":2,"title":"Pourquoi passer de Java à Kotlin","slug":"pourquoi-passer-de-java-a-kotlin","link":"#pourquoi-passer-de-java-a-kotlin","children":[]},{"level":2,"title":"Sources et plus de lecture","slug":"sources-et-plus-de-lecture","link":"#sources-et-plus-de-lecture","children":[]}],"git":{"updatedTime":1696262637000,"contributors":[{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":1},{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":1}]},"filePathRelative":"fr/presentation/README.md"}');export{C as comp,D as data};
diff --git a/assets/index.html-CrQuKRiG.js b/assets/index.html-CrQuKRiG.js
new file mode 100644
index 00000000..d1f37a60
--- /dev/null
+++ b/assets/index.html-CrQuKRiG.js
@@ -0,0 +1 @@
+import{_ as n}from"./kmp_codelab-CiTPMWjt.js";import{_ as i,r,o as l,c as s,a as e,b as o,d as a}from"./app-jR2rC7Ae.js";const c={},m=e("h1",{id:"🛠-let-s-make-a-cross-plaform-app",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#🛠-let-s-make-a-cross-plaform-app"},[e("span",null,"🛠 Let's make a cross-plaform app !")])],-1),d=e("p",null,"By combining KMP and Compose, it is possible to fully develop cross-platform mobile, web and desktop apps using only Kotlin.",-1),h=e("h4",{id:"prerequisites",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#prerequisites"},[e("span",null,"Prerequisites")])],-1),p=e("li",null,"Basic knowledge of kotlin development (nullability,inline & lambda functions mainly)",-1),_={href:"https://developer.android.com/studio",target:"_blank",rel:"noopener noreferrer"},u=e("strong",null,"Giraffe",-1),b=e("li",null,"A good connectivity",-1),f={class:"custom-container tip"},g=e("p",{class:"custom-container-title"},"TIP",-1),k={href:"https://kotlinlang.org/docs/multiplatform-mobile-setup.html#next-step",target:"_blank",rel:"noopener noreferrer"},v={id:"🚀-start-the-practical-work-here-🚀",tabindex:"-1"},w={class:"header-anchor",href:"#🚀-start-the-practical-work-here-🚀"},x={href:"https://worldline.github.io/learning-kotlin-multiplatform/overview/",target:"_blank",rel:"noopener noreferrer"},y=e("figure",null,[e("a",{href:"%5Bhttps://worldline.github.io/learning-kotlin-multiplatform/%5D(https://worldline.github.io/learning-kotlin-multiplatform/overview/)"},[e("img",{src:n,alt:"kmp codelab",tabindex:"0",loading:"lazy"})]),e("figcaption",null,"kmp codelab")],-1);function B(E,I){const t=r("ExternalLinkIcon");return l(),s("div",null,[m,d,h,e("ul",null,[p,e("li",null,[e("a",_,[o("Android Studio IDE"),a(t)]),o(" with latest stable version "),u,o(" version or above")]),b]),e("div",f,[g,e("p",null,[o("For more information about your DEV environment and installs please have a look to jetbrain related "),e("a",k,[o("docs"),a(t)])])]),e("h1",v,[e("a",w,[e("span",null,[e("a",x,[o(" _____ 🚀 Start the practical work here 🚀_____"),a(t)])])])]),y])}const L=i(c,[["render",B],["__file","index.html.vue"]]),N=JSON.parse(`{"path":"/en/other-technologies/","title":"🛠 Let's make a cross-plaform app !","lang":"en-US","frontmatter":{},"headers":[],"git":{"updatedTime":1696345169000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":13},{"name":"Ibrahim Gharbi","email":"brah.gharbi@gmail.com","commits":4},{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":1}]},"filePathRelative":"en/other-technologies/README.md"}`);export{L as comp,N as data};
diff --git a/assets/index.html-D9WLw91N.js b/assets/index.html-D9WLw91N.js
new file mode 100644
index 00000000..e6f01c5d
--- /dev/null
+++ b/assets/index.html-D9WLw91N.js
@@ -0,0 +1,13 @@
+import{_ as i,r as l,o as c,c as o,a as n,b as e,d as s,e as t}from"./app-jR2rC7Ae.js";const r={},p=n("h1",{id:"📚-advanced-and-other-kotlin-features",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#📚-advanced-and-other-kotlin-features"},[n("span",null,"📚 Advanced and other Kotlin features")])],-1),u=n("h2",{id:"delegated-properties",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#delegated-properties"},[n("span",null,"Delegated properties")])],-1),d=n("p",null,[e("Kotlin allows to delegate the getter and setter of a property to another object, which is called a delegate. It is a class that defines the "),n("code",null,"getValue"),e(" and "),n("code",null,"setValue"),e(" methods.")],-1),g=n("p",null,"Kotlin provides standard delegates such lazy properties and observable properties.",-1),I={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGtvdGxpbi5yYW5kb20uUmFuZG9tXG5pbXBvcnQga290bGluLnByb3BlcnRpZXMuRGVsZWdhdGVzXG5pbXBvcnQga290bGluLnJlZmxlY3QuS1Byb3BlcnR5XG5cbmNsYXNzIFN1cGVySGVybyB7XG4gICAgdmFsIHJhbmRvbVN0cmVuZ3RoOiBJbnQgYnkgbGF6eSB7XG4gICAgICAgIHByaW50bG4oXCJjb21wdXRlZFwiKVxuICAgICAgICBSYW5kb20ubmV4dEludCg1MCwgMTAwKVxuICAgIH1cbiAgICBcbiAgICB2YXIgcG93ZXJOYW1lOiBTdHJpbmcgYnkgRGVsZWdhdGVzLm9ic2VydmFibGUoXCJMYXNlclwiKSB7IF8sIG9sZCwgbmV3IC0+XG4gICAgICAgIHByaW50bG4oXCJDaGFuZ2VkIHBvd2VyIGZyb20gJG9sZCB0byAkbmV3XCIpXG4gICAgfVxuICAgIFxuICAgIHZhciBjb2RlTmFtZTogU3RyaW5nIGJ5IENhcGl0YWxpemVEZWxlZ2F0ZSgpXG59XG5cbmNsYXNzIENhcGl0YWxpemVEZWxlZ2F0ZXtcbiAgICB2YXIgdXBwZXJjYXNlZCA9IFwiXCJcbiAgICBvcGVyYXRvciBmdW4gZ2V0VmFsdWUodGhpc1JlZjogQW55PywgcHJvcGVydHk6IEtQcm9wZXJ0eTwqPik6IFN0cmluZyB7XG4gICAgICAgIHByaW50bG4oXCIkdGhpc1JlZiwgdGhhbmsgeW91IGZvciBkZWxlZ2F0aW5nICcke3Byb3BlcnR5Lm5hbWV9JyB0byBtZSFcIilcbiAgICAgICAgcmV0dXJuIHVwcGVyY2FzZWRcbiAgICB9XG5cbiAgICBvcGVyYXRvciBmdW4gc2V0VmFsdWUodGhpc1JlZjogQW55PywgcHJvcGVydHk6IEtQcm9wZXJ0eTwqPiwgdmFsdWU6IFN0cmluZykge1xuICAgICAgICBwcmludGxuKFwiJyR2YWx1ZScgaGFzIGJlZW4gYXNzaWduZWQgdG8gJyR7cHJvcGVydHkubmFtZX0nIGluICR0aGlzUmVmLlwiKVxuICAgICAgICB1cHBlcmNhc2VkID0gdmFsdWUudXBwZXJjYXNlKClcbiAgICB9XG59XG5cbmZ1biBtYWluKCkge1xuICAgdmFsIHN1cGVyTGFuZGVyID0gU3VwZXJIZXJvKClcbiAgIHByaW50bG4oc3VwZXJMYW5kZXIucmFuZG9tU3RyZW5ndGgpXG4gICBwcmludGxuKHN1cGVyTGFuZGVyLnJhbmRvbVN0cmVuZ3RoKVxuICAgXG4gICBzdXBlckxhbmRlci5wb3dlck5hbWUgPSBcInNwZWVkXCJcbiAgICBcbiAgICBzdXBlckxhbmRlci5jb2RlTmFtZSA9IFwic3VwZXJsYW5kZXJcIlxuICAgIHByaW50bG4oc3VwZXJMYW5kZXIuY29kZU5hbWUpXG59In0=",target:"_blank",rel:"noopener noreferrer"},b=t('Kotlin provides a high level concurrency model called Coroutines. The developer can delegated the management of threads to the compiler and runtime and using higher level constructs than threads to express asynchronous operations.
Coroutines in Kotlin revolve around these concepts:
- A coroutine is an instance of suspendable computation.
- Kotlin has many methods for creating a coroutine such as
launch
.
- A coroutine must exist within a coroutine scope.
- For example
runBlocking
creates a coroutine scope whithin which coroutines can be launched.
- A coroutine can run suspend functions which can suspend the coroutine but do not block the thread.
- For example: the
delay
suspend the coroutine but does not block the thread on which it is running. - Suspend functions are operations that may take time such http requests and file system calls.
- The
suspend
qualifier defines a suspend function. It runs within a coroutine and can call other suspend functions. Flow
allows to generate a list of asynchronous values.Deferred
and Channel
transfer a single value and a stream of values, respectively, between coroutines.
',4),h={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGtvdGxpbnguY29yb3V0aW5lcy4qXG5cbnN1c3BlbmQgZnVuIHJ1blNvbWV0aGluZygpIHtcbiAgICBwcmludGxuKFwic3RhcnRpbmdcIilcbiAgICBkZWxheSg1MDApXG4gICAgcHJpbnRsbihcImZpbmlzaGVkXCIpXG59XG5cbi8vIHJ1bkJsb2NraW5nIGNyZWF0ZXMgYSBDb3JvdXRpbmVTY29wZVxuZnVuIG1haW4oKSA9IHJ1bkJsb2NraW5nIHsgXG4gICAgLy8gbGF1bmNoIGEgbmV3IGNvcm91dGluZVxuICAgIGxhdW5jaCB7IFxuICAgICAgICAvLyBTdXNwZW5kIHRoZSBjb3JvdXRpbmUgYnV0IGRvZXMgbm90IGJsb2NrIHRoZSB0aHJlYWRcbiAgICAgICAgZGVsYXkoMTAwMEwpIFxuICAgICAgICBwcmludGxuKFwiV29ybGQhXCIpIFxuICAgIH1cbiAgICAgICAgXG4gICAgLy8gbWFpbiBjb3JvdXRpbmUgY29udGludWVzIHdoaWxlIGEgcHJldmlvdXMgb25lIGlzIGRlbGF5ZWRcbiAgICBwcmludGxuKFwiSGVsbG9cIikgXG4gICAgXG4gICAgdmFsIGpvYiA9IGxhdW5jaCB7XG4gICAgICAgIHJ1blNvbWV0aGluZygpXG4gICAgfVxuICAgIGpvYi5qb2luKClcbiAgICBydW5Tb21ldGhpbmcoKVxuICAgIFxufSJ9",target:"_blank",rel:"noopener noreferrer"},m={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGtvdGxpbnguY29yb3V0aW5lcy4qXG5pbXBvcnQga290bGlueC5jb3JvdXRpbmVzLmZsb3cuKlxuXG5mdW4gZ2VucmF0ZUFzeW5jUmFuZG9tVmFsdWVzKCk6IEZsb3c8SW50PiA9IGZsb3cgeyBcbiAgICBwcmludGxuKFwiRmxvdyBzdGFydGVkXCIpXG4gICAgZm9yIChpIGluIDAuLjEwKSB7XG4gICAgICAgIGRlbGF5KDEwMClcbiAgICAgICAgZW1pdChpKVxuICAgIH1cbn1cblxuLy8gVGhpcyBjYW4gY2F1c2UgcHJvYmxlbXMgaW4gbm90IGhhbmRsZWRlZCBjb3JyZWN0bHlcbmZ1biBnZW5yYXRlQXN5bmNSYW5kb21WYWx1ZXNJbmZpbml0ZSgpOiBGbG93PEludD4gPSBmbG93IHsgXG4gICAgcHJpbnRsbihcIkZsb3cgc3RhcnRlZFwiKVxuICAgIHZhciBpID0gMFxuICAgIHdoaWxlICh0cnVlKSB7XG4gICAgICAgIGRlbGF5KDEwMClcbiAgICAgICAgZW1pdChpKVxuICAgICAgICBpICs9IDFcbiAgICB9XG59XG5cblxuXG4vLyBydW5CbG9ja2luZyBjcmVhdGVzIGEgQ29yb3V0aW5lU2NvcGVcbmZ1biBtYWluKCkgPSBydW5CbG9ja2luZzxVbml0PiB7XG4gICAgbGF1bmNoe1xuICAgICAgICBnZW5yYXRlQXN5bmNSYW5kb21WYWx1ZXMoKS5jb2xsZWN0IHtcbiAgICAgICBcdFx0cHJpbnRsbihcIjFzdCBmbG93OiBHb3QgJGl0XCIpXG4gICBcdFx0fVxuICAgIH1cbiAgIFxuICAgbGF1bmNoe1xuICAgICAgIHdpdGhUaW1lb3V0T3JOdWxsKDEwMDApe1xuICAgICAgICAgICBnZW5yYXRlQXN5bmNSYW5kb21WYWx1ZXMoKS5jb2xsZWN0IHtcbiAgICAgICAgICAgcHJpbnRsbihcIjJuZCBmbG93OiBHb3QgJGl0XCIpXG4gICAgICAgICB9XG4gICAgICAgfVxuICAgfVxufSJ9",target:"_blank",rel:"noopener noreferrer"},G={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGtvdGxpbnguY29yb3V0aW5lcy4qXG5pbXBvcnQga290bGlueC5jb3JvdXRpbmVzLmNoYW5uZWxzLipcblxuZnVuIG1haW4oKSA9IHJ1bkJsb2NraW5nIHtcbiAgICBcbiAgICAvLyBhc3luYyBsYXVuY2hlcyBhIGNvcm91dGluZXMgYW5kIHJldHVybnMgYSBEZWZlcnJlZDxJbnQ+XG4gICAgdmFsIGRlZmVycmVkID0gYXN5bmMge1xuICAgICAgICA0MlxuICAgIH1cbiAgICBwcmludGxuKGRlZmVycmVkLmF3YWl0KCkpXG5cbiAgICB2YWwgY2hhbm5lbCA9IENoYW5uZWw8SW50PigpXG4gICAgbGF1bmNoIHtcbiAgICAgICAgLy8gdGhpcyBtaWdodCBiZSBoZWF2eSBDUFUtY29uc3VtaW5nIGNvbXB1dGF0aW9uIG9yIGFzeW5jIGxvZ2ljLCB3ZSdsbCBqdXN0IHNlbmQgZml2ZSBzcXVhcmVzXG4gICAgICAgIGZvciAoeCBpbiAxLi41KSBjaGFubmVsLnNlbmQoeCAqIHgpXG4gICAgfVxuICAgIC8vIGhlcmUgd2UgcHJpbnQgZml2ZSByZWNlaXZlZCBpbnRlZ2VyczpcbiAgICByZXBlYXQoNSkgeyBwcmludGxuKGNoYW5uZWwucmVjZWl2ZSgpKSB9XG4gICAgcHJpbnRsbihcIkRvbmUhXCIpXG5cbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},C=t(`As seen previously, function extension add behavior to existing classes. Inside the definition of the function extension, we can reference the extension receiver (or this) implicitly.
fun String.countCharacters() = length
+println("hello".countCharacters())
+
We can define this extension with a function literal (or lambda) in instead of a named function (declared with fun
).
var extFn: String.() -> Int
+extFn = { length }
+println("hello".extFn())
+println(extFn("hello"))
+
extFn
is a function literal (lambda) that has access to the receiver (this). That's why it's called a function literal with receiver.
extFn("hello")
or extFn("hello")
call the extension as expected from extension functions.
The type of a function literal with receiver is funName: ReceiverType.(arg1Type, arg2Type, etc.) -> ReturnType
and is called with funName(receiverValue, arg1Value, etc.)
or receiverValue.funName(arg1Value, etc.)
. However, this is not the interesting aspect.
`,8),k=n("code",null,"extFn = { length }",-1),A=n("code",null,"extFn",-1),Z={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGtvdGxpbi5yYW5kb20uUmFuZG9tXG5cbmZ1biBtYWluKCkge1xuICAgIC8vIGZuIGlzIGEgZnVuY3Rpb24gbGl0ZXJhbHMgd2l0aCByZWNlaXZlciB0aGF0IHJldHVybnMgYW4gaW50XG4gICAgZnVuIGRvU29tZXRoaW5nV2l0aFJhbmRvbUludChmbjogSW50LigpIC0+IEludCk6IEludCB7XG4gICAgXHR2YWwgcmFuZG9tSW50ID0gUmFuZG9tLm5leHRJbnQoMzIsIDk4KVxuICAgIFx0cmV0dXJuIHJhbmRvbUludC5mbigpXG5cdH1cbiAgICAgXG4gICAgdmFsIHJlc3VsdCA9IGRvU29tZXRoaW5nV2l0aFJhbmRvbUludCB7XG4gICAgICAgIC8vIFRoZSByZWNlaXZlciBpcyBpbXBsaWNpdGx5IGF2YWlsYWJsZSBpbiB0aGlzIGxhbWJkYVxuICAgICAgICBwcmludGxuKFwidGhpczogJHRoaXNcIilcbiAgICAgICAgcHJpbnRsbihcInBsdXM6ICR7cGx1cygxMDApfVwiKVxuICAgICAgICBtaW51cyg1MDApXG4gICAgfVxuICAgIHByaW50bG4oXCJyZXN1bHQgOiAkcmVzdWx0XCIpXG59In0=",target:"_blank",rel:"noopener noreferrer"},X={href:"https://kotlinlang.org/docs/type-safe-builders.htmlhttps://kotlinlang.org/docs/type-safe-builders.html",target:"_blank",rel:"noopener noreferrer"},W=n("strong",null,"Type-safe builders",-1),y={class:"custom-container tip"},v=n("p",{class:"custom-container-title"},"Builder pattern",-1),x={href:"https://refactoring.guru/design-patterns/builder",target:"_blank",rel:"noopener noreferrer"},B=t(`
+val text = StringBuilder("Temp")
+ .append(1)
+ .append(true)
+ .append("friend")
+ .toString()
+
`,1),V={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiY2xhc3MgTWlsa3tcbiAgICB2YXIgYnJhbmQ6IFN0cmluZyA9IFwiXCJcbiAgICB2YXIgcXVhbnRpdHk6IEludCA9IDBcbiAgICBmdW4gYnJhbmQoaGFuZGxlcjogTWlsay4oKS0+IFN0cmluZyl7XG4gICAgICAgIGJyYW5kID0gaGFuZGxlcigpXG4gICAgfVxuICAgIGZ1biBxdWFudGl0eShoYW5kbGVyOiBNaWxrLigpLT5JbnQpe1xuICAgICAgICBxdWFudGl0eSA9IGhhbmRsZXIoKVxuICAgIH1cbiAgICBvdmVycmlkZSBmdW4gdG9TdHJpbmcoKSA9IFwiJHF1YW50aXR5IEwgb2YgbWlsayBmcm9tICRicmFuZFwiXG59XG5cbi8vIHR5cGVzYWZlIGJ1aWxkZXIgZW50cnkgcG9pbnRcbmZ1biBwcm9kdWNlTWlsayhpbml0OiBNaWxrLigpIC0+IFVuaXQpOiBNaWxrIHtcbiAgICB2YWwgbWlsayA9IE1pbGsoKVxuICAgIG1pbGsuaW5pdCgpXG4gICAgcmV0dXJuIG1pbGtcbn1cblxuZnVuIG1haW4oKXtcbiAgICB2YWwgbWlsayA9IHByb2R1Y2VNaWxrIHtcbiAgICAgICAgYnJhbmQgeyBcIlN1cGVyIG1pbGtcIiB9XG4gICAgICAgIHF1YW50aXR5IHsgMTAwIH1cbiAgICB9XG4gICAgcHJpbnRsbihtaWxrKVxufSJ9",target:"_blank",rel:"noopener noreferrer"},J={href:"https://kotlinlang.org/docs/type-safe-builders.html",target:"_blank",rel:"noopener noreferrer"},f=t('- open the java-integration-exercise projects in the materials folder.
- Have a look at the Java class we provided you in the
src/main/java/com/worldline/learning/kotlin/java2kotlin
package. (yes, that's the Pokemon class) - Convert that Java class in Kotlin using IntelliJ's awesome copy-pasta tool! (just copy paste the java code in a kotlin file, one is provided at
src/main/kotlin/com/worldline/learning/kotlin/java2kotlin
) - Have a look at the generated Kotlin code, and note the major differences you spot!
',3);function w(F,Y){const a=l("ExternalLinkIcon");return c(),o("div",null,[p,u,d,g,n("p",null,[n("a",I,[e("▶️ this code"),s(a)]),e(" illustrates delegate properties.")]),b,n("p",null,[n("a",h,[e("▶️ this code"),s(a)]),e(" show how to create a coroutine and suspend function and how to use them.")]),n("p",null,[n("a",m,[e("▶️ this code"),s(a)]),e(" illustrated flows.")]),n("p",null,[n("a",G,[e("▶️ this code"),s(a)]),e(" illustrates channels and deferred.")]),C,n("p",null,[e("The important part is "),k,e(" which can be put as a function argument in a higher order function. The developer that calls the higher order function must define "),A,e(", which in turn has access to the receiver. This allows for a nice style of programming. "),n("a",Z,[e("▶️ this code"),s(a)]),e(" shows an example.")]),n("p",null,[n("a",X,[W,s(a)]),e(" combine well-named builder functions and functions literals with receiver to create type-safe, statically typed builders in Kotlin.")]),n("div",y,[v,n("p",null,[n("a",x,[e("The builder pattern"),s(a)]),e(" is a way to construct complex objects.")]),B]),n("p",null,[n("a",V,[e("This code shows"),s(a)]),e(" a basic type-safe builder.")]),n("p",null,[n("a",J,[e("Kotlin docs"),s(a)]),e(" provides an example of an advanced type-safe builder that builds an HTML page.")]),f])}const H=i(r,[["render",w],["__file","index.html.vue"]]),N=JSON.parse('{"path":"/en/kotlin-features-advanced/","title":"📚 Advanced and other Kotlin features","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Delegated properties","slug":"delegated-properties","link":"#delegated-properties","children":[]},{"level":2,"title":"Concurrency and Coroutines","slug":"concurrency-and-coroutines","link":"#concurrency-and-coroutines","children":[]},{"level":2,"title":"Function literal with receiver and Type-safe builders","slug":"function-literal-with-receiver-and-type-safe-builders","link":"#function-literal-with-receiver-and-type-safe-builders","children":[]},{"level":2,"title":"🧪 Exercises","slug":"🧪-exercises","link":"#🧪-exercises","children":[]}],"git":{"updatedTime":1696322841000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":5},{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":1},{"name":"Ibrahim Gharbi","email":"brah.gharbi@gmail.com","commits":1}]},"filePathRelative":"en/kotlin-features-advanced/README.md"}');export{H as comp,N as data};
diff --git a/assets/index.html-DJx1WQoN.js b/assets/index.html-DJx1WQoN.js
new file mode 100644
index 00000000..3763368b
--- /dev/null
+++ b/assets/index.html-DJx1WQoN.js
@@ -0,0 +1,270 @@
+import{_ as c,r as o,o as r,c as u,a as n,b as s,d as a,w as i,e}from"./app-jR2rC7Ae.js";const d={},k=n("h1",{id:"📚-backend-development",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#📚-backend-development"},[n("span",null,"📚 Backend development")])],-1),m={href:"https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0",target:"_blank",rel:"noopener noreferrer"},v={href:"https://quarkus.io/guides/kotlin",target:"_blank",rel:"noopener noreferrer"},g={href:"https://github.com/ktorio/ktor",target:"_blank",rel:"noopener noreferrer"},b={href:"https://kotlinlang.org/docs/server-overview.html#deploying-kotlin-server-side-applications",target:"_blank",rel:"noopener noreferrer"},h={href:"https://medium.com/@touskar/une-application-nodejs-avec-kotlin-1969994fb1d2",target:"_blank",rel:"noopener noreferrer"},f=n("h2",{id:"ktor",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#ktor"},[n("span",null,"Ktor")])],-1),y=n("p",null,"Ktor is a cross-platform Kotlin library for building both HTTP clients and servers. This makes Ktor a useful library to learn for both front-end developers for its HTTP client capabilities and backend-development for its HTTP server capabilities. In the following, we'll create a REST API with Ktor server.",-1),w=n("h3",{id:"🧪-develop-an-api-with-ktor",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#🧪-develop-an-api-with-ktor"},[n("span",null,"🧪 develop an API with Ktor")])],-1),_={href:"https://start.ktor.io/",target:"_blank",rel:"noopener noreferrer"},q=e("Click on "Generate project".Download the archive, unzip it, and open the project with IntelliJ.Create a models
package and add to it a Customer
data class with these immutable properties id: String, firstName: String, lastName: String, email: String
.Annotate the class with @Serializable
.Create a new package named routes
and add to it a file CustomerRoutes.kt
that will contain the code for the /customer
endpoint.The code below provides the implementation of some endpoints. Please implement the remaining ones.To enable the route call customerRouting()
in the routing configuration file located in plugins/Routing.kt
.For simplicity, use a global in-memory list of customers val store = mutableListOf<Customer>()
.Run the server by running the main method.Test the api on the IDE by using an http file or using any other client.",10),S=e(`CustomerRoutes.kt
val store = mutableListOf<Customer>()
+
+fun Route.customerRouting() {
+ route("/customer") {
+ get {
+ call.respond(store)
+ }
+ get("{id?}") {
+ val id = call.parameters["id"] ?: return@get call.respondText(
+ "Missing id",
+ status = HttpStatusCode.BadRequest
+ )
+ val customer =
+ store.find { it.id == id } ?: return@get call.respondText(
+ "No customer with id $id",
+ status = HttpStatusCode.NotFound
+ )
+ call.respond(customer)
+ }
+ post {
+ val customer = call.receive<Customer>()
+ store.add(customer)
+ call.respondText("Customer stored correctly", status = HttpStatusCode.Created)
+ }
+ delete("{id?}") {
+
+ }
+ }
+}
+
plugins/Routing.kt
fun Application.configureRouting() {
+ routing {
+ customerRouting()
+ }
+}
+
`,2),x={class:"custom-container tip"},I=e(`return@label
You can specify which level you want to return with an explicit label using return@lambda
.
lambdaA {
+ lambdaB {
+ lambdaC {
+ val randomInt = Random.nextInt(0, 100)
+ if (randomInt > 50) return@lambdaC else return@lambdaB
+ }
+ printf("In lambdaB")
+ }
+}
+
`,3),j={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGtvdGxpbi5yYW5kb20uUmFuZG9tXG5cbi8vIHJldHVybiB3aXRoIGltcGxpY2l0IGxhYmVsc1xuXG5mdW4gbWFpbigpIHtcbiAgIHZhbCBrb3RsaW4gPSBcIvCfmYJcIlxuICAga290bGluLmxldCB7XG4gICAgICAgaXQuYXBwbHkge1xuXHRcdFx0dmFsIHJhbmRvbUludCA9IFJhbmRvbS5uZXh0SW50KDAsIDEwMClcbiAgICAgICAgICAgIHByaW50bG4ocmFuZG9tSW50KVxuICAgICAgICAgICAgaWYgKHJhbmRvbUludCA+IDUwKSByZXR1cm5AYXBwbHkgZWxzZSByZXR1cm5AbGV0XG4gICAgICAgfVxuICAgICAgIHByaW50bG4oXCJpbnQgbGV0IGFmdGVyIGFwcGx5XCIpXG4gICB9XG59In0=",target:"_blank",rel:"noopener noreferrer"},C=e(`CustomerTest.http
POST http://127.0.0.1:8080/customer
+
+
+{
+ "id": "100",
+ "firstName": "Jane",
+ "lastName": "Smith",
+ "email": "jane.smith@company.com"
+}
+
+
+###
+POST http://127.0.0.1:8080/customer
+Content-Type: application/json
+
+{
+ "id": "200",
+ "firstName": "John",
+ "lastName": "Smith",
+ "email": "john.smith@company.com"
+}
+
+###
+POST http://127.0.0.1:8080/customer
+Content-Type: application/json
+
+{
+ "id": "300",
+ "firstName": "Mary",
+ "lastName": "Smith",
+ "email": "mary.smith@company.com"
+}
+
+
+###
+GET http://127.0.0.1:8080/customer
+Accept: application/json
+
+###
+GET http://127.0.0.1:8080/customer/200
+Accept: application/json
+
+###
+GET http://127.0.0.1:8080/customer/500
+Accept: application/json
+
+###
+DELETE http://127.0.0.1:8080/customer/100
+
+###
+DELETE http://127.0.0.1:8080/customer/500
+
`,1),T={href:"https://ktor.io/docs/creating-http-apis.html",target:"_blank",rel:"noopener noreferrer"},A=n("h2",{id:"spring-framework",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-framework"},[n("span",null,"Spring framework")])],-1),P=n("p",null,"Spring is a famous framework for developing server-side applications: APIs, server generated web pages, microservices, etc. It relies on the the Java ecosystem to build and run, thus making it compatible with Kotlin. Even better, Spring officially supports Kotlin. It even allows in start a new project with Kotlin and Gradle-Kotlin. In the next section, we'll use this starter to recreate our above REST API with Spring.",-1),R=n("h3",{id:"🧪-spring-boot-part-1-develop-the-same-api-with-spring-boot",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#🧪-spring-boot-part-1-develop-the-same-api-with-spring-boot"},[n("span",null,"🧪 Spring boot part 1 - develop the same API with Spring Boot")])],-1),J={href:"https://start.spring.io/",target:"_blank",rel:"noopener noreferrer"},E=n("li",null,"Choose Kotlin as the language and Kotlin-Grade as the project manager.",-1),G=n("li",null,[s("Add these dependencies: "),n("strong",null,"Spring Web"),s(", "),n("strong",null,"Spring Boot DevTools"),s(", "),n("strong",null,"h2 database"),s(" and "),n("strong",null,"Spring Data JPA"),s(".")],-1),B={href:"https://marketplace.visualstudio.com/search?term=kotlin&target=VSCode&category=All%20categories&sortBy=Relevance",target:"_blank",rel:"noopener noreferrer"},K={href:"https://marketplace.visualstudio.com/items?itemName=Pivotal.vscode-boot-dev-pack",target:"_blank",rel:"noopener noreferrer"},M=e("Create Customer
data class in the model
package without the @Serializable
annotation.Create a controller
package that contains a CustomerController
class which provides a CRUD using a global list. You can find a skeleton below. - 💡 In Spring, Rest controllers serve the purpose of Ktor routes, where a controller defines a REST resource.
Define the same endpoints as in the previous PW.Start the REST API server by running .\\gradlew bootRun
or from your IDE.",4),L={href:"https://github.com/worldline/learning-kotlin/blob/main/material/spring-boot-kt-api/customer.jetbrains.http",target:"_blank",rel:"noopener noreferrer"},D={href:"https://github.com/worldline/learning-kotlin/blob/main/material/spring-boot-kt-api/customer.vscode-resclient.http",target:"_blank",rel:"noopener noreferrer"},O=e(`CustomerController.kt
val store = mutableListOf<Customer>()
+
+@RestController
+@RequestMapping("/customer")
+class CustomerController {
+ @GetMapping
+ fun getAll() = store
+
+ @GetMapping("{id}")
+ fun getById(@PathVariable id: String) { }
+
+ @PostMapping
+ fun addOne(@RequestBody customer: Customer) { }
+
+ @DeleteMapping("{id}")
+ fun deleteOne(@PathVariable id: String) { }
+}
+
Models or model package ? plural or not ?
Both are ok as long as you follow the same convention in the project.
Let's go a little bit further by storing data in a database and writing some tests.
We'll use the H2 in-memory database for the sake of simplicity, since it does not require a server to run. Classes will mapped to database tables with JPA annotations. The database API we'll be using is called JPARepository
. It is a lightweight API that provides common CRUD features by just defining an interface.
On the testing side, we'll see two different syntaxes. The default one that is more familiar with Java style and the DSL one which is more readable and more familiar with Kotlin developers.
`,6),N={href:"https://start.spring.io/",target:"_blank",rel:"noopener noreferrer"},V=e("Open the project and add this class in the model
package @Entity class Product(@Id @GeneratedValue var id: Long? = null, var name: String, var price: Int)
. This single defines the class as well as the minimal JPA annotations (@Entity
, @Id
and @GeneratedValue
) to generate the corresponding table.In the repository
package, declare the ProductRepository
interface as follows interface ProductRepository: JpaRepository<Product, Long>
. This is enough for Spring to generate an implementation with common features as we'll see later.Next, create a ProductService
class which will contain the business logic. In terms of architecture, the controller calls a service which in turn rely on other services or repositories.",3),H=e(`ProductService.kt
@Service
+class ProductService(@Autowired val productRepository: ProductRepository) {
+ fun getAll() = productRepository.findAll()
+
+
+ fun getById(id: Long) = productRepository.findByIdOrNull(id)
+}
+
- In the controller package, create a
ProductController
class that is mapped to /product
and injects the with @Autowired
. Reply to @Get
as follows.
ProductController.kt
@RestController
+@RequestMapping("/product")
+class ProductController(@Autowired val productService: ProductService) {
+ @GetMapping fun getAll() = productService.getAll()
+
+ @GetMapping("{id}")
+ fun getById(@PathVariable id: Long) =
+ productService.getById(id) ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
+}
+
`,3),W={class:"custom-container tip"},F=e(`Kotlin makes getById(@PathVariable id: Long) more concise
The Elvis operator ?:
allows to simplify the code. Here is a longer version as reference.
@GetMapping("{id}")
+fun getById(@PathVariable id: Long): Product {
+ val product = productService.getById(id)
+ if (product != null){
+ return product
+ }
+ throw ResponseStatusException(HttpStatus.NOT_FOUND)
+}
+
`,3),X=n("code",null,"@ControllerAdvice",-1),Y={href:"https://spring.io/guides/tutorials/rest/",target:"_blank",rel:"noopener noreferrer"},U=e(`- Let's run the project. Before running the project, we need to add a plugin that allows Kotlin classes to generate a default constructor
id("org.jetbrains.kotlin.plugin.jpa") version "1.8.10"
. The plugins should look as follows:
plugins {
+ id("org.jetbrains.kotlin.plugin.jpa") version "1.8.10"
+ id("org.springframework.boot") version "3.0.4"
+ id("io.spring.dependency-management") version "1.1.0"
+ kotlin("jvm") version "1.8.10"
+ kotlin("plugin.spring") version "1.8.10"
+}
+
- As an exercise, implement these endpoints: POST a single product, DELETE by id (
/product/{id}
) and GET by id (/product/{id}
). - Hint:
ProductController
already provides the necessary methods.
- Call the different endpoints with a REST client.
Spring frameworks helps perform different types of tests by providing different classes out of the box:
- Unit testing of services, repositories and the REST API. This is done through mock utilities such as
MockMVC
. - Integration testing of the REST API using
TestRestTemplate
. In this situation, a full server is run and tested.
Most, if not all classes provided by Spring provide an elegant syntax for Java developers. Some of them go further by taking advantage of Kotlin specific features. In the following, we're going to focus on parts that provide Kotlin DSLs, namely unit testing the REST API with MockMVC
.
- Create a test class
ProductControllerUnitTests
with this initial content. MockMvc
allows to unit test the REST API. The @AutoConfigureMockMvc
annotation allows spring to configure it automatically
@SpringBootTest
+@AutoConfigureMockMvc
+class ProductControllerTests(
+ @Autowired val mockMvc: MockMvc,
+ @Autowired val productRepository: ProductRepository) {
+
+ @BeforeEach
+ fun reset(){
+ productRepository.deleteAll()
+ }
+}
+
- Add these two tests. The first one uses a classic approach while the second take advantage of Kotlin DSL capabilities. In addition to that, we name using a more readable string literal
`,10),Z=n("div",{class:"language-kotlin line-numbers-mode","data-ext":"kt","data-title":"kt"},[n("pre",{class:"language-kotlin"},[n("code",null,[n("span",{class:"token annotation builtin"},"@Test"),s(`
+`),n("span",{class:"token keyword"},"fun"),s(),n("span",{class:"token function"},"testWithClassicApproach"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},"{"),s(`
+ mockMvc`),n("span",{class:"token punctuation"},"."),n("span",{class:"token function"},"perform"),n("span",{class:"token punctuation"},"("),n("span",{class:"token keyword"},"get"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"/product"')]),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},")"),s(`
+ `),n("span",{class:"token punctuation"},"."),n("span",{class:"token function"},"andExpect"),n("span",{class:"token punctuation"},"("),n("span",{class:"token function"},"status"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},"."),s("isOk"),n("span",{class:"token punctuation"},")"),s(`
+ `),n("span",{class:"token punctuation"},"."),n("span",{class:"token function"},"andExpect"),n("span",{class:"token punctuation"},"("),n("span",{class:"token function"},"content"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},"."),n("span",{class:"token function"},"string"),n("span",{class:"token punctuation"},"("),n("span",{class:"token function"},"containsString"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"[]"')]),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},")"),s(`
+`),n("span",{class:"token punctuation"},"}"),s(`
+`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),z=n("div",{class:"language-kotlin line-numbers-mode","data-ext":"kt","data-title":"kt"},[n("pre",{class:"language-kotlin"},[n("code",null,[n("span",{class:"token annotation builtin"},"@Test"),s(`
+`),n("span",{class:"token keyword"},"fun"),s(),n("span",{class:"token function"},"`test GET a single product`"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"{"),s(`
+ mockMvc`),n("span",{class:"token punctuation"},"."),n("span",{class:"token function"},"get"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"/product/1"')]),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},"."),n("span",{class:"token function"},"andExpect"),s(),n("span",{class:"token punctuation"},"{"),s(`
+ status `),n("span",{class:"token punctuation"},"{"),s(),n("span",{class:"token function"},"isOk"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"}"),s(`
+ `),n("span",{class:"token function"},"jsonPath"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"$.name"')]),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"{"),s(),n("span",{class:"token function"},"value"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"A"')]),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"}"),s(`
+ `),n("span",{class:"token function"},"jsonPath"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"$.price"')]),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"{"),s(),n("span",{class:"token function"},"value"),n("span",{class:"token punctuation"},"("),n("span",{class:"token number"},"1"),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"}"),s(`
+ content `),n("span",{class:"token punctuation"},"{"),s(),n("span",{class:"token function"},"contentType"),n("span",{class:"token punctuation"},"("),s("MediaType"),n("span",{class:"token punctuation"},"."),s("APPLICATION_JSON"),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"}"),s(`
+ `),n("span",{class:"token punctuation"},"}"),s(`
+`),n("span",{class:"token punctuation"},"}"),s(`
+`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),$=n("ul",null,[n("li",null,"As an exercise, unit tests for the other endpoints.")],-1),Q={class:"custom-container tip"},nn=e(`The request builder of JpaRepository
Spring repositories implement requests based on the name of their methods. For example, to get all products sorted by name, we can add this method to the interface.
interface ProductRepository: JpaRepository<Product, Long> {
+ fun findAllByOrderByNameAsc(): List<Product>;
+}
+
`,3),sn={href:"https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation",target:"_blank",rel:"noopener noreferrer"},an=n("h2",{id:"nodejs",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#nodejs"},[n("span",null,"NodeJS")])],-1),tn=n("strong",null,"external declaration",-1),en={href:"https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.js/-js-module/",target:"_blank",rel:"noopener noreferrer"},on={href:"https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.js/-js-non-module/",target:"_blank",rel:"noopener noreferrer"},ln={href:"https://github.com/Kotlin/dukat",target:"_blank",rel:"noopener noreferrer"},pn={href:"https://github.com/chrisnkrueger/kotlin-express",target:"_blank",rel:"noopener noreferrer"},cn=e(`There are two gradle plugins that allow to create nodeJs projects: the kotlin("js")
one and the kotlin("multiplatform")
one. The difference between the two plugins is that the former only supports JS or WASM while the latter supports more platforms but requires to configure source sets. Thus, the former may seem easier to setup but the latter is better in the long run because it allows us to get more familiar with Kotlin Multiplatform (KMP).
At the time of writing, I didn't find an official wizard or starter project. So we'll create one from scratch using gradle init
.
- Create a new Gradle project using IntelliJ or by running
gradle init
in a empty folder (see below for the replies to the gradle init
command).
gradle init
gradle init
+Starting a Gradle Daemon, 1 incompatible and 1 stopped Daemons could not be reused, use --status for details
+
+Select type of project to generate:
+ 1: basic
+ 2: application
+ 3: library
+ 4: Gradle plugin
+Enter selection (default: basic) [1..4] 1
+
+Select build script DSL:
+ 1: Kotlin
+ 2: Groovy
+Enter selection (default: Kotlin) [1..2] 1
+
+Project name (default: starter): rest-api-kotlin-nodejs
+
+Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no] yes
+
+
+> Task :init
+To learn more about Gradle by exploring our Samples at https://docs.gradle.org/8.3/samples
+
+BUILD SUCCESSFUL in 24s
+2 actionable tasks: 2 executed
+
- In build.gradle.kts, add and configure the
kotlin("multiplatform")
plugin. Also add the express
and dev.chriskrueger:kotlin-express
dependencies.
build.gradle.kts
plugins {
+ kotlin("multiplatform") version "1.9.20-Beta"
+}
+
+repositories {
+ mavenCentral()
+}
+
+group = "tech.worldline.demo"
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ js {
+ nodejs {
+ }
+ binaries.executable()
+ useCommonJs()
+ }
+
+ sourceSets {
+ val jsMain by getting {
+ dependencies {
+ implementation(npm("express", "> 4.0.0 < 5.0.0"))
+ implementation("dev.chriskrueger:kotlin-express:1.2.0")
+ }
+ }
+ }
+}
+
`,7),rn={class:"custom-container tip"},un=n("p",{class:"custom-container-title"},"Some notes on the build file",-1),dn=n("li",null,"express dependency is retrieved from npm",-1),kn=n("code",null,"useCommonJs()",-1),mn={href:"https://github.com/Kotlin/dukat/issues/106",target:"_blank",rel:"noopener noreferrer"},vn={href:"https://github.com/chrisnkrueger/kotlin-express",target:"_blank",rel:"noopener noreferrer"},gn=e(`- create a main.kt file in src/jsMain/kotlin with the following content:
main.kt
data class Message(val id: Int, val message: String)
+
+val messages = mutableListOf(Message(0, "I love Kotlin/JS"))
+
+fun main() {
+ val app = express.Express()
+
+
+ app.get("/hello") { _, res ->
+ res.send(messages)
+ }
+
+
+ app.listen(3000) {
+ console.log("server start at port 3000")
+ }
+}
+
`,2),bn=n("li",null,[s("Run the task "),n("code",null,"jsRun"),s(" from IntelliJ of from the command line "),n("code",null,"./gradlew --console=plain jsRun"),s(". The server should start running.")],-1),hn=n("strong",null,"hello",-1),fn={href:"http://localhost:3000/hello",target:"_blank",rel:"noopener noreferrer"},yn=e('Execution failed for task ':kotlinStoreYarnLock'
If you get this error:
Execution failed for task ':kotlinStoreYarnLock'.\n> yarn.lock was changed. Run the `kotlinUpgradeYarnLock` task to actualize yarn.lock file\n
Run ./gradlew kotlinUpgradeYarnLock
so that yarn.lock is updated
',2),wn={href:"https://www.npmjs.com/package/body-parser",target:"_blank",rel:"noopener noreferrer"},_n=n("code",null,"app.use(bodyParser.json())",-1),qn=n("code",null,"req.body",-1),Sn=n("strong",null,"bodyParser",-1),xn=e(`- First, add the body-parser dependncy in the build file
implementation(npm("body-parser", "> 1.0.0 < 2.0.0"))
- Next, we would write:
app.use(bodyparser.json())
to activate the library. Let's guess what a minimal definition of bodyparser
can be.
BodyParser.kt
+external class BodyParser {
+
+ fun json(): Any
+
+}
+
+
+@JsModule("body-parser")
+external val bodyParser: BodyParser
+
- Finally, we just need to add the BodyParser.kt file into the project and use it in our server.
main.kt
app.use(bodyParser.json())
+app.post("/hello") { req, res ->
+
+ if (req.body as? Message == null) {
+ println("failed to get the body from Kotlin")
+ }
+
+
+ println("req.body from JS \${js("req.body.id")} - \${js("req.body.message")}")
+ val id = js("req.body.id") as? Int
+ val message = js("req.body.message") as? String
+ if (message != null && id != null) {
+ messages.add(Message(id, message))
+ res.status(201).end()
+ } else {
+ res.status(400).send(js("{cause : 'error'}") as Any)
+ }
+}
+
- Add PUT and DELETE endpoints
`,7),In={href:"https://github.com/worldline/learning-kotlin/tree/master/material/ktor-api",target:"_blank",rel:"noopener noreferrer"},jn={href:"https://github.com/worldline/learning-kotlin/tree/master/material/spring-boot-kt-api",target:"_blank",rel:"noopener noreferrer"},Cn={href:"https://github.com/worldline/learning-kotlin/blob/main/material/rest-api-kotlin-nodejs",target:"_blank",rel:"noopener noreferrer"},Tn=n("h2",{id:"📖-further-readings",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#📖-further-readings"},[n("span",null,"📖 Further readings")])],-1),An=n("p",null,"These official tutorials go even further:",-1),Pn={href:"https://kotlinlang.org/docs/jvm-spring-boot-restful.html",target:"_blank",rel:"noopener noreferrer"},Rn={href:"https://spring.io/guides/tutorials/spring-boot-kotlin/",target:"_blank",rel:"noopener noreferrer"},Jn={href:"https://spring.io/guides/tutorials/rest/",target:"_blank",rel:"noopener noreferrer"},En={href:"https://quarkus.io/guides/kotlin",target:"_blank",rel:"noopener noreferrer"},Gn=n("p",null,[n("strong",null,"References")],-1),Bn={href:"https://dev.to/mpetuska/js-in-kotlinjs-c4g",target:"_blank",rel:"noopener noreferrer"},Kn={href:"https://www.baeldung.com/kotlin/mockmvc-kotlin-dsl",target:"_blank",rel:"noopener noreferrer"},Mn={href:"https://spring.io/guides/tutorials/spring-boot-kotlin/",target:"_blank",rel:"noopener noreferrer"},Ln={href:"https://www.baeldung.com/kotlin/jpa",target:"_blank",rel:"noopener noreferrer"},Dn={href:"https://stackoverflow.com/questions/47143127/spring-data-jpa-how-to-use-kotlin-nulls-instead-of-optional",target:"_blank",rel:"noopener noreferrer"};function On(Nn,Vn){const t=o("ExternalLinkIcon"),l=o("CodeGroupItem"),p=o("CodeGroup");return r(),u("div",null,[k,n("p",null,[s("Many frameworks officially support Kotlin: "),n("a",m,[s("Spring"),a(t)]),s(", "),n("a",v,[s("Quarkus"),a(t)]),s(", "),n("a",g,[s("Ktor"),a(t)]),s(", among others "),n("a",b,[s("listed here"),a(t)]),s(".")]),n("p",null,[s("In addition to that, Kotlin is theoretically compatible with any framework that targets the JVM or JS. For example, this tutorial shows "),n("a",h,[s("how to use nodejs with Kotlin"),a(t)]),s(". However, frameworks that do not officially support Kotlin may require some tweaking to use it.")]),f,y,w,n("ul",null,[n("li",null,[s("Create a project on "),n("a",_,[s("start.ktor.io"),a(t)]),s(" with the following plugins: Content Negotiation, kotlinx.serialization, and Routing.")]),q]),S,n("div",x,[I,n("p",null,[n("a",j,[s("This code runs another example"),a(t)]),s(".")])]),C,n("p",null,[n("a",T,[s("This page has detailed steps"),a(t)])]),A,P,R,n("ul",null,[n("li",null,[s("Create a project on "),n("a",J,[s("start.spring.io (also called Spring initializr)"),a(t)]),s(" with the following dependencies: Spring Web and Spring Boot DevTools.")]),E,G,n("li",null,[s('Click on "Generate". Download the archive, unzip it, and open the project with IntelliJ (preferably) or VSCode. '),n("ul",null,[n("li",null,[s("For VSCode, install a "),n("a",B,[s("Kotlin extension"),a(t)]),s(" and "),n("a",K,[s("Spring Boot Extension Pack"),a(t)]),s(" ( ⚠️ Spring extension do not seem to support kotlin).")])])]),M,n("li",null,[s("Please test the endpoints with a REST client. You can find http files here in "),n("a",L,[s("JetBrains format"),a(t)]),s(" or "),n("a",D,[s("VSCode's REST Client extension"),a(t)])])]),O,n("ul",null,[n("li",null,[s("Create a new Spring project using "),n("a",N,[s("Spring initializr"),a(t)]),s(" with Kotlin and the following dependencies: Spring Data JPA, H2 Database, Spring Boot DevTools, Spring Web")]),V]),H,n("div",W,[F,n("p",null,[s("In addition to that, Spring provides "),X,s(" to change the exception message. You can see an "),n("a",Y,[s("example here"),a(t)]),s(".")])]),U,a(p,null,{default:i(()=>[a(l,{title:"Without DSL (Test Get All)"},{default:i(()=>[Z]),_:1}),a(l,{title:"With DSL (Test Get Single)"},{default:i(()=>[z]),_:1})]),_:1}),$,n("div",Q,[nn,n("p",null,[n("a",sn,[s("The official documentation"),a(t)]),s(" provides more detailed explanations and examples.")])]),an,n("p",null,[s("Thanks to Kotlin/JS, we can write apps that target nodejs using Kotlin. We can even import npm libraries as long as you declare the JS API surface that you'll be using in Kotlin. This is called "),tn,s(" (You can think of it as an equivalent of TypeScript's type definitions) that declares the symbols that we want to access in Kotlin thanks to "),n("a",en,[s("@JsModule"),a(t)]),s(" and "),n("a",on,[s("@JsNonModule"),a(t)]),s(" annotations.")]),n("p",null,[s("Defining such external declarations can be a hassle and there seems to be no official automatic generator ("),n("a",ln,[s("dukat"),a(t)]),s(" has been removed in kotlin 1.8.20). In that case, we have two options, either we write the external declaration ourselves or import it as a dependency if available. Fortunately for express developers, "),n("a",pn,[s("chrisnkrueger/kotlin-express"),a(t)]),s(" provides declarations for the express library.")]),cn,n("div",rn,[un,n("ul",null,[dn,n("li",null,[kn,s(),n("a",mn,[s("is required"),a(t)]),s(" to be able to use "),n("a",vn,[s("chrisnkrueger/kotlin-express"),a(t)]),s(" in our code.")])])]),gn,n("ul",null,[bn,n("li",null,[s("Open the "),hn,s(" endpoint on "),n("a",fn,[s("http://localhost:3000/hello"),a(t)])])]),yn,n("p",null,[s("Let's add a post endpoint which reads the body as a json. In order to read the body as json, we must add this possibility to express by importing the npm library "),n("a",wn,[s("body-parser"),a(t)]),s(" and by calling "),_n,s(". Once this setup is complete, "),qn,s(" will contain the content of the body. However, there is no available external definition for "),Sn,s(" as of the time of writing. Thus, we must create or own external definition.")]),xn,n("ul",null,[n("li",null,[n("a",In,[s("ktor Rest API"),a(t)])]),n("li",null,[n("a",jn,[s("Spring boot Rest API"),a(t)])]),n("li",null,[s("The starter and final nodejs projects are available "),n("a",Cn,[s("here"),a(t)])])]),Tn,An,n("ul",null,[n("li",null,[n("a",Pn,[s("This tutorial from kotlinlang"),a(t)]),s(" shows how to create a RESTful web service with a database using Spring Boot.")]),n("li",null,[n("a",Rn,[s("This one from spring.io"),a(t)]),s(" show how to build a web application with Spring Boot and Kotlin.")]),n("li",null,[n("a",Jn,[s("Rest ÄPIs with Spring"),a(t)])]),n("li",null,[n("a",En,[s("Quarkus and kotlin"),a(t)])])]),Gn,n("ul",null,[n("li",null,[n("a",Bn,[s("JS in Kotlin/JS"),a(t)])]),n("li",null,[n("a",Kn,[s("mockmvc kotlin dsl"),a(t)])]),n("li",null,[n("a",Mn,[s("spring-boot-kotlin tutorial"),a(t)])]),n("li",null,[n("a",Ln,[s("Working with Kotlin and JPA"),a(t)])]),n("li",null,[n("a",Dn,[s("Spring Data JPA How to use Kotlin nulls instead of Optional"),a(t)])])])])}const Wn=c(d,[["render",On],["__file","index.html.vue"]]),Fn=JSON.parse('{"path":"/en/backend-development/","title":"📚 Backend development","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Ktor","slug":"ktor","link":"#ktor","children":[{"level":3,"title":"🧪 develop an API with Ktor","slug":"🧪-develop-an-api-with-ktor","link":"#🧪-develop-an-api-with-ktor","children":[]}]},{"level":2,"title":"Spring framework","slug":"spring-framework","link":"#spring-framework","children":[{"level":3,"title":"🧪 Spring boot part 1 - develop the same API with Spring Boot","slug":"🧪-spring-boot-part-1-develop-the-same-api-with-spring-boot","link":"#🧪-spring-boot-part-1-develop-the-same-api-with-spring-boot","children":[]},{"level":3,"title":"🧪 Spring boot part 2 - adding a database","slug":"🧪-spring-boot-part-2-adding-a-database","link":"#🧪-spring-boot-part-2-adding-a-database","children":[]},{"level":3,"title":"🧪 Spring boot part 2 - adding tests","slug":"🧪-spring-boot-part-2-adding-tests","link":"#🧪-spring-boot-part-2-adding-tests","children":[]}]},{"level":2,"title":"NodeJS","slug":"nodejs","link":"#nodejs","children":[{"level":3,"title":"🧪 Getting started with Kotlin/JS and Express","slug":"🧪-getting-started-with-kotlin-js-and-express","link":"#🧪-getting-started-with-kotlin-js-and-express","children":[]},{"level":3,"title":"🧪 Adding a post endpoint and an external Kotlin/JS definition","slug":"🧪-adding-a-post-endpoint-and-an-external-kotlin-js-definition","link":"#🧪-adding-a-post-endpoint-and-an-external-kotlin-js-definition","children":[]},{"level":3,"title":"🧪 Adding more endpoints","slug":"🧪-adding-more-endpoints","link":"#🧪-adding-more-endpoints","children":[]}]},{"level":2,"title":"🎯 Solutions","slug":"🎯-solutions","link":"#🎯-solutions","children":[]},{"level":2,"title":"📖 Further readings","slug":"📖-further-readings","link":"#📖-further-readings","children":[]}],"git":{"updatedTime":1702392867000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":22},{"name":"Ibrahim Gharbi","email":"brah.gharbi@gmail.com","commits":3},{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":2},{"name":"Tobias Preuss","email":"tobias.preuss@googlemail.com","commits":2},{"name":"Yassine Benabbas @ Worldline","email":"86733850+wlybe@users.noreply.github.com","commits":2}]},"filePathRelative":"en/backend-development/README.md"}');export{Wn as comp,Fn as data};
diff --git a/assets/index.html-DuVz8Jxd.js b/assets/index.html-DuVz8Jxd.js
new file mode 100644
index 00000000..add5802d
--- /dev/null
+++ b/assets/index.html-DuVz8Jxd.js
@@ -0,0 +1,24 @@
+import{_ as l}from"./kotlin-wasm-webapp-R4_9ho9v.js";import{_ as s,r as i,o as r,c as p,a as e,b as t,d as o,e as a}from"./app-jR2rC7Ae.js";const c="/learning-kotlin/assets/launch-android-app-BS4WBFFP.png",d="/learning-kotlin/assets/kmp-compose-desktop-CzocCLNL.png",m="/learning-kotlin/assets/hello-compose-demo-B4DIIuDy.gif",u={},h=e("h1",{id:"📚-frontend-development",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#📚-frontend-development"},[e("span",null,"📚 Frontend development")])],-1),f=e("p",null,"Kotlin supports a wide selection of frontend frameworks across all platforms: mobile, desktop and web.",-1),g=e("p",null,"Please find below a glimpse of the possibilities that you can do right from IntelliJ:",-1),k=e("strong",null,"Desktop",-1),b={href:"https://openjfx.io/",target:"_blank",rel:"noopener noreferrer"},_=e("em",null,"was",-1),w={href:"https://tornadofx.io/",target:"_blank",rel:"noopener noreferrer"},v={href:"https://www.jetbrains.com/lp/compose-mpp/",target:"_blank",rel:"noopener noreferrer"},S=e("strong",null,"Web",-1),A={href:"https://ktor.io/docs/creating-interactive-website.html",target:"_blank",rel:"noopener noreferrer"},y={href:"https://freemarker.apache.org/",target:"_blank",rel:"noopener noreferrer"},x=e("li",null,[t("With "),e("strong",null,"KotlinJS"),t(", developers can create React, nodsjs, or vanilla JS Apps using Kotlin.")],-1),K=e("li",null,[t("Kotlin "),e("strong",null,"WASM"),t(" compiles into "),e("em",null,"Web Assembly"),t(". It can complement KotlinJS for computation intensive tasks.")],-1),M=e("strong",null,"Mobiles",-1),W={href:"https://developer.android.com/jetpack/compose",target:"_blank",rel:"noopener noreferrer"},I=e("strong",null,"xml layouts",-1),J=e("p",null,[t("Kotlin supports cross platform frontend development thanks to "),e("strong",null,"Kotlin MultiPlatform (KMP)")],-1),j=e("h2",{id:"kotlin-multiplatform-kmp",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#kotlin-multiplatform-kmp"},[e("span",null,"Kotlin Multiplatform (KMP)")])],-1),C={href:"https://kotlinlang.org/docs/multiplatform.html",target:"_blank",rel:"noopener noreferrer"},P={href:"https://blog.jetbrains.com/kotlin/2021/08/compose-multiplatform-goes-alpha/",target:"_blank",rel:"noopener noreferrer"},B=e("figure",null,[e("img",{src:"https://kotlinlang.org/docs/images/kotlin-multiplatform.svg",alt:"KMP",tabindex:"0",loading:"lazy"}),e("figcaption",null,"KMP")],-1),T=e("p",null,"Many combinations of targets and use cases are possible:",-1),O={href:"https://kotlinlang.org/docs/multiplatform-full-stack-app.html",target:"_blank",rel:"noopener noreferrer"},L={href:"https://kotlinlang.org/docs/multiplatform-library.html",target:"_blank",rel:"noopener noreferrer"},q=e("h2",{id:"kotlin-js-and-kotlin-wasm",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#kotlin-js-and-kotlin-wasm"},[e("span",null,"Kotlin/JS and Kotlin/WASM")])],-1),z=e("li",null,"Kotlin/JS can also target the web and even use web frameworks (such as react) in Kolitn.",-1),D=e("li",null,[t("Kotlin WASM is another possibility to target the web but this will generate WASM instead of pure JS code. "),e("ul",null,[e("li",null,"It can be used for example to develop computation intensive libraries.")])],-1),F={href:"https://wasi.dev/",target:"_blank",rel:"noopener noreferrer"},R=e("li",null,"Let's keep watching 😄.",-1),N=e("h3",{id:"🧪-kotlin-wasm-web-app",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#🧪-kotlin-wasm-web-app"},[e("span",null,"🧪 Kotlin/WASM web app")])],-1),U=e("code",null,"git clone git@github.com:Kotlin/kotlin-wasm-examples.git",-1),V=e("strong",null,"browser-example",-1),E={href:"https://kotlinlang.org/docs/wasm-get-started.html",target:"_blank",rel:"noopener noreferrer"},H=e("li",null,[t("Open the project and run the "),e("code",null,"wasmJsBrowserRun"),t(" task.")],-1),G={href:"http://localhost:8080/",target:"_blank",rel:"noopener noreferrer"},Y=e("ul",null,[e("li",null,"⚠️ You may need to activate some flags on your browser for the app to work. If you see a blank page, please read the browser logs to check for the instructions.")],-1),$=e("figure",null,[e("img",{src:l,alt:"Alt text",tabindex:"0",loading:"lazy"}),e("figcaption",null,"Alt text")],-1),Q=e("li",null,[t("Please check the contents of "),e("strong",null,"src/wasmJsMain/kotlin/Simple.kt"),t(" to understand how the page is coded.")],-1),X=e("strong",null,"build/js/packages/project_name/kotlin",-1),Z=e("li",null,"WASM being a binary format, we need to convert it first to text format.",-1),ee={href:"https://github.com/WebAssembly/wabt",target:"_blank",rel:"noopener noreferrer"},te=e("em",null,"wasm2wattool",-1),ne=e("code",null,"wasm2wat --enable-all -v .\\kotlin-wasm-demo-wasm.wasm -o wasm.wat",-1),oe={href:"https://webassembly.github.io/wabt/demo/wasm2wat/",target:"_blank",rel:"noopener noreferrer"},ae=e("li",null,"❗ However, I couldn't get it to work (yet).",-1),le=a(`Both Kotlin/WASM and Kotlin/JS IntelliJ work somewhat similarly.
- Both rely on the KMP plugin
- Kotlin/WASM is enabled by adding a
wasmjs
section in the build.gradle.kts
file, while Kotlin/JS is enabled by adding a js
section. - The Kotlin code will compile to WASM and / or JS. Kotlin/JS generates only JS while Kotin/WASM generates both JS and WASM.
- In both cases, the entry point of the generated code is a JS file called module_name.js.
- The index.html in the resources folder loads the generated JS explained above (the one named module_name.js).
- The task
wasmBrowserDevelopmentRun
or jsWasmBrowserDevelopmentRun
run a local server that hosts both the index.html files and the generated JS and WASM files.
"Compose Multiplatform simplifies and accelerates UI development for Desktop and Web applications, and allows extensive UI code sharing between Android, iOS, Desktop and Web. It's a modern toolkit for building native UI. Quickly bring your app to life with less code, powerful tools, and intuitive Kotlin APIs."
imgButton(
+ onClick = {
+ logger.info("Pressed!")
+ }
+) {
+ Text("Hello $platform")
+}
+
`,7),se={href:"https://developer.android.com/jetpack/compose",target:"_blank",rel:"noopener noreferrer"},ie={href:"https://developer.apple.com/xcode/swiftui/",target:"_blank",rel:"noopener noreferrer"},re={href:"https://www.jetbrains.com/lp/compose-multiplatform/",target:"_blank",rel:"noopener noreferrer"},pe={class:"custom-container tip"},ce=e("p",{class:"custom-container-title"},"Compose multiplatform vs Jetpack Compose",-1),de={href:"https://developer.android.com/jetpack/compose/tutorial",target:"_blank",rel:"noopener noreferrer"},me=e("div",{class:"custom-container warning"},[e("p",{class:"custom-container-title"},"Compose HTML is not cross-platform"),e("p",null,"Compose HTML is UI a library targeting Kotlin/JS which is not compatible with Compose Multiplatform (it is a different API). For cross-platform UI development with Compose Multiplatform, compose Web is the choice.")],-1),ue=e("h3",{id:"🧪-create-a-compose-multiplatform-app",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#🧪-create-a-compose-multiplatform-app"},[e("span",null,"🧪 Create a Compose multiplatform app")])],-1),he={href:"https://github.com/JetBrains/compose-multiplatform-template",target:"_blank",rel:"noopener noreferrer"},fe={href:"https://github.com/JetBrains/compose-multiplatform-template#set-up-the-environment",target:"_blank",rel:"noopener noreferrer"},ge=e("ul",null,[e("li",null,"On Windows and Linux, we don't need to install iOS/macOS related tools but and we won't be able to run iOS/macOS targets."),e("li",null,[t("If we don't want to install Android Studio, we need at least to install the Android SDK either through the official installer or from the "),e("em",null,'"Languages and Framework -> Android SDK"'),t(" menu in the settings.")])],-1),ke={href:"https://github.com/JetBrains/compose-multiplatform-template",target:"_blank",rel:"noopener noreferrer"},be=e("em",null,"use this template",-1),_e=a("Open the downloaded projet. You'll note that it contains these modules: - a shared module (or subproject) that contains common code as well as
- and another module for earch targeted platform: androidApp, iOSApp and desktopApp (When web will be included in the template, we should also see a webApp project). These contain the source code of the apps itself (such as the main activity in Android, the
@main App
in iOS and the main function in desktopJVM) and well as platform specific resources that cannot be placed in the shared module. Some examples of such files are the AndroidManifest.xml for android and the info.plist in iOS.
In order to run the desktopApp, open a terminal on the project root folder and launch this command: ./gradlew desktopApp:run
.",2),we=e("img",{src:c,alt:"Alt text",loading:"lazy"},null,-1),ve={href:"https://gist.github.com/MoshDev/a61080cc5e1f5bafdf3cc0bf70fd86fd",target:"_blank",rel:"noopener noreferrer"},Se={href:"https://github.com/JetBrains/compose-multiplatform-template#on-ios",target:"_blank",rel:"noopener noreferrer"},Ae=a('
Alt textCompose multiplatform is a component based declarative UI framework. Each component is called a Composable
and is defined as a function annotated with @Composable
.
In compose multiplatform, the main component (the component at the root of the App) is usually found in shared/src/commonMain/Kotlin/App.kt.
- Take a look at shared/src/commonMain/Kotlin/App.kt, run the app and try to understand how compose works.
- Let's create a new composable called
RandomNumberList
.
@Composable
+fun RandomNumberList(){
+
+ val myRandomValues = List(5) { Random.nextInt(0, 30) }
+
+ LazyColumn {
+ items(myRandomValues.size){
+ Text(text = "$it")
+ }
+ }
+}
+
- Place this composable below
AnimatedVisibility
and Button
and run the app.
- Exercise: Make the "Hello, .." button switch between showing the list and and the image.
Hello compose demo',11),ye={href:"https://github.com/worldline/learning-kotlin/tree/main/material/webapp-kotlin-wasm",target:"_blank",rel:"noopener noreferrer"},xe={href:"https://github.com/worldline/learning-kotlin/tree/main/material/app-compose-multiplatform",target:"_blank",rel:"noopener noreferrer"},Ke=e("h2",{id:"📖-further-reading",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#📖-further-reading"},[e("span",null,"📖 Further reading")])],-1),Me={href:"https://seb.deleuze.fr/the-huge-potential-of-kotlin-wasm/",target:"_blank",rel:"noopener noreferrer"},We={href:"https://kmp.jetbrains.com/",target:"_blank",rel:"noopener noreferrer"};function Ie(Je,je){const n=i("ExternalLinkIcon");return r(),p("div",null,[h,f,g,e("ul",null,[e("li",null,[t("On the "),k,t(" side "),e("ul",null,[e("li",null,[t("Thanks to JVM support, Kotlin supports "),e("a",b,[t("JavaFX"),o(n)]),t(" (There "),_,t(" a Kotlin counterpart called "),e("a",w,[t("tornadofx"),o(n)]),t(" which is not maintained anymore).")]),e("li",null,[e("a",v,[t("Compose Multiplatform"),o(n)]),t(" brings Jetpack Compose to the desktop, the web and mobile.")])])]),e("li",null,[t("On the "),S,e("ul",null,[e("li",null,[e("a",A,[t("Ktor"),o(n)]),t(" can use templates engines "),e("a",y,[t("such as FreeMarker"),o(n)]),t(" to create server pages.")]),x,K])]),e("li",null,[t("On "),M,e("ul",null,[e("li",null,[t("Android developers use the "),e("a",W,[t("Jetpack Compose"),o(n)]),t(" UI Framework or the legacy "),I,t(". It is experimental on iOS.")])])])]),J,j,e("blockquote",null,[e("p",null,[t('"The Kotlin Multiplatform technology is designed to simplify the development of cross-platform projects. It reduces time spent writing and maintaining the same code for different platforms while retaining the flexibility and benefits of native programming." '),e("a",C,[t("₁"),o(n)])])]),e("p",null,[e("a",P,[t("KMP"),o(n)]),t(" relies on Kotlin native and other Kotlin features to help developers create projects that target multiple platforms using a common Kotlin code-base.")]),B,T,e("ul",null,[e("li",null,[e("a",O,[t("Full-Stack web apps"),o(n)]),t(": A project that contains a backend and a web app while sharing common logic.")]),e("li",null,[e("a",L,[t("Multiplatform libraries"),o(n)])])]),q,e("ul",null,[z,D,e("li",null,[t("Maybe we can do even more in the future with as all these technologies (Kotlin, WASM and Kotlin/WASM) evolve. - For example, "),e("a",F,[t("WASI"),o(n)]),t(" allows WASM to communicate with the operating system. - This means that me may see Kotlin/WASM project projects in the future that can target both the browser and the OS.")]),R]),N,e("ul",null,[e("li",null,[t("Let's create a Kotlin/WASM app. By cloning "),U,t(" and opening the "),V,t(" folder in your IDE. "),e("ul",null,[e("li",null,[t("To get up to date information on how to start a Kotlin/WASM project, please refer to the "),e("a",E,[t("official documentation for kotlin/wasm"),o(n)]),t(".")])])]),H,e("li",null,[t("The development server should start and you can open your WASM powered webapp on "),e("a",G,[t("http://localhost:8080/"),o(n)]),Y])]),$,e("ul",null,[Q,e("li",null,[t("Next, let's check the generated wasm file which is available in "),X,e("ul",null,[Z,e("li",null,[t("We can either install "),e("a",ee,[t("WABT (The WebAssembly Binary Toolkit or wabbit)"),o(n)]),t(" and use the "),te,t(),ne,t(",")]),e("li",null,[t("or use an online converter "),e("a",oe,[t("such as this one"),o(n)])]),ae])])]),le,e("p",null,[t("It is based on "),e("a",se,[t("Android Jetpack Compose"),o(n)]),t(" declarative UI approach ( which is similar also to "),e("a",ie,[t("iOS SwiftUI"),o(n)]),t(" ) "),e("a",re,[t("1"),o(n)])]),e("div",pe,[ce,e("p",null,[t("While very similar, Compose multiplatform is different from Jetpack Compose as the latter is only compatible with Android. Google provides a "),e("a",de,[t("JetPack compose tutorial"),o(n)]),t(" for Android development.")])]),me,ue,e("p",null,[t("We'll create a multiplatform app using the "),e("a",he,[t("official template"),o(n)]),t(". At the time of writing, this template does not include a compose web target.")]),e("ul",null,[e("li",null,[t("Please check that your environment is correctly setup "),e("a",fe,[t("as explained here"),o(n)]),t(". "),ge]),e("li",null,[t("Open the "),e("a",ke,[t("official template"),o(n)]),t(' and either download a zip or use the "'),be,t('" options on GitHub.')]),_e,e("li",null,[t("In order to run the Android App, the simplest way is to launch it from IntelliJ "),we,t(". It is also possible "),e("a",ve,[t("define a gradle task"),o(n)]),t(" that installs the app on the device and issues a command to the device to launch it.")]),e("li",null,[t("In order to run the iOS App, the simplest way is to run it on the simulator using IntelliJ. In order to run it on a real device, the TramID needs to be defined as "),e("a",Se,[t("explained here"),o(n)])])]),Ae,e("ul",null,[e("li",null,[e("a",ye,[t("Kotlin/JS and Kotlin/WASM PW"),o(n)])]),e("li",null,[e("a",xe,[t("Compose multiplatform PW2"),o(n)])])]),Ke,e("ul",null,[e("li",null,[e("a",Me,[t("The huge potential of Kotlin/WASM"),o(n)])]),e("li",null,[e("a",We,[t("Official KMP wizard"),o(n)])])])])}const Be=s(u,[["render",Ie],["__file","index.html.vue"]]),Te=JSON.parse('{"path":"/en/front-development/","title":"📚 Frontend development","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Kotlin Multiplatform (KMP)","slug":"kotlin-multiplatform-kmp","link":"#kotlin-multiplatform-kmp","children":[]},{"level":2,"title":"Kotlin/JS and Kotlin/WASM","slug":"kotlin-js-and-kotlin-wasm","link":"#kotlin-js-and-kotlin-wasm","children":[{"level":3,"title":"🧪 Kotlin/WASM web app","slug":"🧪-kotlin-wasm-web-app","link":"#🧪-kotlin-wasm-web-app","children":[]},{"level":3,"title":"Kotlin/JS and Kotlin/WASM common points","slug":"kotlin-js-and-kotlin-wasm-common-points","link":"#kotlin-js-and-kotlin-wasm-common-points","children":[]}]},{"level":2,"title":"Compose multiplatform","slug":"compose-multiplatform","link":"#compose-multiplatform","children":[{"level":3,"title":"🧪 Create a Compose multiplatform app","slug":"🧪-create-a-compose-multiplatform-app","link":"#🧪-create-a-compose-multiplatform-app","children":[]},{"level":3,"title":"🧪 Playing with the Compose multiplatform API","slug":"🧪-playing-with-the-compose-multiplatform-api","link":"#🧪-playing-with-the-compose-multiplatform-api","children":[]}]},{"level":2,"title":"🎯 Solutions","slug":"🎯-solutions","link":"#🎯-solutions","children":[]},{"level":2,"title":"📖 Further reading","slug":"📖-further-reading","link":"#📖-further-reading","children":[]}],"git":{"updatedTime":1702464292000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":25},{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":3},{"name":"Ibrahim Gharbi","email":"brah.gharbi@gmail.com","commits":1}]},"filePathRelative":"en/front-development/README.md"}');export{Be as comp,Te as data};
diff --git a/assets/index.html-hW5i8q7d.js b/assets/index.html-hW5i8q7d.js
new file mode 100644
index 00000000..6fac4106
--- /dev/null
+++ b/assets/index.html-hW5i8q7d.js
@@ -0,0 +1,195 @@
+import{_ as c,r as o,o as r,c as u,a as n,b as s,d as e,w as i,e as t}from"./app-jR2rC7Ae.js";const d={},k=n("h1",{id:"📚-developpement-du-backend",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#📚-developpement-du-backend"},[n("span",null,"📚 Développement du backend")])],-1),m={href:"https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0",target:"_blank",rel:"noopener noreferrer"},v={href:"https://quarkus.io/guides/kotlin",target:"_blank",rel:"noopener noreferrer"},g={href:"https://github.com/ktorio/ktor",target:"_blank",rel:"noopener noreferrer"},b={href:"https://kotlinlang.org/docs/server-overview.html#deploying-kotlin-server-side-applications",target:"_blank",rel:"noopener noreferrer"},h=n("p",null,"En outre, Kotlin est théoriquement compatible avec tout framework qui cible la JVM ou JS. Cependant, les frameworks qui ne supportent pas officiellement Kotlin peuvent nécessiter quelques ajustements pour l'utiliser.",-1),f=n("h2",{id:"ktor",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#ktor"},[n("span",null,"Ktor")])],-1),_=n("p",null,"Ktor est une bibliothèque Kotlin multiplateforme permettant de développer des clients et des serveurs HTTP. Cela fait de Ktor une bibliothèque utile à la fois aux développeurs frontend, pour la partie client HTTP, ainsi qu'aux développeurs backend, pour la partie serveur HTTP. Dans ce qui suit, nous allons créer une API REST avec le serveur Ktor.",-1),q=n("h3",{id:"tp-developper-une-api-avec-ktor",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#tp-developper-une-api-avec-ktor"},[n("span",null,"TP : développer une API avec Ktor")])],-1),y={href:"https://start.ktor.io/",target:"_blank",rel:"noopener noreferrer"},x=t("Cliquez sur "Generate project".Téléchargez l'archive, décompressez-la et ouvrez le projet avec votre IDE préféré.Créez un package models
et ajoutez-y une classe de données Customer
avec ces propriétés immuables id : String, firstName : String, lastName : Chaîne, email : Chaîne
.Annotez la classe avec @Serializable
.Créez un nouveau package nommé routes
et ajoutez-y un fichier CustomerRoutes.kt
qui contiendra le code pour l'endpoint /customer
.Le code ci-dessous fournit l'implémentation de certains endpoints. Veuillez implémenter les autres.Pour activer la route, appelez customerRouting()
dans le fichier de configuration du routage situé dans plugins/Routing.kt
.Pour plus de simplicité, utilisez une liste globale de clients en mémoire val store = mutableListOf<Customer>()
.Lancer le serveur en exécutant la méthode main.Tester l'API sur l'IDE en utilisant un fichier http ou en utilisant n'importe quel autre client.",10),S=t(`CustomerRoutes.kt
val store = mutableListOf<Customer>()
+
+fun Route.customerRouting() {
+ route("/customer") {
+ get {
+ call.respond(store)
+ }
+ get("{id?}") {
+ val id = call.parameters["id"] ? : return@get call.respondText(
+ "Missing id",
+ status = HttpStatusCode.BadRequest
+ )
+ val customer =
+ store.find { it.id == id } ? : return@get call.respondText(
+ "Pas de client avec l'id $id",
+ status = HttpStatusCode.NotFound
+ )
+ call.respond(customer)
+ }
+ post {
+ val customer = call.receive<Customer>()
+ store.add(customer)
+ call.respondText("Customer stored correctly", status = HttpStatusCode.Created)
+ }
+ delete("{id?}") {
+
+ }
+ }
+}
+
+
plugins/Routing.kt
fun Application.configureRouting() {
+ routing {
+ customerRouting()
+ }
+}
+
`,2),w={class:"custom-container tip"},j=t(`return@label
Vous pouvez spécifier le niveau que vous voulez retourner avec un label explicite en utilisant return@lambda
.
lambdaA {
+ lambdaB {
+ lambdaC {
+ val randomInt = Random.nextInt(0, 100)
+ if (randomInt > 50) return@lambdaC else return@lambdaB
+ }
+ printf("In lambdaB")
+ }
+}
+
`,3),C={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGtvdGxpbi5yYW5kb20uUmFuZG9tXG5cbi8vIHJldHVybiB3aXRoIGltcGxpY2l0IGxhYmVsc1xuXG5mdW4gbWFpbigpIHtcbiAgIHZhbCBrb3RsaW4gPSBcIvCfmYJcIlxuICAga290bGluLmxldCB7XG4gICAgICAgaXQuYXBwbHkge1xuXHRcdFx0dmFsIHJhbmRvbUludCA9IFJhbmRvbS5uZXh0SW50KDAsIDEwMClcbiAgICAgICAgICAgIHByaW50bG4ocmFuZG9tSW50KVxuICAgICAgICAgICAgaWYgKHJhbmRvbUludCA+IDUwKSByZXR1cm5AYXBwHkgZWxzZSByZXR1cm5AbGV0XG4gICAgICAgfVxuICAgICAgIHByaW50bG4oXCJpbnQgbV0IGFmdGVyIGFwcGx5XCIpXG4gICB9XG59In0=",target:"_blank",rel:"noopener noreferrer"},P=t(`CustomerTest.http
POST http://127.0.0.1:8080/customer
+Content-Type : application/json
+
+{
+ "id" : "100",
+ "firstName" : "Jane",
+ "lastName" : "Smith",
+ "email" : "jane.smith@company.com"
+}
+
+
+###
+POST http://127.0.0.1:8080/customer
+Content-Type : application/json
+
+{
+ "id" : "200",
+ "firstName" : "John",
+ "lastName" : "Smith",
+ "email" : "john.smith@company.com"
+}
+
+###
+POST http://127.0.0.1:8080/customer
+Content-Type : application/json
+
+{
+ "id" : "300",
+ "firstName" : "Mary",
+ "lastName" : "Smith",
+ "email" : "mary.smith@company.com"
+}
+
+
+###
+GET http://127.0.0.1:8080/customer
+Accept : application/json
+
+###
+GET http://127.0.0.1:8080/customer/200
+Accepte : application/json
+
+###
+GET http://127.0.0.1:8080/customer/500
+Accepte : application/json
+
+###
+DELETE http://127.0.0.1:8080/customer/100
+
+###
+DELETE http://127.0.0.1:8080/customer/500
+
`,1),I={href:"https://ktor.io/docs/creating-http-apis.html",target:"_blank",rel:"noopener noreferrer"},T=n("h2",{id:"nodejs",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#nodejs"},[n("span",null,"nodejs")])],-1),A=n("p",null,"Grâce à Kotlin/JS, nous pouvons écrire des applications qui ciblent nodejs en utilisant Kotlin.",-1),R=n("strong",null,"déclaration externe",-1),E={href:"https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.js/-js-module/",target:"_blank",rel:"noopener noreferrer"},z={href:"https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.js/-js-non-module/",target:"_blank",rel:"noopener noreferrer"},G={href:"https://github.com/Kotlin/dukat",target:"_blank",rel:"noopener noreferrer"},K=n("p",null,"Heureusement pour nous, le prochain TP utilise la librairie Express pour laquelle nous pouvons trouver une déclaration de type externe.",-1),B=n("h3",{id:"tp-api-rest-avec-kotlin-js-et-express",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#tp-api-rest-avec-kotlin-js-et-express"},[n("span",null,"TP : API Rest avec Kotlin/JS et Express")])],-1),D=n("li",null,"Dans IntelliJ, créer un nouveau projet nodejs",-1),J=n("strong",null,"build.gradle.ts",-1),L=n("li",null,[s("Mettre la dernière version de Kotlin dans la ligne contenant "),n("code",null,'kotlin("js")'),s(".")],-1),O={href:"https://expressjs.com/",target:"_blank",rel:"noopener noreferrer"},M=n("strong",null,"Express",-1),V={href:"https://github.com/chrisnkrueger/kotlin-express",target:"_blank",rel:"noopener noreferrer"},N=n("code",null,"useCommonJs()",-1),H=n("code",null,"js",-1),X={href:"https://github.com/Kotlin/dukat/issues/106",target:"_blank",rel:"noopener noreferrer"},W={href:"https://github.com/chrisnkrueger/kotlin-express",target:"_blank",rel:"noopener noreferrer"},Y=t(`implementation(npm("express", "> 4.0.0 < 5.0.0"));
+implementation("dev.chriskrueger:kotlin-express:1.2.0");
+
- Modifiez main.kt comme suit. Cela crée un serveur API REST qui écoute le port 3000 et fournit une route GET /hello.
data class Message(val id : Int, val message : String)
+
+fun main() {
+ val messages = listOf(Message(0, "I love Kotlin/JS"))
+ val app = express.Express()
+ app.get("/hello") { req, res ->
+ res.send(messages)
+ }
+
+ app.listen(3000) {
+ console.log("server start at port 3000")
+ }
+}
+
`,3),Z=n("li",null,[s("Exécutez la tâche "),n("code",null,"nodeRun"),s(" depuis votre IDE ou depuis la ligne de commande (si vous avez installé Gradle). "),n("ul",null,[n("li",null,[s("Si vous rencontrez une erreur avec Yarn lock, exécutez la tâche "),n("code",null,"kotlinUpgradeYarnLock"),s(" puis réessayez.")])])],-1),U=n("li",null,"Ajouter des routes en POST, PUT et DELETE",-1),F=n("code",null,"req.body",-1),Q=n("code",null,"undefined",-1),$=n("strong",null,"body parser",-1),nn=n("li",null,[s("Pour un corps en JSON, nous devons appeler "),n("code",null,"app.use(bodyParser.json())"),s(".")],-1),sn={href:"https://www.npmjs.com/package/body-parser",target:"_blank",rel:"noopener noreferrer"},en=n("strong",null,"bodyParser",-1),an={href:"https://github.com/chrisnkrueger/kotlin-express",target:"_blank",rel:"noopener noreferrer"},tn=n("strong",null,"bodyParser",-1),on={href:"https://www.npmjs.com/package/body-parser?activeTab=code",target:"_blank",rel:"noopener noreferrer"},ln={href:"https://github.com/worldline/learning-kotlin/blob/main/material/kotlin-nodejs-demo/src/main/kotlin/BodyParser.kt",target:"_blank",rel:"noopener noreferrer"},pn=n("h2",{id:"spring-framework",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-framework"},[n("span",null,"Spring framework")])],-1),cn=n("p",null,"Spring est un framework célèbre pour le développement d'applications côté serveur : API REST, pages web générées par le serveur, microservices, etc. Il s'appuie sur l'écosystème Java pour la compilation et l'exécution, ce qui le rend compatible avec Kotlin. Mieux encore, Spring supporte officiellement Kotlin. On peut même démarrer un nouveau projet avec Kotlin et Gradle-Kotlin. Dans la prochaine section, nous utiliserons ce projet pour recréer notre API REST plus haut avec Spring.",-1),rn=n("h3",{id:"tp-spring-boot-part-1-developper-la-meme-api-avec-spring-boot",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#tp-spring-boot-part-1-developper-la-meme-api-avec-spring-boot"},[n("span",null,"TP : Spring boot part 1 - développer la même API avec Spring Boot")])],-1),un={href:"https://start.spring.io/",target:"_blank",rel:"noopener noreferrer"},dn=n("li",null,"Choisissez Kotlin comme langage et Kotlin-Grade comme gestionnaire de projet.",-1),kn=n("li",null,[s("Ajoutez les dépendances suivantes : "),n("strong",null,"Spring Web"),s(" et "),n("strong",null,"Spring Boot DevTools"),s(".")],-1),mn=n("strong",null,"Generate",-1),vn={href:"https://marketplace.visualstudio.com/search?term=kotlin&target=VSCode&category=All%20categories&sortBy=Relevance",target:"_blank",rel:"noopener noreferrer"},gn={href:"https://marketplace.visualstudio.com/items?itemName=Pivotal.vscode-boot-dev-pack",target:"_blank",rel:"noopener noreferrer"},bn=n("li",null,[s("Vérifiez que la partie plugins "),n("code",null,"build.gradle.kts"),s(" utilise la dernière version de Kotlin. Voici à quoi cela devrait ressembler avec Kotlin "),n("em",null,"1.8.10"),s(" :")],-1),hn=t(`plugins {
+ id("org.springframework.boot") version "3.0.4"
+ id("io.spring.dependency-management") version "1.1.0"
+ kotlin("jvm") version "1.8.10"
+ kotlin("plugin.spring") version "1.8.10"
+}
+
`,1),fn=t("Créez la data class Customer
dans le package model
(sans l'annotation @Serializable
).Créez un paquetage controller
qui contient une classe CustomerController
qui fournit un CRUD en utilisant une liste globale. - Vous pouvez trouver un squelette ci-dessous.
- 💡 Dans Spring, les contrôleurs Rest servent de routes Ktor, où un contrôleur définit une ressource REST.
Définissez les mêmes routes que dans le TP précédent.Démarrez le serveur de l'API REST en exécutant : - Sur Powershell :
.\\gradlew.bat bootRun
- Tout shell Unix :
.\\gradlew bootRun
- Ou bien, vérifiez si votre IDE fournit déjà des configurations d'exécution pour les projets Spring Boot.
",4),_n=n("strong",null,"http",-1),qn={href:"https://github.com/worldline/learning-kotlin/blob/main/material/spring-boot-kt-api/customer.jetbrains.http",target:"_blank",rel:"noopener noreferrer"},yn={href:"https://github.com/worldline/learning-kotlin/blob/main/material/spring-boot-kt-api/customer.vscode-resclient.http",target:"_blank",rel:"noopener noreferrer"},xn=t(`CustomerController.kt
val store = mutableListOf<Customer>()
+
+@RestController
+@RequestMapping("/customer")
+class CustomerController {
+ @GetMapping
+ fun getAll() = store
+
+ @GetMapping("{id}")
+ fun getById(@PathVariable id : String) { }
+
+ @PostMapping
+ fun addOne(@RequestBody customer : Customer) { }
+
+ @DeleteMapping("{id}")
+ fun deleteOne(@PathVariable id : String) { }
+}
+
Allons un peu plus loin en stockant des données dans une base de données et en écrivant quelques tests.
Nous utiliserons la base de données en mémoire H2 pour des raisons de simplicité, puisqu'elle ne nécessite pas de serveur pour fonctionner. Les classes seront mappées aux tables de la base de données avec des annotations JPA. L'API de base de données que nous utiliserons s'appelle JPARepository
. C'est une API légère qui fournit des fonctionnalités CRUD communes à partir d'une simple une interface.
`,4),Sn={href:"https://start.spring.io/",target:"_blank",rel:"noopener noreferrer"},wn=t("Ouvrez le projet et ajoutez cette classe dans le package model
@Entity class Product(@Id @GeneratedValue var id : Long ? = null, var name : String, var price : Int)
. Ceci définit la classe ainsi que les annotations JPA minimales (@Entity
, @Id
et @GeneratedValue
) pour générer la table correspondante.Dans le package repository
, déclarez l'interface ProductRepository
comme suit interface ProductRepository : JpaRepository<Produit, Long>
. C'est suffisant pour que Spring génère une implémentation avec des caractéristiques communes comme nous le verrons plus tard.Ensuite, créez une classe ProductService
qui contiendra la logique métier. En termes d'architecture, le contrôleur appelle un service qui, à son tour, s'appuie sur d'autres services ou référentiels.",3),jn=t(`ProductService.kt
@Service
+class ProductService(@Autowired val productRepository: ProductRepository) {
+ fun getAll() = productRepository.findAll()
+
+
+ fun getById(id: Long) = productRepository.findByIdOrNull(id)
+}
+
- Dans le package controller, créez une classe
ProductController
qui est mappée à /product
et injectée avec @Autowired
. Répondez à @Get
comme suit.
ProductController.kt
@RestController
+@RequestMapping("/product")
+class ProductController(@Autowired val productService : ProductService) {
+ @GetMapping fun getAll() = productService.getAll()
+
+ @GetMapping("{id}")
+ fun getById(@PathVariable id : Long) =
+ productService.getById(id) ? : throw ResponseStatusException(HttpStatus.NOT_FOUND)
+}
+
Kotlin rend getById(@PathVariable id : Long) plus concis
L'opérateur Elvis ?:
permet de simplifier le code. Voici une version plus longue en guise de référence.
@GetMapping("{id}")
+fun getById(@PathVariable id : Long) : Produit {
+ val product = productService.getById(id)
+ if (product != null){
+ return product
+ }
+ throw ResponseStatusException(HttpStatus.NOT_FOUND)
+}
+
En outre, Spring fournit @ControllerAdvice
pour modifier le message d'exception. Vous pouvez voir un [exemple ici] (https://spring.io/guides/tutorials/rest/).
- Exécutons le projet. Avant de lancer le projet, nous devons ajouter un plugin qui permet aux classes Kotlin de générer un constructeur par défaut
id("org.jetbrains.kotlin.plugin.jpa") version "1.8.10"
. Les plugins devraient ressembler à ce qui suit :
plugins {
+ id("org.jetbrains.kotlin.plugin.jpa") version "1.8.10"
+ id("org.springframework.boot") version "3.0.4"
+ id("io.spring.dependency-management") version "1.1.0"
+ kotlin("jvm") version "1.8.10"
+ kotlin("plugin.spring") version "1.8.10"
+}
+
+
- En guise d'exercice, implémentez ces routes : POST d'un seul produit, DELETE par id (
/produit/{id}
) et GET par id (/produit/{id}
). - Indice :
ProductController
fournit déjà les méthodes nécessaires.
- Appelez les différents points de terminaison avec un client REST.
- Tester votre API Rest avec un client HTTP
Les frameworks Spring permettent d'effectuer différents types de tests en fournissant différentes classes dès le départ :
- Tests unitaires/de composants des services et de l'API REST. Cela se fait par le biais d'utilitaires de bouchonnage tels que
MockMVC
. - Tests d'intégration de l'API REST en utilisant
TestRestTemplate
. Dans ce cas, un serveur complet est exécuté et testé.
La plupart des classes fournies par Spring, si ce n'est toutes, offrent une syntaxe élégante pour les développeurs Java. Certaines d'entre elles vont plus loin en tirant parti des caractéristiques spécifiques de Kotlin. Dans ce qui suit, nous allons nous concentrer sur les parties qui fournissent des DSLs Kotlin, à savoir le test unitaire de l'API REST avec MockMVC
.
- Créer une classe de test
ProductControllerUnitTests
avec le contenu initial ci-dessous. MockMvc
permet de tester unitairement l'API REST. L'annotation @AutoConfigureMockMvc
permet à Spring de la configurer automatiquement.
@SpringBootTest
+@AutoConfigureMockMvc
+classe ProductControllerTests(
+ @Autowired val mockMvc : MockMvc,
+ @Autowired val productRepository : ProductRepository) {
+
+ @BeforeEach
+ fun reset(){
+ productRepository.deleteAll()
+ }
+}
+
- Ajoutez les deux tests ci-dessous. Le premier utilise une approche classique tandis que le second tire parti des capacités du DSL de Kotlin. De plus, nous utilisons une chaîne littérale plus lisible.
`,14),Cn=n("div",{class:"language-kotlin line-numbers-mode","data-ext":"kt","data-title":"kt"},[n("pre",{class:"language-kotlin"},[n("code",null,[n("span",{class:"token annotation builtin"},"@Test"),s(`
+`),n("span",{class:"token keyword"},"fun"),s(),n("span",{class:"token function"},"testWithClassicApproach"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},"{"),s(`
+ mockMvc`),n("span",{class:"token punctuation"},"."),n("span",{class:"token function"},"perform"),n("span",{class:"token punctuation"},"("),n("span",{class:"token keyword"},"get"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"/product"')]),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},")"),s(`
+ `),n("span",{class:"token punctuation"},"."),n("span",{class:"token function"},"andExpect"),n("span",{class:"token punctuation"},"("),n("span",{class:"token function"},"status"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},"."),s("isOk"),n("span",{class:"token punctuation"},")"),s(`
+ `),n("span",{class:"token punctuation"},"."),n("span",{class:"token function"},"andExpect"),n("span",{class:"token punctuation"},"("),n("span",{class:"token function"},"content"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},"."),n("span",{class:"token function"},"string"),n("span",{class:"token punctuation"},"("),n("span",{class:"token function"},"containsString"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"[]"')]),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},")"),s(`
+`),n("span",{class:"token punctuation"},"}"),s(`
+`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),Pn=n("div",{class:"language-kotlin line-numbers-mode","data-ext":"kt","data-title":"kt"},[n("pre",{class:"language-kotlin"},[n("code",null,[n("span",{class:"token annotation builtin"},"@Test"),s(`
+`),n("span",{class:"token keyword"},"fun"),s(),n("span",{class:"token function"},"`test GET a single product`"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"{"),s(`
+ mockMvc`),n("span",{class:"token punctuation"},"."),n("span",{class:"token function"},"get"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"/product/1"')]),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},"."),n("span",{class:"token function"},"andExpect"),s(),n("span",{class:"token punctuation"},"{"),s(`
+ status `),n("span",{class:"token punctuation"},"{"),s(),n("span",{class:"token function"},"isOk"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"}"),s(`
+ `),n("span",{class:"token function"},"jsonPath"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"$.name"')]),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"{"),s(),n("span",{class:"token function"},"value"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"A"')]),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"}"),s(`
+ `),n("span",{class:"token function"},"jsonPath"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"$.price"')]),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"{"),s(),n("span",{class:"token function"},"value"),n("span",{class:"token punctuation"},"("),n("span",{class:"token number"},"1"),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"}"),s(`
+ content `),n("span",{class:"token punctuation"},"{"),s(),n("span",{class:"token function"},"contentType"),n("span",{class:"token punctuation"},"("),s("MediaType"),n("span",{class:"token punctuation"},"."),s("APPLICATION_JSON"),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"}"),s(`
+ `),n("span",{class:"token punctuation"},"}"),s(`
+`),n("span",{class:"token punctuation"},"}"),s(`
+`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),In=t(`- En guise d'exercice, écrire des tests pour les autres points d'accès.
Le constructeur de requêtes de JpaRepository
Les repository Spring implémentent des requêtes basées sur le nom de leurs méthodes. Par exemple, pour obtenir tous les produits triés par nom, nous pouvons ajouter cette méthode à l'interface.
interface ProductRepository : JpaRepository<Produit, Long> {
+ fun findAllByOrderByNameAsc() : List<Produit> ;
+}
+
La [documentation officielle] (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation) fournit des explications et des exemples plus détaillés.
`,3),Tn={href:"https://github.com/worldline/learning-kotlin/tree/master/material/ktor-api",target:"_blank",rel:"noopener noreferrer"},An={href:"https://github.com/worldline/learning-kotlin/tree/master/material/kotlin-nodejs-demo",target:"_blank",rel:"noopener noreferrer"},Rn={href:"https://github.com/worldline/learning-kotlin/tree/master/material/spring-boot-kt-api",target:"_blank",rel:"noopener noreferrer"},En=n("h2",{id:"aller-plus-loin",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#aller-plus-loin"},[n("span",null,"Aller plus loin")])],-1),zn=n("p",null,"Ces tutoriels officiels vont encore plus loin :",-1),Gn={href:"https://kotlinlang.org/docs/jvm-spring-boot-restful.html",target:"_blank",rel:"noopener noreferrer"},Kn={href:"https://spring.io/guides/tutorials/spring-boot-kotlin/",target:"_blank",rel:"noopener noreferrer"},Bn={href:"https://spring.io/guides/tutorials/rest/",target:"_blank",rel:"noopener noreferrer"},Dn={href:"https://quarkus.io/guides/kotlin",target:"_blank",rel:"noopener noreferrer"},Jn=n("h2",{id:"lien-et-references",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#lien-et-references"},[n("span",null,"Lien et références")])],-1),Ln={href:"https://dev.to/mpetuska/js-in-kotlinjs-c4g",target:"_blank",rel:"noopener noreferrer"},On={href:"https://www.baeldung.com/kotlin/mockmvc-kotlin-dsl",target:"_blank",rel:"noopener noreferrer"},Mn={href:"https://spring.io/guides/tutorials/spring-boot-kotlin/",target:"_blank",rel:"noopener noreferrer"},Vn={href:"https://www.baeldung.com/kotlin/jpa",target:"_blank",rel:"noopener noreferrer"},Nn={href:"https://stackoverflow.com/questions/47143127/spring-data-jpa-how-to-use-kotlin-nulls-instead-of-optional",target:"_blank",rel:"noopener noreferrer"};function Hn(Xn,Wn){const a=o("ExternalLinkIcon"),l=o("CodeGroupItem"),p=o("CodeGroup");return r(),u("div",null,[k,n("p",null,[s("De nombreux frameworks supportent officiellement Kotlin comme "),n("a",m,[s("Spring"),e(a)]),s(", "),n("a",v,[s("Quarkus"),e(a)]),s(" et "),n("a",g,[s("Ktor"),e(a)]),s(", parmi d'autres "),n("a",b,[s("listés ici"),e(a)]),s(".")]),h,f,_,q,n("ul",null,[n("li",null,[s("Créez un projet sur "),n("a",y,[s("start.ktor.io"),e(a)]),s(" avec les plugins suivants : Content Negotiation, kotlinx.serialization, et Routing.")]),x]),S,n("div",w,[j,n("p",null,[n("a",C,[s("Ce code exécute un autre exemple"),e(a)]),s(".")])]),P,n("p",null,[n("a",I,[s("Cette page contient des étapes détaillées"),e(a)])]),T,A,n("p",null,[s("On peut même importer des librairies npm à condition de déclarer les API JS que l'on va utiliser en Kotlin. C'est ce qu'on appelle une "),R,s(" (vous pouvez la considérer comme un équivalent des définitions de type de TypeScript) qui déclare les symboles auxquels nous voulons accéder en Kotlin grâce aux annotations "),n("a",E,[s("@JsModule"),e(a)]),s(" et "),n("a",z,[s("@JsNonModule"),e(a)]),s(". Définir de telles déclarations externes peut s'avérer fastidieux et il ne semble pas y avoir de générateur automatique officiel et stable ("),n("a",G,[s("dukat"),e(a)]),s(" a été supprimé dans kotlin 1.8.20). Dans ce cas, nous avons deux options, soit écrire la déclaration externe nous-même, soit l'importer en tant que dépendance si elle est disponible.")]),K,B,n("ul",null,[D,n("li",null,[s("Une fois le projet chargé, éditer "),J,s(" comme suit : "),n("ul",null,[L,n("li",null,[s("Ajoutez ces deux dépendances: "),n("ul",null,[n("li",null,[s("La première est la bibliothèque "),n("a",O,[M,e(a)]),s(",")]),n("li",null,[s("et la seconde est la définition externe fournie par "),n("a",V,[s("chrisnkrueger/kotlin-express"),e(a)]),s(".")])])]),n("li",null,[s("ajouter une ligne "),N,s(" à l'intérieur du bloc "),H,s(". Ceci "),n("a",X,[s("est nécessaire"),e(a)]),s(" pour pouvoir utiliser "),n("a",W,[s("chrisnkrueger/ kotlin-express"),e(a)]),s(" dans notre code.")])])])]),Y,n("ul",null,[Z,U,n("li",null,[s("En ce qui concerne le corps du POST, Express positionne "),F,s(" à "),Q,s(" à moins que nous ne spécifions un "),$,s(". "),n("ul",null,[nn,n("li",null,[n("a",sn,[en,e(a)]),s(" est une bibliothèque npm et malheureusement, "),n("a",an,[s("chrisnkrueger/kotlin-express"),e(a)]),s(" ne fournit pas de définition externe pour "),tn,s(" au moment de l'écriture de ces lignes (chrisnkrueger/kotlin-express en version 1.2.0).")]),n("li",null,[s("Pouvez-vous essayer de la définir vous-même en lisant le "),n("a",on,[s("code de la bibliothèque"),e(a)]),s(" ?")]),n("li",null,[s("Vous pouvez trouver une solution "),n("a",ln,[s("ici"),e(a)])])])])]),pn,cn,rn,n("ul",null,[n("li",null,[s("Créez un projet sur "),n("a",un,[s("start.spring.io (aussi appelé Spring initializr)"),e(a)]),s(" avec les dépendances suivantes : Spring Web et Spring Boot DevTools.")]),dn,kn,n("li",null,[s("Cliquez sur "),mn,s(". Téléchargez l'archive, décompressez-la et ouvrez le projet avec IntelliJ (de préférence) ou VSCode. "),n("ul",null,[n("li",null,[s("Pour VSCode, installez une "),n("a",vn,[s("extension Kotlin"),e(a)]),s(" et "),n("a",gn,[s("Spring Boot Extension Pack"),e(a)]),s(" ( ⚠️ l'extension Spring ne semble pas supporter kotlin).")])])]),bn]),hn,n("ul",null,[fn,n("li",null,[s("Veuillez tester les routes avec un client REST. Vous pouvez trouver des fichiers "),_n,s(" ici au "),n("a",qn,[s("format JetBrains"),e(a)]),s(" ou "),n("a",yn,[s(" au format de l'extension REST Client de VSCode"),e(a)])])]),xn,n("ul",null,[n("li",null,[s("Créez un nouveau projet Spring en utilisant "),n("a",Sn,[s("Spring initializr"),e(a)]),s(" avec Kotlin et les dépendances suivantes : Spring Data JPA, H2 Database, Spring Boot DevTools, Spring Web.")]),wn]),jn,e(p,null,{default:i(()=>[e(l,{title:"Sans DSL (Test Get All)"},{default:i(()=>[Cn]),_:1}),e(l,{title:"Avec DSL (Test Get Single)"},{default:i(()=>[Pn]),_:1})]),_:1}),In,n("ul",null,[n("li",null,[n("a",Tn,[s("Projet Ktor"),e(a)])]),n("li",null,[n("a",An,[s("Projet nodejs"),e(a)])]),n("li",null,[n("a",Rn,[s("Projet Spring boot"),e(a)])])]),En,zn,n("ul",null,[n("li",null,[n("a",Gn,[s("Ce tutoriel de kotlinlang"),e(a)]),s(" montre comment créer un service web RESTful avec une base de données en utilisant Spring Boot.")]),n("li",null,[n("a",Kn,[s("Ce tutoriel de spring.io"),e(a)]),s(" montre comment construire une application web avec Spring Boot et Kotlin.")]),n("li",null,[n("a",Bn,[s("Rest APIs with Spring"),e(a)])]),n("li",null,[n("a",Dn,[s("Quarkus et Kotlin"),e(a)])])]),Jn,n("ul",null,[n("li",null,[n("a",Ln,[s("JS et Kotlin/JS"),e(a)])]),n("li",null,[n("a",On,[s("mockmvc kotlin dsl"),e(a)])]),n("li",null,[n("a",Mn,[s("spring-boot-kotlin tutorial"),e(a)])]),n("li",null,[n("a",Vn,[s("Kotlin et JPA"),e(a)])]),n("li",null,[n("a",Nn,[s("Spring Data JPA Comment utiliser les nulls Kotlin au lieu de Optional"),e(a)])])])])}const Zn=c(d,[["render",Hn],["__file","index.html.vue"]]),Un=JSON.parse('{"path":"/fr/backend-development/","title":"📚 Développement du backend","lang":"fr-FR","frontmatter":{},"headers":[{"level":2,"title":"Ktor","slug":"ktor","link":"#ktor","children":[{"level":3,"title":"TP : développer une API avec Ktor","slug":"tp-developper-une-api-avec-ktor","link":"#tp-developper-une-api-avec-ktor","children":[]}]},{"level":2,"title":"nodejs","slug":"nodejs","link":"#nodejs","children":[{"level":3,"title":"TP : API Rest avec Kotlin/JS et Express","slug":"tp-api-rest-avec-kotlin-js-et-express","link":"#tp-api-rest-avec-kotlin-js-et-express","children":[]}]},{"level":2,"title":"Spring framework","slug":"spring-framework","link":"#spring-framework","children":[{"level":3,"title":"TP : Spring boot part 1 - développer la même API avec Spring Boot","slug":"tp-spring-boot-part-1-developper-la-meme-api-avec-spring-boot","link":"#tp-spring-boot-part-1-developper-la-meme-api-avec-spring-boot","children":[]},{"level":3,"title":"TP : Spring boot partie 2 - ajouter une base de données","slug":"tp-spring-boot-partie-2-ajouter-une-base-de-donnees","link":"#tp-spring-boot-partie-2-ajouter-une-base-de-donnees","children":[]},{"level":3,"title":"TP : Spring boot partie 3 - ajouter des tests","slug":"tp-spring-boot-partie-3-ajouter-des-tests","link":"#tp-spring-boot-partie-3-ajouter-des-tests","children":[]},{"level":3,"title":"Projets terminés","slug":"projets-termines","link":"#projets-termines","children":[]}]},{"level":2,"title":"Aller plus loin","slug":"aller-plus-loin","link":"#aller-plus-loin","children":[]},{"level":2,"title":"Lien et références","slug":"lien-et-references","link":"#lien-et-references","children":[]}],"git":{"updatedTime":1696262637000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":4},{"name":"Ibrahim Gharbi","email":"brah.gharbi@gmail.com","commits":2},{"name":"Tobias Preuss","email":"tobias.preuss@googlemail.com","commits":2},{"name":"Yassine Benabbas @ Worldline","email":"86733850+wlybe@users.noreply.github.com","commits":2},{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":1}]},"filePathRelative":"fr/backend-development/README.md"}');export{Zn as comp,Un as data};
diff --git a/assets/index.html-mnsf-FXr.js b/assets/index.html-mnsf-FXr.js
new file mode 100644
index 00000000..543a0ad1
--- /dev/null
+++ b/assets/index.html-mnsf-FXr.js
@@ -0,0 +1 @@
+import{_ as s,a as d,b as u,c}from"./qrcode-mixtit24-D6tnJWnS.js";import{_ as p,r as i,o as m,c as f,a as e,b as l,d as n,w as o,e as r}from"./app-jR2rC7Ae.js";const h="/learning-kotlin/assets/qrcode-devoxxma23-Beeff8NO.gif",_={},k=r('
am2023 logo',4),g={href:"https://awl.li/am23-kt",target:"_blank",rel:"noopener noreferrer"},v=e("figure",null,[e("img",{src:s,alt:"qr code",tabindex:"0",loading:"lazy"}),e("figcaption",null,"qr code")],-1),b=e("h3",{id:"agenda",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#agenda"},[e("span",null,"Agenda")])],-1),x={href:"https://worldline.github.io/learning-kotlin-multiplatform/",target:"_blank",rel:"noopener noreferrer"},w=e("h2",{id:"mobile-devops-summit-2023",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#mobile-devops-summit-2023"},[e("span",null,"Mobile DevOps summit 2023")])],-1),A=e("li",null,[l("Titre: "),e("strong",null,"From Android to Multiplatform: leveraging the full potential of Kotlin")],-1),K={href:"https://awl.li/mds23-kt",target:"_blank",rel:"noopener noreferrer"},y=e("figure",null,[e("img",{src:d,alt:"qr code",width:"200",tabindex:"0",loading:"lazy"}),e("figcaption",null,"qr code")],-1),S=e("h3",{id:"agenda-1",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#agenda-1"},[e("span",null,"Agenda")])],-1),M=e("h2",{id:"devoxx-morocco-2023",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#devoxx-morocco-2023"},[e("span",null,"Devoxx Morocco 2023")])],-1),q=e("li",null,[l("Titre: "),e("strong",null,"Let's discover the amazing possibilities of Kotlin")],-1),D={href:"https://awl.li/devoxxma23-kt",target:"_blank",rel:"noopener noreferrer"},C=e("figure",null,[e("img",{src:h,alt:"qr code",width:"200",tabindex:"0",loading:"lazy"}),e("figcaption",null,"qr code")],-1),P=e("h3",{id:"agenda-2",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#agenda-2"},[e("span",null,"Agenda")])],-1),R=r('- Titre : Développement front et back en Kotlin. Une visite guidée de KMP
qr code',4),z=e("strong",null,"Développement backend",-1),I={href:"https://speakerdeck.com/yostane/kotlin-pour-le-developpement-backend",target:"_blank",rel:"noopener noreferrer"},J=e("strong",null,"Développement frontend",-1),L=e("strong",null,"Développement fullstack",-1),B=e("strong",null,"Autres fonctionnalités",-1);function W(N,F){const a=i("ExternalLinkIcon"),t=i("RouteLink");return m(),f("div",null,[k,e("p",null,[l("Lien du workshop: "),e("a",g,[l("awl.li/am23-kt"),n(a)])]),v,b,e("ul",null,[e("li",null,[n(t,{to:"/fr/backend-development/#ktor"},{default:o(()=>[l("Backend development: Rest API with Ktor")]),_:1})]),e("li",null,[l("Optional: "),n(t,{to:"/fr/backend-development/#ktor"},{default:o(()=>[l("Backend development: Rest API with nodeJS and Kotlin/JS")]),_:1})]),e("li",null,[n(t,{to:"/fr/front-development/#kotlin-js-and-kotlin-wasm"},{default:o(()=>[l("Front-end development: Kotlin/WASM and Kotlin/JS webapp")]),_:1})]),e("li",null,[n(t,{to:"/fr/front-development/#compose"},{default:o(()=>[l("Front-end development: cross-platform Hello world App with Compose multiplatform")]),_:1})]),e("li",null,[e("a",x,[l("Front-end development: Cross-platform Quiz App with Compose multiplatform"),n(a)])]),e("li",null,[n(t,{to:"/fr/other-technologies/#pw-add-a-ktor-server-app"},{default:o(()=>[l("Full-stack development: Quiz App with Compose multiplatform and Ktor server")]),_:1})])]),w,e("ul",null,[A,e("li",null,[l("Lien: "),e("a",K,[l("awl.li/mds23-kt"),n(a)])])]),y,S,e("ul",null,[e("li",null,[n(t,{to:"/fr/other-technologies/#pw-add-a-ktor-server-app"},{default:o(()=>[l("Développement d'une application multi-plateformes")]),_:1})]),e("li",null,[l("Démonstration des autres possibilités: "),e("ul",null,[e("li",null,[n(t,{to:"/fr/backend-development/#ktor"},{default:o(()=>[l("API Rest avec Ktor")]),_:1})]),e("li",null,[n(t,{to:"/fr/backend-development/#ktor"},{default:o(()=>[l("API Rest avec nodeJS et Kotlin/JS")]),_:1})]),e("li",null,[n(t,{to:"/fr/front-development/#kotlin-js-and-kotlin-wasm"},{default:o(()=>[l("Webapp Kotlin/WASM et Kotlin/JS ")]),_:1})])])])]),M,e("ul",null,[q,e("li",null,[l("Lien: "),e("a",D,[l("awl.li/mds23-kt"),n(a)])])]),C,P,e("ul",null,[e("li",null,[n(t,{to:"/fr/presentation/#prerequisites"},{default:o(()=>[l("Prérequis")]),_:1})]),e("li",null,[l("Caractéristiques notables de Kotlin: "),n(t,{to:"/fr/kotlin-features/#null-safety"},{default:o(()=>[l("le null safety")]),_:1}),l(" et "),n(t,{to:"/fr/kotlin-features/#functions"},{default:o(()=>[l("les fonctions")]),_:1})]),e("li",null,[n(t,{to:"/fr/backend-development/#spring-framework"},{default:o(()=>[l("API Rest avec Spring boot")]),_:1})]),e("li",null,[n(t,{to:"/fr/backend-development/#ktor"},{default:o(()=>[l("API Rest avec Ktor")]),_:1})]),e("li",null,[n(t,{to:"/fr/front-development/#compose"},{default:o(()=>[l(`Développement d'une application multi-plateformes type "Hello World" avec Compose Multiplatform`)]),_:1})]),e("li",null,[n(t,{to:"/fr/other-technologies/#pw-add-a-ktor-server-app"},{default:o(()=>[l("Dévelopment sullstack d'une application de Quiz avec with Compose multiplatform en front et Ktor server en backend")]),_:1})]),e("li",null,[l("Autres fonctionnalités et possibilités: "),e("ul",null,[e("li",null,[n(t,{to:"/fr/front-development/#kotlin-js-and-kotlin-wasm"},{default:o(()=>[l("Kotlin/WASM")]),_:1})]),e("li",null,[n(t,{to:"/fr/backend-development/#nodejs"},{default:o(()=>[l("Serveur NodeJS avec Kotlin/JS")]),_:1})]),e("li",null,[n(t,{to:"/fr/kotlin-features-advanced/#concurrency-and-coroutines"},{default:o(()=>[l("Coroutines")]),_:1})])])])]),R,e("ul",null,[e("li",null,[n(t,{to:"/fr/presentation/#prerequisites"},{default:o(()=>[l("Prérequis")]),_:1})]),e("li",null,[l("Fonctionnalités notables: "),n(t,{to:"/fr/kotlin-features/#null-safety"},{default:o(()=>[l("null safety")]),_:1}),l(" et "),n(t,{to:"/fr/kotlin-features/#functions"},{default:o(()=>[l("les fonctions")]),_:1})]),e("li",null,[z,e("ul",null,[e("li",null,[e("a",I,[l("Présentation d'introduction à Kotlin pour le développement backend"),n(a)])]),e("li",null,[n(t,{to:"/fr/backend-development/#spring-framework"},{default:o(()=>[l("API Rest avec Spring boot")]),_:1})]),e("li",null,[n(t,{to:"/fr/backend-development/#ktor"},{default:o(()=>[l("API Rest avec Ktor")]),_:1})])])]),e("li",null,[J,e("ul",null,[e("li",null,[n(t,{to:"/fr/front-development/#compose"},{default:o(()=>[l('Application "Hello World" avec Compose Multiplatform')]),_:1})])])]),e("li",null,[L,e("ul",null,[e("li",null,[n(t,{to:"/fr/other-technologies/#pw-add-a-ktor-server-app"},{default:o(()=>[l("Application de quiz avec Ktor + Compose Multiplatform")]),_:1})])])]),e("li",null,[B,e("ul",null,[e("li",null,[n(t,{to:"/fr/front-development/#kotlin-js-and-kotlin-wasm"},{default:o(()=>[l("Kotlin/WASM")]),_:1})]),e("li",null,[n(t,{to:"/fr/backend-development/#nodejs"},{default:o(()=>[l("Développement NodeJS en Kotlin")]),_:1})]),e("li",null,[n(t,{to:"/fr/kotlin-features-advanced/#concurrency-and-coroutines"},{default:o(()=>[l("Coroutines")]),_:1})])])])])])}const O=p(_,[["render",W],["__file","index.html.vue"]]),T=JSON.parse('{"path":"/fr/workshops/","title":"📅 Workshops","lang":"fr-FR","frontmatter":{},"headers":[{"level":2,"title":"Android makers 2023: Kotlin Beyond Android","slug":"android-makers-2023-kotlin-beyond-android","link":"#android-makers-2023-kotlin-beyond-android","children":[{"level":3,"title":"Liens","slug":"liens","link":"#liens","children":[]},{"level":3,"title":"Agenda","slug":"agenda","link":"#agenda","children":[]}]},{"level":2,"title":"Mobile DevOps summit 2023","slug":"mobile-devops-summit-2023","link":"#mobile-devops-summit-2023","children":[{"level":3,"title":"Agenda","slug":"agenda-1","link":"#agenda-1","children":[]}]},{"level":2,"title":"Devoxx Morocco 2023","slug":"devoxx-morocco-2023","link":"#devoxx-morocco-2023","children":[{"level":3,"title":"Agenda","slug":"agenda-2","link":"#agenda-2","children":[]}]},{"level":2,"title":"(2024) MiXit","slug":"_2024-mixit","link":"#_2024-mixit","children":[{"level":3,"title":"Agenda","slug":"agenda-3","link":"#agenda-3","children":[]}]}],"git":{"updatedTime":1713801068000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":5},{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":1},{"name":"yostane","email":"1958676+yostane@users.noreply.github.com","commits":1}]},"filePathRelative":"fr/workshops/README.md"}');export{O as comp,T as data};
diff --git a/assets/index.html-pJuZpkgy.js b/assets/index.html-pJuZpkgy.js
new file mode 100644
index 00000000..1645a515
--- /dev/null
+++ b/assets/index.html-pJuZpkgy.js
@@ -0,0 +1,7 @@
+import{_ as i,r as g,o as a,c as s,a as l,b as e,d as n,e as c}from"./app-jR2rC7Ae.js";const b={},t=c('Kotlin est un langage qui support les paradigmes orienté objet et fonctionnel. Ce chapitre couvre les caractéristiques basiques et intermédiaires. Le chapitre suivant couvrira les fonctionnalités avancées.
- Kotlin est typé statiquement et prend en charge le typage implicite.
- Typage statique : les types ne peuvent pas changer à l'exécution (c'est l'inverse du typage dynamique).
- Typage implicite : le compilateur peut inférer le type tant que c'est possible.
var
crée des variables ré-assignables.val
crée des variables qu'on ne peut plus ré-assigner.- Les points-virgules sont facultatifs.
- Kotlin prend en charge la déclaration de haut niveau des variables et des fonctions (elles peuvent être déclarées en dehors des classes).
- L'interpolation de chaîne de caractères est disponible avec cette syntaxe
${expression}
. if
et when
sont des expressions (elles peuvent renvoyer une valeur). when
est équivalent à l'instruction switch
des autres langages- L'opérateur ternaire n'est pas disponible. L'expression
if
le remplace.
- for-each est le seul type de boucle for disponible.
- La programmation orientée objet est prise en charge comme en Java avec quelques fonctionnalités supplémentaires telles que les extensions.
- Le compilateur prend en charge la Null Safety. Il permet d'écrire du code sans erreur de pointeur nulle vérifié à la compilation.
- La programmation fonctionnelle est prise en charge (fonctions d'ordre supérieur et fonctions en tant qu'éléments de 1ère classe, etc.).
Utiliser val par défaut
Utiliser var
uniquement si vous réassignez une variable ou argument.
',6),o={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gRnVuY3Rpb25zIGNhbiBiZSBkZWNsYXJlZCBvdXRzaWRlIG9mIGNsYXNzZXNcbmZ1biBzdW0oeDpJbnQsIHk6SW50KSA6IEludCB7XG4gICAgcmV0dXJuIHggKyB5XG59IC8vIGZ1bmN0aW9uIGRlZmluaXRpb25cblxuXG52YWwgZ2xvYmFsVmFsdWUgPSBcIkkgYW0gYSBnbG9iYWwgdmFsdWVcIlxuXG5mdW4gbWFpbigpIHtcbiAgICB2YXIgYTogSW50ID0gMCAvLyB2YXJpYWJsZVxuXHRhID0gMjNcblxuXHR2YWwgYjogU3RyaW5nID0gXCJIZWxsb1wiIC8vIGltbXV0YWJsZSB2YXJpYWJsZVxuXHQvL2IgPSBcIldvcmxkXCIgLy9lcnJvclxuXG5cdHZhciBtZXNzYWdlID0gXCJIZWxsb1wiIC8vIFR5cGUgaXMgaW5mZXJyZWRcbiAgICBcbiAgICB2YWwgdG90YWwgPSBzdW0oMiwgMSkgLy8gY2FsbCBzdW0gZnVuY3Rpb24gICAgXG4gICAgcHJpbnRsbihcIlRoZSBzdW0gaXMgJHt0b3RhbH1cIikgLy8gaW50ZXJwb2xhdGlvblxuICAgIHByaW50bG4oXCJUaGUgZ2xvYmFsVmFsdWUgaXMgLT4gJGdsb2JhbFZhbHVlXCIpXG4gICAgXG4gICAgdmFsIHRlc3RSZXN1bHQxID0gaWYgKHRvdGFsID4gMykgeyBcbiAgICAgICAgXCJCaWdcIiBcbiAgICB9IGVsc2Uge1xuICAgICAgICBcInNtYWxsXCJcbiAgICB9ICBcbiAgICBwcmludGxuKHRlc3RSZXN1bHQxKVxuICBcbiAgICB2YWwgdGVzdFJlc3VsdDIgPSBpZiAodG90YWwgPiAzKSBcIkJpZ1wiIGVsc2UgXCJzbWFsbFwiXG4gICAgcHJpbnRsbih0ZXN0UmVzdWx0MilcbiAgICBcbiAgICB2YWwgZ3JlZXRpbmcgPSBcIkJvbmpvdXJcIlxuICAgIHZhbCBsYW5ndWFnZSA9IHdoZW4oZ3JlZXRpbmcpe1xuICAgICAgIFwiQm9uam91clwiLFwic2FsdXRcIiAtPiBcIkZyYW7Dp2Fpc1wiXG4gICAgICAgIFwiR29vZCBtb3JuaW5nXCIgLT4gXCJFbmdsaXNoXCJcbiAgICAgICAgZWxzZSAtPiBcIlVua25vd25cIlxuICAgIH1cbiAgICBwcmludGxuKGxhbmd1YWdlKVxuICAgIFxuICAgIGZvciAoaSBpbiAxLi4zKSBwcmludGxuKGkpIC8vIHNpbmdsZSBsaW5lXG5cdGZvciAoaSBpbiA2IGRvd25UbyAwIHN0ZXAgMikgcHJpbnRsbihpKVxuICAgIGZvciAobXNnIGluIGFycmF5T2YoXCJIZWxsb1wiLCBcIndvcmxkXCIpKSB7XG4gICAgXHRwcmludGxuKG1zZylcblx0fVxuICAgIHZhciB4ID0gMlxuICAgIHdoaWxlICh4ID4gMCkge1xuICAgIFx0eC0tXG5cdH1cbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},u=c('Dans cette section, les termes 'argument' et 'paramètre' sont utilisés de manière interchangeable.
Les fonctions de Kotlin ont les caractéristiques suivantes :
- Déclaration :
fun functionName(arg1 : type1 = defaultvalue1, ...) : retunrType
. - Appeler une fonction en passant la valeur dans l'ordre de déclaration.
- Utilisez des étiquettes d'argument pour plus de clarté, cependant, cela permet également un classement arbitraire des arguments.
- Les arguments optionnels ont une valeur par défaut et peuvent être omis lors de l'appel.
- Les fonctions sont des éléments de première classe ou citoyens : elles peuvent être affectées à une variable, passées en tant que paramètre de fonction ou renvoyées par une fonction.
- 💡 Une fonction qui prend une fonction comme argument ou en renvoie une est une fonction d'ordre supérieur.
- Un type de fonction peut être exprimé comme suit :
(typeOfParam1, typeOfParam2, etc) -> returnType
(Le type de retour vide est Unit
). - Les fonctions anonymes utilisent la syntaxe suivante
{ argName1, argName2, etc. -> // code }
- Aussi appelées fonctions lambda ou fonctions littérales
- Le dernier argument de la fonction peut être mis après la fermeture après la parenthèse fermante
compute(9, 5) { x, y -> x * y }
',4),d={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gRnVuY3Rpb25zIGNhbiBiZSBkZWNsYXJlZCBvdXRzaWRlIG9mIGNsYXNzZXNcbmZ1biBzdW0oeDpJbnQsIHk6SW50KSA6IEludCB7XG4gICAgcmV0dXJuIHggKyB5XG59IC8vIGZ1bmN0aW9uIGRlZmluaXRpb25cblxuLy9PbmUgbGluZSBmdW5jdGlvblxuZnVuIG11bHQoeDpJbnQsIHk6IEludCkgPSB4ICogeVxuXG5mdW4gZ3JlZXQoeWVhcjogSW50ID0gMjAxOCwgbW9udGg6IEludCA9IDEsXG4gICAgICAgICAgbWVzc2FnZTogU3RyaW5nID0gXCJIZWxsb1wiKSA6IFN0cmluZyB7XG4gICAgcmV0dXJuIFwiJHttZXNzYWdlfS4gV2UgYXJlIGluICR7bW9udGh9LyR7eWVhcn1cIlxufVxuXG5mdW4gY2FsY3VsYXRlKHg6IEludCwgeTogSW50LCBmOiAoSW50LCBJbnQpIC0+IEludCkgOiBJbnR7XG4gICAgcmV0dXJuIGYoeCwgeSlcbn0gLy8gY2FsY3VsYXRlIHRha2VzIGEgZnVuY3Rpb24gYXMgYSBwYXJhbXRlclxuXG5mdW4gbWFpbihhcmd2OkFycmF5PFN0cmluZz4pe1xuICAgIHZhbCBhID0gc3VtKDIsIDEpIC8vIGNhbGwgc3VtIGZ1bmN0aW9uXG4gICAgcHJpbnRsbihtdWx0KGEsIHkgPSA4KSkgLy8gd2UgY2FuIG5hbWUgcGFyYW1ldGVyc1xuICAgIFxuICAgIHByaW50bG4oZ3JlZXQoMjAxNywgMTAsIFwiQm9uam91clwiKSkgLy9Cb25qb3VyLiBXZSBhcmUgaW4gMTAvMjAxN1xuICAgIHByaW50bG4oZ3JlZXQoKSkgLy9IZWxsby4gV2UgYXJlIGluIDEvMjAxOFxuICAgIHByaW50bG4oZ3JlZXQoMjAxOCwgbWVzc2FnZSA9IFwiS29ubmljaGl3YVwiKSlcbiAgICBcbiAgICBwcmludGxuKGNhbGN1bGF0ZSg5LCA1LCA6OnN1bSkpIC8vIDE0XG4gICAgLy8gZnVuY3Rpb25zIGNhbiBiZSBkZWNsYXJlZCBmcm9tIHdpdGhpbiBvdGhlciBmdW5jdGlvbnNcbiAgICB2YWwgZiA9IHt4OiBJbnQsIHk6IEludCAtPiB4IC0geX1cbiAgICBwcmludGxuKGNhbGN1bGF0ZSg5LCA1LCBmKSlcbiAgICBcbiAgICBmdW4gZGl2aWRlKHg6SW50LCB5OiBJbnQpID0geCAvIHlcbiAgICBwcmludGxuKGNhbGN1bGF0ZSg5LCA1LCA6OmRpdmlkZSkpXG4gICAgXG4gICAgcHJpbnRsbihjYWxjdWxhdGUoOSwgNSwgeyB4LCB5IC0+IHggKiB5IH0gKSkgLy8gNDVcbiAgICAvLyBJZiB0aGUgbGFzdCBwYXJhbWV0ZXIgaXMgZnVuY3Rpb24sIHdlIGNhbiBjbG9zZSAoKSBlcmxpZXJcbiAgICBwcmludGxuKGNhbGN1bGF0ZSg5LCA1KSB7IHgsIHkgLT4geCAqIHkgfSApIC8vIDQ1XG59In0=",target:"_blank",rel:"noopener noreferrer"},G=c('La prochaine section abordera le null safety.
null safety est une fonctionnalité du compilateur qui élimine la fameuse Null pointer exception ou npe. En effet, le compilateur signale des erreurs et des avertissements lorsque nous manipulons des types nullables (également appelées types optionnels) dès qu'il y a un risque de npe à l'exécution. Ainsi, afin de mettre Voici une liste des fonctionnalités de sécurité null fournies par Kotlin :
- Tous les types ne sont pas nullables par défaut ; nous ne pouvons pas affecter
null
à une variable ou à un argument. - Par exemple, ce code échoue
var s: String = null
.
- Un type peut être rendu nullable en le suffixant avec un ?. Par exemple :
var s : chaîne ? = nul
. - Kotlin interdit d'appeler une méthode ou une propriété de type non nullable, sauf si l'on fait l'une de ces possibilités :
- Utilisez le chaînage optionnel avec le suffixe ?.
- Fournissez une valeur par défaut avec l'opérateur elvis ?:.
- Smart-cast le nullable dans un non-nullable.
- Utilisez l'opérateur !! qui élimine les vérifications du compilateur. Cela ne devrait jamais être utilisé.
Ne jamais déballer avec !!
Car cela équivaut à désactiver la null safety. Utilisez les autres possibilités à la place.
',5),m={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJndjpBcnJheTxTdHJpbmc+KXtcbiAgICB2YXIgczogU3RyaW5nIC8vIHZhcnMgbXVzdCBiZSBpbml0aWFsaXplZCBiZWZvcmUgdXNpbmdcbiAgICAvLyBwcmludGxuKHMpIC0+IGNvbXBpbGUgZmFpbHNcbiAgICBzID0gXCJoZWxsb1wiXG4gICAgcHJpbnRsbihzKSAvLyBva1xuICAgIC8vIHMgPSBudWxsIC0+IGNvbXBpbGUgZmFpbHMuIFR5cGVzIGFyZSBub3QgbnVsbGFibGVcbiAgICB2YWwgbXNnIDogU3RyaW5nPyA9IG51bGwgLy8gbnVsbGFibGUgdHlwZXNcbiAgICBwcmludGxuKG1zZylcbiAgICAvLyBtc2cucmV2ZXJzZWQoKSAvLyBtc2cgbWF5IGJlIG51bGwgLT4gY29tcGlsZSBrb1xuICAgIHByaW50bG4obXNnPy5yZXZlcnNlZCgpPy5jYXBpdGFsaXplKCkpIC8vIHJldHVybiBudWxsIGlmIGFueSBwYXJ0IGlzIG51bGxcbiAgICAvL3ByaW50bG4obXNnISEucmV2ZXJzZWQoKSkgLy8gdGVsbCBjb21waWxlciB0byBpZ25vcmUgbnVsbCBjaGVja3MgLT4gTlBFIGlmIHRoZSB2YXIgaXMgbnVsbFxuICAgIFxuICAgIHZhciBuYjogSW50PyA9IDJcbiAgICAvLyBrb3RsaW4gY29tcGlsZXIga25vd24gdGhhdCBuYiBpcyBuZXZlciBudWxsIGluIGVsc2UgLT4gc21hcnQgY2FzdGluZ1xuICAgIHZhbCBkb3VibGUgPSBpZiggbmIgPT0gbnVsbCApIDAgZWxzZSBuYiAqIDJcbiAgICBwcmludGxuKGRvdWJsZSlcbiAgICBcbiAgICAvLyBzaG9ydGN1dCBvZiBwcmV2IGlmIHVzaW5nID86IGVsdmlzIG9wZXJhdG9yXG4gICAgdmFsIHRyaXBsZSA9IG5iPy50aW1lcygzKSA/OiAwIDsgcHJpbnRsbih0cmlwbGUpXG4gICAgLy9PdGhlciBzbWFydCBjYXN0aW5nIHVzZXNcbiAgICBpZiAobmIgaXMgSW50KSB7XG4gICAgICAgIHByaW50bG4obmIudGltZXMoMykpXG4gICAgfVxuICAgIFxuICAgIGlmIChuYiAhPSBudWxsKSB7XG4gICAgICAgIHByaW50bG4obmIudGltZXMoMykpXG4gICAgfVxuICAgIFxuICAgIC8vIHRoZSBhcmd1bWVudCBpcyB1bndyYXBwZWQgaW4gdGhlIGxhbWRhIG9mIGxldFxuICAgIG5iPy5sZXQgeyBpdC50aW1lcygzKSB9XG4gICAgXG4gICAgLy9zYWZlIGNhc3QgcmV0dXJuIG51bGxhYmxlIGFuZCBkb2VzIG5vdCB0aHJvdyBleGNlcHRpb25cbiAgICB2YWwgbmV3TmIgPSBuYiBhcz8gTG9uZ1xuICAgIHByaW50bG4obmV3TmIpXG4gICAgXG4gICAgLy8gQ29udmVydCBudWxsYWJsZSBsaXN0IHRvIG5vbiBudWxsYWJsZSBvbmVcbiAgICB2YWwgbnVsbGFibGVMaXN0OiBMaXN0PEludD8+ID0gbGlzdE9mKDEsIDIsIG51bGwsIDQpXG5cdHZhbCBpbnRMaXN0OiBMaXN0PEludD4gPSBudWxsYWJsZUxpc3QuZmlsdGVyTm90TnVsbCgpXG4gICAgcHJpbnRsbihpbnRMaXN0KVxufSJ9",target:"_blank",rel:"noopener noreferrer"},r=l("em",null,"null safety",-1),p=l("div",{class:"custom-container warning"},[l("p",{class:"custom-container-title"},"La classe `Optional` de Java ne fournit aucun protection à la compilation"),l("p",null,[e("Ce code lance une npe en Java: "),l("code",null,"Optional s = null; s.isPresent();"),e(". Le compilateur Java (au moins à la version version 17) ne propose pas d'équivalent à ce que propose Kotlin comme le smart casting.")])],-1),C=l("h3",{id:"enumerations",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#enumerations"},[l("span",null,"Énumérations")])],-1),Z={href:"https://www.geeksforgeeks.org/enum-classes-in-kotlin",target:"_blank",rel:"noopener noreferrer"},B=l("code",null,"enum class",-1),X=l("ul",null,[l("li",null,[e("Les expressions "),l("em",null,"when"),e(" prennent en charge les énumérations.")]),l("li",null,[e("Une "),l("code",null,"enum class"),e(" peut définir des méthodes et implémenter des interfaces mais elle ne peut pas dériver d'une classe.")]),l("li",null,[e("Il existe des méthodes pour lister les constantes d'une "),l("code",null,"enum class"),e(".")]),l("li",null,"Chaque constante d'une énumération a des propriétés pour obtenir son nom et sa position (en commençant par 0).")],-1),h={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGphdmEudXRpbC5mdW5jdGlvbi5CaW5hcnlPcGVyYXRvclxuaW1wb3J0IGphdmEudXRpbC5mdW5jdGlvbi5JbnRCaW5hcnlPcGVyYXRvclxuXG4vLyAvLyBTb3VyY2U6IGh0dHBzOi8va290bGlubGFuZy5vcmcvZG9jcy9lbnVtLWNsYXNzZXMuaHRtbFxuXG5lbnVtIGNsYXNzIERpcmVjdGlvbiB7XG4gICAgTk9SVEgsIFNPVVRILCBXRVNULCBFQVNUXG59XG5cbmVudW0gY2xhc3MgQ29sb3IodmFsIHJnYjogSW50KSB7XG4gICAgUkVEKDB4RkYwMDAwKSxcbiAgICBHUkVFTigweDAwRkYwMCksXG4gICAgQkxVRSgweDAwMDBGRilcbn1cblxuZW51bSBjbGFzcyBQcm90b2NvbFN0YXRlIHtcbiAgICBXQUlUSU5HIHtcbiAgICAgICAgb3ZlcnJpZGUgZnVuIHNpZ25hbCgpID0gVEFMS0lOR1xuICAgIH0sXG5cbiAgICBUQUxLSU5HIHtcbiAgICAgICAgb3ZlcnJpZGUgZnVuIHNpZ25hbCgpID0gV0FJVElOR1xuICAgIH07XG5cbiAgICBhYnN0cmFjdCBmdW4gc2lnbmFsKCk6IFByb3RvY29sU3RhdGVcbn1cblxuZW51bSBjbGFzcyBJbnRBcml0aG1ldGljcyA6IEJpbmFyeU9wZXJhdG9yPEludD4sIEludEJpbmFyeU9wZXJhdG9yIHtcbiAgICBQTFVTIHtcbiAgICAgICAgb3ZlcnJpZGUgZnVuIGFwcGx5KHQ6IEludCwgdTogSW50KTogSW50ID0gdCArIHVcbiAgICB9LFxuICAgIFRJTUVTIHtcbiAgICAgICAgb3ZlcnJpZGUgZnVuIGFwcGx5KHQ6IEludCwgdTogSW50KTogSW50ID0gdCAqIHVcbiAgICB9O1xuXG4gICAgb3ZlcnJpZGUgZnVuIGFwcGx5QXNJbnQodDogSW50LCB1OiBJbnQpID0gYXBwbHkodCwgdSlcbn1cblxuZnVuIG1haW4oKSB7XG4gICAgdmFsIHByb3RvY29sU3RhdGUgPSBQcm90b2NvbFN0YXRlLlRBTEtJTkdcbiAgICBwcmludGxuKFwiJHByb3RvY29sU3RhdGUsIHNpZ25hbCAtPiAke3Byb3RvY29sU3RhdGUuc2lnbmFsKCl9XCIpXG4gICAgcHJpbnRsbihcIm5hbWU6ICR7cHJvdG9jb2xTdGF0ZS5uYW1lfVwiKVxuICAgIHByaW50bG4oXCJvcmRpbmFsOiAke3Byb3RvY29sU3RhdGUub3JkaW5hbH1cIilcbiAgICBcbiAgICB2YWwgb3A6IEludEJpbmFyeU9wZXJhdG9yID0gSW50QXJpdGhtZXRpY3MuUExVU1xuICAgIHByaW50bG4ob3AuYXBwbHlBc0ludCgyLCAzMCkpXG4gICAgXG4gICAgcHJpbnRsbihQcm90b2NvbFN0YXRlLnZhbHVlcygpLmpvaW5Ub1N0cmluZyhcIiwgXCIpKVxuICAgIHByaW50bG4oUHJvdG9jb2xTdGF0ZS52YWx1ZU9mKFwiV0FJVElOR1wiKSlcbiAgICBcbiAgICB2YWwgYSA9IDEzXG4gICAgdmFsIGIgPSAzMVxuICAgIC8vIGxvb3AgdGhyb3VnaCB0aGUgcG9zc2libGUgZW51ZW1lcmF0aW9uc1xuICAgIGZvciAoZiBpbiBJbnRBcml0aG1ldGljcy52YWx1ZXMoKSkge1xuICAgICAgICBwcmludGxuKFwiJGYoJGEsICRiKSA9ICR7Zi5hcHBseShhLCBiKX1cIilcbiAgICB9XG59In0=",target:"_blank",rel:"noopener noreferrer"},W={href:"https://kotlinlang.org/docs/enum-classes.html",target:"_blank",rel:"noopener noreferrer"},y=l("h3",{id:"exercices",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercices"},[l("span",null,"Exercices")])],-1),A=l("h4",{id:"exercice-1",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercice-1"},[l("span",null,"Exercice 1")])],-1),V={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gUTogZGVjbGFyZSBhIGZ1bmN0aW9uIGNhbGxlZCBjb3VudFZvd2VscyB0aGF0IHRha2VzIGEgU3RyaW5nIGFyZ3VtZW50IFxuLy8gYW5kIHJldHVybnMgaXRzIG51bWJlciBvZiB2b3dlbHMgKGEsIHUsIGkgLCBlLCBvKS4gUGxlYXNlIHVzZSBhIGZvb3IgbG9vcC4ifQ==",target:"_blank",rel:"noopener noreferrer"},Y={class:"custom-container details"},J=l("summary",null,"Déplier pour consulter la solution",-1),R={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gUTogZGVjbGFyZSBhIGZ1bmN0aW9uIGNhbGxlZCBjb3VudFZvd2VscyB0aGF0IHRha2VzIGEgU3RyaW5nIGFyZ3VtZW50IFxuLy8gYW5kIHJldHVybnMgaXRzIG51bWJlciBvZiB2b3dlbHMgKGEsIHUsIGkgLCBlLCBvKS4gUGxlYXNlIHVzZSBhIGZvb3IgbG9vcC5cblxuZnVuIGNvdW50Vm93ZWxzKHRleHQ6IFN0cmluZykgOiBJbnQge1xuICAgIHZhciBjb3VudCA9IDBcbiAgICBmb3IgKGMgaW4gdGV4dCl7XG4gICAgICAgIGlmIChcImF1aWVvXCIuY29udGFpbnMoYykpe1xuICAgICAgICAgICAgY291bnQgKz0gMVxuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiBjb3VudFxufVxuXG5mdW4gbWFpbihhcmdzOkFycmF5PFN0cmluZz4pe1xuICAgIHByaW50bG4oY291bnRWb3dlbHMoXCJIZWxsb1wiKSlcbiAgICBwcmludGxuKGNvdW50Vm93ZWxzKFwiV29ybGRcIikpXG59In0=",target:"_blank",rel:"noopener noreferrer"},x=l("h4",{id:"exercise-2",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercise-2"},[l("span",null,"Exercise 2")])],-1),v={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gUTogcHJpbnQgdGhlIG1heCB2YWx1ZSBvZiB0aGUgbGlzdCAoaGludDogeW91IGNhbiB1c2UgdGhlIG1heCBtZXRob2QpXG5mdW4gcHJpbnRNYXgoaXRlbXM6IEFycmF5PEludD4/KXtcbiAgICBcbn1cblxuZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPil7XG4gICAgdmFyIGl0ZW1zOiBBcnJheTxJbnQ+PyA9IG51bGxcblx0aXRlbXMgPSBhcnJheU9mKDEsIDIsIDIpXG5cdHByaW50TWF4KGl0ZW1zKVxuICAgIFxuICAgIC8vIFE6IHByaW50IHRoZSB2YWx1ZSBvZiB0aGUgc2Vjb25kIGl0ZW0gb2YgdGhlIGFycmF5XG5cblx0Ly8gUTogcHJpbnQgdGhlIHN1bSBvZiB0aGUgZmlyc3QgYW5kIGxhc3QgaXRlbSBvZiB0aGUgYXJyYXlcbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},F={class:"custom-container details"},H=l("summary",null,"Déplier pour consulter la solution",-1),w={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gUTogcHJpbnQgdGhlIG1heCB2YWx1ZSBvZiB0aGUgbGlzdCAoaGludDogeW91IGNhbiB1c2UgdGhlIG1heCBtZXRob2QpXG5mdW4gcHJpbnRNYXgoaXRlbXM6IEFycmF5PEludD4/KXtcbiAgICBwcmludGxuKGl0ZW1zPy5tYXgoKSA/OiAwKVxuICAgIHByaW50bG4oaWYgKGl0ZW1zICE9IG51bGwpIGl0ZW1zLm1heCgpIGVsc2UgMClcbn1cblxuZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPil7XG4gICAgdmFyIGl0ZW1zOiBBcnJheTxJbnQ+PyA9IG51bGxcblx0Ly9pdGVtcyA9IGFycmF5T2YoMSwgMiwgMylcblx0cHJpbnRNYXgoaXRlbXMpXG4gICAgcHJpbnRNYXgobnVsbClcbiAgICBcbiAgICAvLyBROiBwcmludCB0aGUgdmFsdWUgb2YgdGhlIHNlY29uZCBpdGVtIG9mIHRoZSBhcnJheVxuICAgIC8vIC8vIFE6IHByaW50IHRoZSBzdW0gb2YgdGhlIGZpcnN0IGFuZCBsYXN0IGl0ZW0gb2YgdGhlIGFycmF5XG4gICAgaWYgKGl0ZW1zICE9IG51bGwpe1xuICAgICAgICBwcmludGxuKGl0ZW1zWzJdKVxuXHRcdHByaW50bG4oaXRlbXNbMl0gKyBpdGVtc1swXSlcbiAgICB9XG59In0=",target:"_blank",rel:"noopener noreferrer"},N=c('Kotlin permet d'écrire du code Orienté Object concis grâce aux caractéristiques suivantes :
- Concepts disponibles : classes, héritage, interfaces et classes abstraites.
- Prise en charge possée des propriétés : les getters et les setters sont automatiquement implémentés.
- On peut les personnaliser les accesseurs en définissant les fonctions
get()
et set(value)
à côté de la déclaration de la propriété.
- Les arguments du constructeur sont définis à côté du nom de la classe
class ClassName(arg1, atg2, )
- Préfixer les arguments d'un constructeur avec
val
ou var
en fait une propriété (val
la rend non ré-assignable). - Le nom du constructeur est
init
et ne nécessite pas de paramètres. - Le compilateur vérifie que toutes les propriétés non nullables sont initialisées à la fin du constructeur.
- ⚠️ Le compilateur ne vérifie pas l'initialisation des propriétés
lateinit
. Ainsi, y accéder avant alors qu'elles ne sont pas initialisés provoque une exception.
- Une classe doit être préfixée avec
open
pour permettre l'héritage. - Kotlin utilise le niveau d'accès
public
par défaut. - L'opérateur d'égalité
==
appelle implicitement la méthode equals()
(contrairement à Java qui utilise l'égalité de référence). - Un objet compagnon contient des méthodes et des propriétés statiques.
- Les extensions ajoutent des fonctions et des propriétés aux classes existantes.
- 💡 Ils remplacent l'héritage dans de nombreuses situations.
- Par exemple, nous pouvons ajouter des fonctions à la classe String au lieu de créer une nouvelle classe
StringUtils
.
- Les classes et interfaces scellées ne peuvent pas être étendues ou implémentées par des tiers.
Ne définir les accesseurs que si vous avez un comportement personnalisé
Kotlin prend en charge les propriétés de façon plus poussée que Java et permet d'ajouter des accesseurs ultérieurement sans refactoriser le code qui appelle ces propriétés. Ainsi, par défaut, il suffit de définir le nom des propriétés sans accesseurs et on peut les utiliser directement.
',5),S={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gY2xhc3Mgd2l0aCBkZWZhdWx0IGNvbnN0cnVjdG9yXG4vLyBvcGVuOiBhbGxvdyBpbmhlcml0YW5jZSAoY2xhc3NlcyBhbmQgcHJvcGVydGllcyBhcmUgZmluYWwgYnkgZGVmYXVsdClcbi8vIC8vIHZhbCBnZW5lcmF0ZXMgcmVhZC1vbmx5IHByb3BlcnRpZXNcbm9wZW4gY2xhc3MgUGxhbmV0KG9wZW4gdmFsIHJhZGl1czogTG9uZykge1xuICAgIC8vIHZhciBnZW5lcmF0ZXMgcHJvcGVydGllc1xuICAgIHZhciByb3RhdGlvblNwZWVkID0gMFxuICAgIFxuICAgIC8vIFdlIGNhbiBjdXN0b21pemUgdGhlIGdldHRlciBhbmQgdGhlIHNldHRlciBvZiB0aGUgcHJvcG9lcnR5XG4gICAgdmFsIHJhZGl1c0luTWV0ZXI6IExvbmcgXG4gICAgXHRnZXQoKSA9IHJhZGl1cyAqIDEwMDBcbn1cbmNsYXNzIEluaGFiaXRlZFBsYW5ldChvdmVycmlkZSB2YWwgcmFkaXVzOiBMb25nLCB2YXIgcG9wdWxhdGlvbjogTG9uZyk6IFBsYW5ldChyYWRpdXMpIHtcbiAgICBpbml0IHsgcHJpbnRsbihcImNvbnN0cnVjdG9yIGNhbGxlZFwiKSB9XG4gICAgXG4gICAgdmFyIHBvcHVsYXRpb25JbkJpbGxpb246IEludFxuICAgIGdldCgpID0gKHBvcHVsYXRpb24gLyAxXzAwMF8wMDBfMDAwKS50b0ludCgpXG4gICAgc2V0KHZhbHVlKSB7XG4gICAgICAgIHBvcHVsYXRpb24gPSB2YWx1ZS50b0xvbmcoKSAqIDFfMDAwXzAwMF8wMDBcbiAgICB9XG4gICAgXG4gICAgZnVuIGluY3JlYXNlUG9wdWxhdGlvbihhbW91bnQ6IExvbmcpe1xuICAgICAgICBwb3B1bGF0aW9uICs9IGFtb3VudFxuICAgIH0gICAgICAgXG59XG5cbi8vIGV4dGVuc2lvbnNcbmZ1biBTdHJpbmcuY291bnRMZXR0ZXJzKGxldHRlcnM6IEFycmF5PENoYXI+KTogSW50IFxuXHQ9IHRoaXMuZmlsdGVyIHsgbGV0dGVycy5jb250YWlucyhpdCkgfVxuICAgIFx0XHQubWFwIHsgMSB9LnN1bSgpXG4gICAgICAgICAgICBcbnZhbCBJbmhhYml0ZWRQbGFuZXQuaGFzSW5oYWJpdGFudHM6IEJvb2xlYW4gXG5cdGdldCgpID0gcG9wdWxhdGlvbiA+IDBcblxuZnVuIG1haW4oKXtcbiAgICB2YWwgZWFydGggPSBJbmhhYml0ZWRQbGFuZXQoNl8zNzEsIDdfNzUzXzAwMF8wMDBfMDAwKVxuICAgIHByaW50bG4oZWFydGgpXG4gICAgcHJpbnRsbihlYXJ0aC5yYWRpdXMpXG4gICAgcHJpbnRsbihlYXJ0aC5wb3B1bGF0aW9uKVxuICAgIGVhcnRoLnBvcHVsYXRpb24gKz0gMVxuICAgIHByaW50bG4oZWFydGgucG9wdWxhdGlvbilcbiAgICBwcmludGxuKGVhcnRoLnBvcHVsYXRpb25JbkJpbGxpb24pXG4gICAgXG4gICAgZWFydGguaW5jcmVhc2VQb3B1bGF0aW9uKDEwMClcbiAgICBwcmludGxuKGVhcnRoLnBvcHVsYXRpb24pXG4gICAgcHJpbnRsbihlYXJ0aC5wb3B1bGF0aW9uSW5CaWxsaW9uKVxuICAgIFxuICAgIHByaW50bG4oXCJIZWxsb1wiLmNvdW50TGV0dGVycyhhcnJheU9mKCdIJywgJ2UnKSkpXG4gICAgcHJpbnRsbihlYXJ0aC5oYXNJbmhhYml0YW50cylcbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},k=l("h3",{id:"data-class",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#data-class"},[l("span",null,"Data class")])],-1),L={href:"https://kotlinlang.org/docs/data-classes.html",target:"_blank",rel:"noopener noreferrer"},z=l("ul",null,[l("li",null,[l("code",null,"equals()"),e(", "),l("code",null,"hashCode()"),e(", "),l("code",null,"copy()"),e(" et "),l("code",null,"toString()")]),l("li",null,[l("code",null,"componentN()"),e(" qui est une syntaxe alternative pour récupérer les propriétés.")])],-1),E={href:"https://kotlinlang.org/docs/data-classes.html",target:"_blank",rel:"noopener noreferrer"},j=l("ul",null,[l("li",null,"Le constructeur principal doit avoir au moins un paramètre."),l("li",null,[e("Tous les paramètres du constructeur principal doivent être marqués comme "),l("code",null,"val"),e(" ou "),l("code",null,"var"),e(".")]),l("li",null,"Une Data class ne peut pas être abstraite, ouverte à l'héritage, scellée ou interne (💡 mais des extensions sont possibles).")],-1),U={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gZGF0YSBjbGFzcyBhcmUgZmluYWwgY2xhc3NlcyBnZW5lcmF0ZSBtYW55IHVzZWZ1bCBib2lsZXJwbGF0ZSBjb2RlIChoYXNoQ29kZSwgZXF1YWxzLCBjb21wb25lbnROKCksIGNvcHkgYW5kIHRvU3RyaW5nKVxuZGF0YSBjbGFzcyBTcGFjZVNoaXAodmFsIG1heFNwZWVkOiBJbnQsIHZhciBjdXJyZW50U3BlZWQ6IEludCA9IDApXG5cbmZ1biBtYWluKCkge1xudmFsIHNoaXAxID0gU3BhY2VTaGlwKDEwMDAsIDApXG4gICAgdmFsIHNoaXAyID0gU3BhY2VTaGlwKDEwMDAsIDApXG4gICAgdmFsIHNoaXAzID0gU3BhY2VTaGlwKDEwMDAsIDEwKVxuICAgIHByaW50bG4oc2hpcDEpXG4gICAgcHJpbnRsbihzaGlwMSA9PSBzaGlwMilcbiAgICBwcmludGxuKHNoaXAxID09IHNoaXAzKVxuICAgIFxuICAgIHByaW50bG4oc2hpcDEuY29tcG9uZW50MSgpKVxuICAgIHByaW50bG4oc2hpcDEuY29tcG9uZW50MigpKVxuICAgIFxuICAgIC8vIGNvbXBvbmVudHMgZW5hYmxlIGRlc3RydWN0dXJpbmdcbiAgICB2YWwgKG1heFNwZWVkLCBjdXJyZW50U3BlZWQpID0gc2hpcDJcbiAgICBwcmludGxuKFwiTWF4IHNwZWVkOiAkbWF4U3BlZWQuIEN1cnJlbnQgc3BlZWQ6ICRjdXJyZW50U3BlZWRcIilcbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},f=l("h3",{id:"programmation-fonctionnelle",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#programmation-fonctionnelle"},[l("span",null,"Programmation fonctionnelle")])],-1),M=l("h4",{id:"concepts-generaux",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#concepts-generaux"},[l("span",null,"Concepts généraux")])],-1),K={href:"https://www.geeksforgeeks.org/functional-programming-paradigm",target:"_blank",rel:"noopener noreferrer"},Q=l("p",null,"Expliquons brièvement ces concepts :",-1),D=l("li",null,"Les variables immuables signifient qu'on ne peut pas changer la valeur d'une variable ou ses propriétés une fois qu'elle a été créée. Si nous voulons le faire, nous devons créer une nouvelle instance avec la nouvelle valeur.",-1),T=l("li",null,"Les fonctions pures sont des fonctions qui n'ont pas d'effets secondaires et renverront donc toujours la même sortie étant donné la même entrée.",-1),_=l("li",null,"Les fonctions sont des citoyennes de première classe : elles peuvent être affectées à une variable ou utilisées dans des fonctions d'ordre supérieur (passées en tant qu'un argument de fonction ou retournées dans un fonction).",-1),P={href:"https://ericnormand.me/podcast/what-is-referential-transparency",target:"_blank",rel:"noopener noreferrer"},q=l("p",null,"💡 Les langages fonctionnels purs fournissent ces fonctionnalités de manière native et les appliquent (au moment de la construction).",-1),O=l("h4",{id:"kotlin-et-programmation-fonctionnelle",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#kotlin-et-programmation-fonctionnelle"},[l("span",null,"Kotlin et programmation fonctionnelle")])],-1),$={href:"https://github.com/Kotlin/kotlinx.collections.immutable",target:"_blank",rel:"noopener noreferrer"},ll={class:"custom-container warning"},el=l("p",{class:"custom-container-title"},"`listOf` génère des listes en lecture seule, mais qui sont mutables",-1),Il={href:"https://www.baeldung.com/kotlin/immutable-collections",target:"_blank",rel:"noopener noreferrer"},nl=c(`@Test
+fun givenReadOnlyList_whenCastToMutableList_checkNewElementsAdded(){
+ val list: List<String> = listOf("This", "Is", "Totally", "Immutable")
+ (list as MutableList<String>)[2] = "Not"
+ assertEquals(listOf("This", "Is", "Not", "Immutable"), list)
+}
+
`,1),cl={href:"https://arrow-kt.io/",target:"_blank",rel:"noopener noreferrer"},il=l("h4",{id:"programmation-declarative",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#programmation-declarative"},[l("span",null,"Programmation déclarative")])],-1),gl=l("p",null,[e("La programmation déclarative est un style célèbre dans la programmation fonctionnelle. Il consiste à écrire du code sous la forme d'un enchaînement d'appels de fonction dans ce style "),l("code",null,"val result = f(x).g(y). ..."),e(". Les fonctions d'ordre supérieur remplacent de nombreuses situations où nous utiliserions des boucles. Cela favorise le code lisible qui est facile à déboguer et à maintenir.")],-1),al={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oKSB7XG4gICAgdmFsIGZhbnRhc3lOYW1lcyA9IGxpc3RPZihcIkNsZWJicmVyXCIsIFwiU25pbmp1clwiLCBcIk1vZGRuYWFjXCIsIFwiTGVlZG5hdFwiICxcIkdpZXJ3eXN0XCIsIFwiWmV2dXJpZ1wiLCBcIkNsYWFtcGFyYWlydFwiLCBcIkd1ZmFwcmFhbVwiLCBcIlJpZW1hcHJhc3RcIiwgXCJCaWxhbmpvbVwiKVxuICAgIFxuICAgIC8vIGZpbmQgbmFtZXMgdGhhdCBzdGFydCB3aXRoIFwiR1wiIG9yIGVuZCB3aXRoIFwidFwiIGFuZCByZXR1cm4gdGhlbSBhcyBjYXBpdGFsIGxldHRlcnNcbiAgICB2YWwgc2VsZWN0ZWROYW1lcyA9IGZhbnRhc3lOYW1lcy5maWx0ZXIgeyBpdC5zdGFydHNXaXRoKCdHJykgfHwgaXQuZW5kc1dpdGgoJ3QnKSB9Lm1hcCB7IGl0LnRvVXBwZXJDYXNlKCkgfVxuICAgIHByaW50bG4oXCJGaWx0ZXJlZCBuYW1lcyAke3NlbGVjdGVkTmFtZXMuam9pblRvU3RyaW5nKFwiLCBcIil9XCIpXG4gICAgXG4gICAgLy8gY291bnQgdGhlIG51bWJlciBvZiB2b3dlbHNcblx0Ly8gZmxhcG1hcCBhbGxvd3MgdG8gY29udmVydCBhIDJkIGFycmF5IHRvIGEgMWQgYXJyYXkgYnkgY29uY2F0ZW5hdGluZyB0aGUgZWxlbWVudHMgKGNhbGxlZCBhIGZsYXQgb3BlcmF0aW9uKVxuXHR2YWwgdm93ZWxDb3VudCA9IGZhbnRhc3lOYW1lcy5mbGF0TWFweyBpdC50b0xpc3QoKSB9LmZpbHRlcnsgXCJhaXVlb1wiLmNvbnRhaW5zKGl0KSB9Lm1hcCB7IDEgfS5zdW0oKVxuICAgIHZhbCB2b3dlbENvdW50MiA9IGZhbnRhc3lOYW1lcy5mbGF0TWFweyBpdC50b0xpc3QoKSB9XG4gICAgICAgIC5tYXB7IGlmIChcImFpdWVvXCIuY29udGFpbnMoaXQpKSAxIGVsc2UgMCB9XG4gICAgICAgIC5yZWR1Y2UgeyBhY2MsIGN1cnJlbnQgLT4gYWNjICsgY3VycmVudCB9XG4gICAgXG4gICAgcHJpbnRsbihcIlZvd2VsIGNvdW50OiAkdm93ZWxDb3VudFwiKVxuICAgIHByaW50bG4oXCJWb3dlbCBjb3VudDogJHZvd2VsQ291bnQyXCIpXG59In0=",target:"_blank",rel:"noopener noreferrer"},sl=l("h3",{id:"kotlin-and-java-interoperability",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#kotlin-and-java-interoperability"},[l("span",null,"Kotlin and Java interoperability")])],-1),bl=l("li",null,"Kotlin is designed with Java interoperability in mind.",-1),tl=l("li",null,"Kotlin code may require some annotations to be called from Java.",-1),ol=l("li",null,"It is possible to mix Java and Kotlin in the same project.",-1),ul=l("li",null,"JetBrain's IntelliJ and Android Studio can convert to Kotlin when pasting java code.",-1),dl=l("code",null,"@JvmRecord",-1),Gl={href:"https://kotlinlang.org/docs/jvm-records.html",target:"_blank",rel:"noopener noreferrer"},ml=l("li",null,[e("It is much more easier and natural to call Java from Kotlin. "),l("ul",null,[l("li",null,"For example: Java accessors are converted to Kotlin properties.")])],-1),rl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGphdmEudXRpbC4qXG5mdW4gY29udmVydFRvQXJyYXlMaXN0KGl0ZW1zOiBMaXN0PEludD4pOiBBcnJheUxpc3Q8SW50PiB7XG4gICAgLy8gaW5zdGFudGlhdGUgYXMgaW4gSmF2YSB3aXRob3V0ICduZXcnXG4gICAgdmFsIGxpc3QgPSBBcnJheUxpc3Q8SW50PigpXG4gICAgLy8gJ2ZvcictbG9vcHMgd29yayBmb3IgSmF2YSBjb2xsZWN0aW9uczpcbiAgICBmb3IgKGl0ZW0gaW4gaXRlbXMpIHsgXG4gICAgICAgIGxpc3QuYWRkKGl0ZW0pXG4gICAgfVxuICAgIFxuICAgIGZvciAoaSBpbiAwLi5pdGVtcy5zaXplIC0gMSkge1xuICAgICAgICBsaXN0W2ldID0gaXRlbXNbaV1cbiAgICB9XG4gICAgcmV0dXJuIGxpc3Rcbn1cbmZ1biBtYWluKCl7XG4gICAgdmFsIGxpc3QgPSBjb252ZXJ0VG9BcnJheUxpc3QobGlzdE9mKDAsIDIsIC0xKSlcbiAgICBwcmludGxuKFwibGlzdDogJHtsaXN0fS4gdHlwZTogJHtsaXN0OjpjbGFzcy5xdWFsaWZpZWROYW1lfVwiKVxuICAgIC8vbGlzdDogWzAsIDIsIC0xXS4gdHlwZTogamF2YS51dGlsLkFycmF5TGlzdFxufSJ9",target:"_blank",rel:"noopener noreferrer"},pl=l("code",null,"List",-1),Cl=l("code",null,"ArrayList",-1),Zl={href:"https://kotlinlang.org/docs/jvm-get-started.html",target:"_blank",rel:"noopener noreferrer"},Bl=l("h3",{id:"exercices-1",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercices-1"},[l("span",null,"Exercices")])],-1),Xl=l("h4",{id:"exercice-3",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercice-3"},[l("span",null,"Exercice 3")])],-1),hl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gRXh0ZW5zaW9ucyBhbGxvd3MgdG8gYWRkIGZ1bmN0aW9ucyBvciBwcm9wZXJ0aWVzIHRvIGFuIGV4aXN0aW5nIGNsYXNzIChldmVuIGNsYXNzZXMgaW1wb3J0ZWQgZnJvbSBvdGhlciBsaWJyYXJpZXMpXG4vLyBodHRwczovL2tvdGxpbmxhbmcub3JnL2RvY3MvcmVmZXJlbmNlL2V4dGVuc2lvbnMuaHRtbFxuXG5vcGVuIGNsYXNzIEFkdmVudHVyZXIodmFyIGhwOiBJbnQpe1xuICAgIHZhbCBtYXhIcCA9IGhwXG4gICAgZnVuIGF0dGFjaygpOlN0cmluZyA9IFwiSSBkZWFsdCAxMCBkYW1hZ2VcIlxufVxuLy8gRXh0ZW5zaW9uIGZ1bmN0aW9uIHN5bnRheDogZnVuIGNsYXNzTmFtZS5mdW5jdGlvbk5hbWUocHJhbXMpIDogcmV0dXJuVHlwZVxuZnVuIEFkdmVudHVyZXIucmVwb3J0U3RhdHVzKCk6U3RyaW5nIHtcbiAgICByZXR1cm4gXCJJIGhhdmUgJHtocH0gSFBcIlxufVxuXG5mdW4gQWR2ZW50dXJlci5hdHRhY2soKTpTdHJpbmcgPSBcIkkgZGVhbHQgMTAwMCBkYW1hZ2VcIlxuXG5jbGFzcyBCYXJiYXJpYW4oaHA6IEludCk6IEFkdmVudHVyZXIoaHApXG5cbmZ1biBCYXJiYXJpYW4ucmVwb3J0U3RhdHVzKCk6U3RyaW5nID0gXCJJIGFtIGEgYmFyYmFyaWFuIGFuZCBJIGhhdmUgJHtocH0gSFBcIlxuXG5mdW4gcHJpbnRTdGF0dXMoYWR2ZW50dXJlcjogQWR2ZW50dXJlcikge1xuICAgIHByaW50bG4oYWR2ZW50dXJlci5yZXBvcnRTdGF0dXMoKSlcbn1cblxuZnVuIG1haW4oKXtcbiAgICAvLyBUT0RPIGNyZWF0ZSBhbiBhZHZlbnR1cmVyIHdpdGggMjAwaHAgYW5kIHByaW50IHRoZSByZXN1bHQgb2YgdGhlIHJlcG9ydFN0YXR1cyBleHRlbnNpb24gbWV0aG9kXG5cbiAgICAvLyBUT0RPIHByaW50IHRoZSByZXN1bHQgb2YgdGhlIGF0dGFjayBmdW5jdGlvbi4gV2hpY2ggdmVyc2lvbiBpcyByZXRhaW5lZCAobWVtYmVyIGZ1biBvciBleHRlbnNpb24gZnVuID8pXG5cbiAgICAvLyBUT0RPIGRlZmluZSB0aGUgZXh0ZXNudGlvbiBmdW4gJ0FkdmVudHVyZXIuYXR0YWNrKGRhbWFnZTogSW50KTpTdHJpbmcnIHRoYXQgcHJpbnRzIHRoZSB2YWx1ZSBwYXNzZWQgYXMgYSBwYXJhbWV0ZXJcblxuICAgIC8vIFRPRE8gcHJpbnQgdGhlIHJlc3VsdHMgb2YgdGhpcyBuZXcgZXh0ZW5zaW9uIGZ1bmN0aW9uIHdpdGggcGFyYW1ldGVyIHZhbHVlcyA1MCwgMTAgYW5kIDIwMFxuXG4gICAgLy8gVE9ETyB3aGF0IGlzIHRoZSByZXN1bHQgb2YgcHJpbnRTdGF0dXMgb24gYSBCYXJiYXJpYW4gPyB3aGF0IGRvIHlvdSBjb25jbHVkZSA/XG4gICAgdmFsIGJhcmJhcmlhbiA9IEJhcmJhcmlhbig1MDApXG4gICAgcHJpbnRTdGF0dXMoYmFyYmFyaWFuKVxuXG4gICAgLy8gVE9ETyBjcmVhdGUgYW4gZXh0ZW5zaW9uIHByb3BlcnR5IFwibG9zdEhwXCIgdGhhdCBjb3JyZXNwb25kcyB0byBcIm1heEhwIC0gaHBcIiAuXG4gICAgLy8gIEJlY2F1c2Ugd2UgY2Fubm90IGFkZCBmaWVsZHMgdG8gYW4gZXhpc3RpbmcgY2xhc3MsIGV4dGVuc2lvbiBwcm9wZXJ0aWVzIGNhbiBiZSBkZWZpbmVkIHVzaW5nIGdldHRlcnMgYW5kIHNldHRlcnMuXG5cbiAgICAvKiAgdW5jb21tZW50IGFmdGVyIGRlZmluaW5nIHRoZSBwcm9wZXJ0eSAgXG4gICAgdmFsIGIgPSBCYXJiYXJpYW4oMzAwKVxuICAgIGIuaHAgLT0gMTAwXG4gICAgcHJpbnRsbihiLmxvc3RIcCkgLy8gMTAwXG4gICAgYi5sb3N0SHAgPSAyNTBcbiAgICBwcmludGxuKGIuaHApIC8vIDUwXG4gICAgKi9cbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},Wl={class:"custom-container details"},yl=l("summary",null,"Solution(s)",-1),Al={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gRXh0ZW5zaW9ucyBhbGxvd3MgdG8gYWRkIGZ1bmN0aW9ucyBvciBwcm9wZXJ0aWVzIHRvIGFuIGV4aXN0aW5nIGNsYXNzIChldmVuIGNsYXNzZXMgaW1wb3J0ZWQgZnJvbSBvdGhlciBsaWJyYXJpZXMpXG4vLyBodHRwczovL2tvdGxpbmxhbmcub3JnL2RvY3MvcmVmZXJlbmNlL2V4dGVuc2lvbnMuaHRtbFxuXG5vcGVuIGNsYXNzIEFkdmVudHVyZXIodmFyIGhwOiBJbnQpe1xuICAgIHZhbCBtYXhIcCA9IGhwXG4gICAgZnVuIGF0dGFjaygpOlN0cmluZyA9IFwiSSBkZWFsdCAxMCBkYW1hZ2VcIlxufVxuLy8gRXh0ZW5zaW9uIGZ1bmN0aW9uIHN5bnRheDogZnVuIGNsYXNzTmFtZS5mdW5jdGlvbk5hbWUocHJhbXMpIDogcmV0dXJuVHlwZVxuZnVuIEFkdmVudHVyZXIucmVwb3J0U3RhdHVzKCk6U3RyaW5nIHtcbiAgICByZXR1cm4gXCJJIGhhdmUgJHtocH0gSFBcIlxufVxuXG5mdW4gQWR2ZW50dXJlci5hdHRhY2soKTpTdHJpbmcgPSBcIkkgZGVhbHQgMTAwMCBkYW1hZ2VcIlxuXG5jbGFzcyBCYXJiYXJpYW4oaHA6IEludCk6IEFkdmVudHVyZXIoaHApXG5cbmZ1biBCYXJiYXJpYW4ucmVwb3J0U3RhdHVzKCk6U3RyaW5nID0gXCJJIGFtIGEgYmFyYmFyaWFuIGFuZCBJIGhhdmUgJHtocH0gSFBcIlxuXG5mdW4gcHJpbnRTdGF0dXMoYWR2ZW50dXJlcjogQWR2ZW50dXJlcikge1xuICAgIHByaW50bG4oYWR2ZW50dXJlci5yZXBvcnRTdGF0dXMoKSlcbn1cblxuZnVuIG1haW4oKXtcbiAgICAvLyBUT0RPIGNyZWF0ZSBhbiBhZHZlbnR1cmVyIHdpdGggMjAwaHAgYW5kIHByaW50IHRoZSByZXN1bHQgb2YgdGhlIHJlcG9ydFN0YXR1cyBleHRlbnNpb24gbWV0aG9kXG5cbiAgICAvLyBUT0RPIHByaW50IHRoZSByZXN1bHQgb2YgdGhlIGF0dGFjayBmdW5jdGlvbi4gV2hpY2ggdmVyc2lvbiBpcyByZXRhaW5lZCAobWVtYmVyIGZ1biBvciBleHRlbnNpb24gZnVuID8pXG5cbiAgICAvLyBUT0RPIGRlZmluZSB0aGUgZXh0ZXNudGlvbiBmdW4gJ0FkdmVudHVyZXIuYXR0YWNrKGRhbWFnZTogSW50KTpTdHJpbmcnIHRoYXQgcHJpbnRzIHRoZSB2YWx1ZSBwYXNzZWQgYXMgYSBwYXJhbWV0ZXJcblxuICAgIC8vIFRPRE8gcHJpbnQgdGhlIHJlc3VsdHMgb2YgdGhpcyBuZXcgZXh0ZW5zaW9uIGZ1bmN0aW9uIHdpdGggcGFyYW1ldGVyIHZhbHVlcyA1MCwgMTAgYW5kIDIwMFxuXG4gICAgLy8gVE9ETyB3aGF0IGlzIHRoZSByZXN1bHQgb2YgcHJpbnRTdGF0dXMgb24gYSBCYXJiYXJpYW4gPyB3aGF0IGRvIHlvdSBjb25jbHVkZSA/XG4gICAgdmFsIGJhcmJhcmlhbiA9IEJhcmJhcmlhbig1MDApXG4gICAgcHJpbnRTdGF0dXMoYmFyYmFyaWFuKVxuXG4gICAgLy8gVE9ETyBjcmVhdGUgYW4gZXh0ZW5zaW9uIHByb3BlcnR5IFwibG9zdEhwXCIgdGhhdCBjb3JyZXNwb25kcyB0byBcIm1heEhwIC0gaHBcIiAuXG4gICAgLy8gIEJlY2F1c2Ugd2UgY2Fubm90IGFkZCBmaWVsZHMgdG8gYW4gZXhpc3RpbmcgY2xhc3MsIGV4dGVuc2lvbiBwcm9wZXJ0aWVzIGNhbiBiZSBkZWZpbmVkIHVzaW5nIGdldHRlcnMgYW5kIHNldHRlcnMuXG4gICAgdmFyIEJhcmJhcmlhbi5sb3N0SHA6SW50XG4gICAgICAgIGdldCgpID0gbWF4SHAgLSBocFxuICAgICAgICBzZXQgKHZhbHVlKSB7IGhwID0gbWF4SHAgLSB2YWx1ZX1cblxuICAgIHZhbCBiID0gQmFyYmFyaWFuKDMwMClcbiAgICBiLmhwIC09IDEwMFxuICAgIHByaW50bG4oYi5sb3N0SHApIC8vIDEwMFxuICAgIGIubG9zdEhwID0gMjUwXG4gICAgcHJpbnRsbihiLmhwKSAvLyA1MFxuXG59In0=",target:"_blank",rel:"noopener noreferrer"},Vl=l("h4",{id:"exercice-4",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercice-4"},[l("span",null,"Exercice 4")])],-1),Yl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGphdmEudGltZS5Mb2NhbERhdGVcbmltcG9ydCBqYXZhLnRpbWUuUGVyaW9kXG5cbi8vIC8vIFE6IGNyZWF0ZSBhbiBlbnVtZXJhdGlvbiBjbGFzcyBjYWxsZWQgXCJDYXRDb2xvclwiIHRoYXQgaGFzIHRoZXNlIHZhbHVlczogV0hJVEUsIENSRUFNLCBCTEFDSywgUkVELGFuZCBCUk9XTlxuXG4vLyBROiBjcmVhdGUgYSBkYXRhY2xhc3MgXCJDYXRcIiB0aGF0IGhhcyAzIGltbXV0YWJsZSBwcm9wZXJ0aWVzOiBuYW1lOiBTdHJpbmcsIGJpcnRoRGF0ZTogTG9jYWxEYXRlIGFuZCBjb2xvcjogQ2F0Q29sb3JcblxuZnVuIG1haW4oKXtcbiAgICAvLyBROiBhZGQgYSBjYXQgd2l0aCBjcmVhbSBjb2xvciBhbmQgYW5vdGhlciBvbmUgd2l0aCBibGFjayBjb2xvclxuICAgIHZhbCBjYXRzID0gbGlzdE9mKFxuICAgICAgQ2F0KFwiQmVsbGFcIiwgTG9jYWxEYXRlLnBhcnNlKFwiMjAyMC0xMC0xMlwiKSwgQ2F0Q29sb3IuV0hJVEUpLFxuICAgICAgQ2F0KFwiU2ltYmFcIiwgTG9jYWxEYXRlLnBhcnNlKFwiMjAxMC0wMS0zMVwiKSwgQ2F0Q29sb3IuQlJPV04pLFxuICAgICAgQ2F0KFwiS2l0dHlcIiwgTG9jYWxEYXRlLnBhcnNlKFwiMjAxNy0wNS0wNlwiKSwgQ2F0Q29sb3IuV0hJVEUpXG4gICAgKVxuICAgIFxuICAgIC8vIFBsZWFzZSB1c2UgYSBkZWNsYXJhdGl2ZSBzdHlsZSBpbiB0aGlzIGV4ZXJjaXNlXG5cbiAgICAvLyBROiBwcmludCB0aGUgbmFtZXMgb2Ygd2hpdGUgY2F0c1xuXG4gICAgLy8gUTogcHJpbnQgdGhlIG5hbWUgYW5kIGFnZSAoYW5kIGp1c3QgdGhlIG5hbWUgYW5kIGFnZSkgb2YgY2F0cyB0aGF0IGFyZSBhdCBsZWFzdCA1IHllYXJzIG9sZC5cbiAgICAvLyBIaW50OiBpbXBvcnQgamF2YS50aW1lLlBlcmlvZCBhbmQgdXNlIFBhaXIgY2xhc3NcbiAgICAgICBcbiAgICBcbiAgICAvLyBMZXQncyBzdXBwb3NlIHRoYXQgd2UgaGF2ZSBhbiBhcnJheSBvZiBvd25lcnMgd2hlcmUgZWFjaCBlbGVtZW50IGlzIHRoZSBvd25lciBvZiB0aGUgY29ycmVzcG9uZGluZyBpbmRleCBpbiB0aGUgY2F0cyB0YWJsZS4gRm9yIGV4YW1wbGUgQW5kYW5vciBpcyB0aGUgb3duZXIgb2YgS2l0dHlcbiAgICB2YWwgb3duZXJzID0gbGlzdE9mKFwiSXJ5ZW5cIiwgXCJJcnllblwiLCBcIkFuZGFub3JcIiwgXCJBbmRhbm9yXCIsIFwiR2FuZGFsZlwiKVxuICAgIFxuICAgIC8vIFE6IHVzaW5nIHRoZSB6aXAgbWV0aG9kLCBwcmludCB0aGUgbmFtZSBvZiBjYXRzIG93bmVkIGJ5IFwiSXJ5ZW5cIlxufSJ9",target:"_blank",rel:"noopener noreferrer"},Jl={class:"custom-container details"},Rl=l("summary",null,"Solutions(s)",-1),xl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGphdmEudGltZS5Mb2NhbERhdGVcbmltcG9ydCBqYXZhLnRpbWUuUGVyaW9kXG5cbi8vIC8vIFE6IGNyZWF0ZSBhbiBlbnVtZXJhdGlvbiBjbGFzcyBjYWxsZWQgXCJDYXRDb2xvclwiIHRoYXQgaGFzIHRoZXNlIHZhbHVlczogV0hJVEUsIENSRUFNLCBCTEFDSywgUkVELGFuZCBCUk9XTlxuZW51bSBjbGFzcyBDYXRDb2xvciB7XG4gICAgV0hJVEUsIENSRUFNLCBCTEFDSywgUkVELCBCUk9XTlxufVxuXG4vLyBROiBjcmVhdGUgYSBkYXRhY2xhc3MgXCJDYXRcIiB0aGF0IGhhcyAzIGltbXV0YWJsZSBwcm9wZXJ0aWVzOiBuYW1lOiBTdHJpbmcsIGJpcnRoRGF0ZTogTG9jYWxEYXRlIGFuZCBjb2xvcjogQ2F0Q29sb3Jcbi8vIEhpbnQ6IGltcG9ydCBqYXZhLnRpbWUuTG9jYWxEYXRlXG5kYXRhIGNsYXNzIENhdCh2YWwgbmFtZTogU3RyaW5nLCB2YWwgYmlydGhEYXRlOiBMb2NhbERhdGUsIHZhbCBjb2xvcjogQ2F0Q29sb3IpXG5cbmZ1biBtYWluKCl7XG4gICAgLy8gUTogYWRkIGEgY2F0IHdpdGggY3JlYW0gY29sb3IgYW5kIGFub3RoZXIgb25lIHdpdGggYmxhY2sgY29sb3JcbiAgICB2YWwgY2F0cyA9IGxpc3RPZihcbiAgICAgIENhdChcIkJlbGxhXCIsIExvY2FsRGF0ZS5wYXJzZShcIjIwMjAtMTAtMTJcIiksIENhdENvbG9yLldISVRFKSxcbiAgICAgIENhdChcIlNpbWJhXCIsIExvY2FsRGF0ZS5wYXJzZShcIjIwMTAtMDEtMzFcIiksIENhdENvbG9yLkJST1dOKSxcbiAgICAgIENhdChcIktpdHR5XCIsIExvY2FsRGF0ZS5wYXJzZShcIjIwMTctMDUtMDZcIiksIENhdENvbG9yLldISVRFKSxcbiAgICAgIENhdChcIk1pYW91XCIsIExvY2FsRGF0ZS5wYXJzZShcIjIwMTEtMDItMjVcIiksIENhdENvbG9yLkNSRUFNKSxcbiAgICAgIENhdChcIk55YW5cIiwgTG9jYWxEYXRlLnBhcnNlKFwiMjAxNC0wNy0xMVwiKSwgQ2F0Q29sb3IuQkxBQ0spXG4gICAgKVxuICAgIFxuICAgIC8vIFBsZWFzZSB1c2UgYSBkZWNsYXJhdGl2ZSBzdHlsZSBpbiB0aGlzIGV4ZXJjaXNlXG5cbiAgICAvLyBROiBwcmludCB0aGUgbmFtZXMgb2Ygd2hpdGUgY2F0c1xuICAgIHZhbCB3aGl0ZUNhdE5hbWVzID0gY2F0cy5maWx0ZXIgeyBpdC5jb2xvciA9PSBDYXRDb2xvci5XSElURSB9Lm1hcCB7IGl0Lm5hbWUgfVxuICAgIHByaW50bG4oXCJXaGl0ZSBjYXRzOiAke3doaXRlQ2F0TmFtZXMuam9pblRvU3RyaW5nKFwiLCBcIil9XCIpXG5cbiAgICAvLyBROiBwcmludCB0aGUgbmFtZSBhbmQgYWdlIChhbmQganVzdCB0aGUgbmFtZSBhbmQgYWdlKSBvZiBjYXRzIHRoYXQgYXJlIGF0IGxlYXN0IDUgeWVhcnMgb2xkLlxuICAgIC8vIEhpbnQ6IGltcG9ydCBqYXZhLnRpbWUuUGVyaW9kIGFuZCB1c2UgUGFpciBjbGFzc1xuICAgIHZhbCBNb3JlVGhhbjV5b0NhdHMgPSBjYXRzLmZpbHRlciB7IFBlcmlvZC5iZXR3ZWVuKGl0LmJpcnRoRGF0ZSwgTG9jYWxEYXRlLm5vdygpKS5nZXRZZWFycygpID49IDUgfVxuICAgIFx0Lm1hcCB7IFBhaXIoaXQubmFtZSwgUGVyaW9kLmJldHdlZW4oaXQuYmlydGhEYXRlLCBMb2NhbERhdGUubm93KCkpLmdldFllYXJzKCkpIH1cbiAgICAgICAgLm1hcCB7IFwiKG5hbWU6ICR7aXQuZmlyc3R9IC0gYWdlOiAke2l0LnNlY29uZH0pXCIgfVxuICAgIHByaW50bG4oXCJXaGl0ZSBjYXRzOiAke01vcmVUaGFuNXlvQ2F0cy5qb2luVG9TdHJpbmcoXCIsIFwiKX1cIilcbiAgICAgICAgXG4gICAgXG4gICAgLy8gTGV0J3Mgc3VwcG9zZSB0aGF0IHdlIGhhdmUgYW4gYXJyYXkgb2Ygb3duZXJzIHdoZXJlIGVhY2ggZWxlbWVudCBpcyB0aGUgb3duZXIgb2YgdGhlIGNvcnJlc3BvbmRpbmcgaW5kZXggaW4gdGhlIGNhdHMgdGFibGUuIEZvciBleGFtcGxlIEFuZGFub3IgaXMgdGhlIG93bmVyIG9mIEtpdHR5XG4gICAgdmFsIG93bmVycyA9IGxpc3RPZihcIklyeWVuXCIsIFwiSXJ5ZW5cIiwgXCJBbmRhbm9yXCIsIFwiQW5kYW5vclwiLCBcIkdhbmRhbGZcIilcbiAgICBcbiAgICAvLyBROiB1c2luZyB0aGUgemlwIG1ldGhvZCwgcHJpbnQgdGhlIG5hbWUgb2YgY2F0cyBvd25lZCBieSBcIklyeWVuXCJcblx0dmFsIGlyeWVuQ2F0cyA9IChvd25lcnMgemlwIGNhdHMpLmZpbHRlciB7IGl0LmZpcnN0ID09IFwiSXJ5ZW5cIn0ubWFwIHsgaXQuc2Vjb25kLm5hbWUgfVxuICAgIHByaW50bG4oXCJJcnllbidzIGNhdHMgYXJlOiAke2lyeWVuQ2F0cy5qb2luVG9TdHJpbmcoXCIsIFwiKX1cIilcbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},vl=l("h4",{id:"exercice-5",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercice-5"},[l("span",null,"Exercice 5")])],-1),Fl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gVGhlICd0aGlzJyBrZXl3b3JkIGluc2lkZSBhbiBleHRlbnNpb24gY29ycmVzcG9uZHMgdG8gdGhlIHJlY2VpdmVyIG9iamVjdCAodGhlIG9iamVjdCB0aGF0IGNhbGxzIHRoZSBleHRlbnNpb24pXG4vLyBUT0RPIGNlZWF0ZSBhbiBleHRlbnNpb24gZnVuY3Rpb24gJ21ha2VRdWVzdGlvbicgb2YgdGhlIFN0cmluZyBjbGFzcyB0aGF0IHJldHVybiBhIG5ldyBTdHJpbmcgdGhhdCBjb250YWlucyB0aGUgb3JpZ2luYWwgU3RyaW5nICsgJz8nIGF0IHRoZSBlbmRcblxuLy8gcHJpbnRsbihcIkhvdyBkbyB5b3UgZG9cIi5tYWtlUXVlc3Rpb24oKSkgLy8gc2hvdWxkIHByaW50IFwiSG93IGRvIHlvdSBkbyA/XCJcblxuY2xhc3MgTWFnZShocDogSW50KTogRXh0ZW5zaW9uczAxLkFkdmVudHVyZXIoaHApe1xuICAgIGNvbXBhbmlvbiBvYmplY3Qge1xuICAgICAgICB2YXIgY291bnQgPSAwXG4gICAgfVxuICAgIGluaXQge1xuICAgICAgICBNYWdlLmNvdW50ICs9IDFcbiAgICB9XG59XG4vLyBUT0RPIGRlZmluZSBhbiBleHRlbnNpb24gZnVuY3Rpb24gb24gdGhlIGNvbXBhbmlvbiBvYmplY3QgdGhhdCBwcmludHMgdGhlIG51bWJlciBvZiBjcmVhdGVkIG1hZ2VzLlxuLy8gaGludDogXCJ0aGlzXCIgYWxsb3dzIHRvIHJlZmVyZW5jZSB0aGUgY29tcGFuaW9uIG9iamVjdFxuXG52YWwgbWFnZSA9IE1hZ2UoMTAwKVxudmFsIG1hZ2UyID0gTWFnZSgyMDApXG5wcmludChcIk1hZ2UgY291bnQ6IFwiKVxuLy8gTWFnZS5wcmludENvdW50KCkgLy8gMlxuXG4vLyBUT0RPIGNyYXRlIGFuIGV4dGVuc2lvbiBmdW5jdGlvbiBcImV2ZW5Db3VudCgpOkludFwiIG9uIExpc3Q8SW50PiB0aGF0IHJldHVybiB0aGUgbnVtYmVyIG9mIGV2ZW4gdmFsdWVzXG5cbi8vcHJpbnRsbihsaXN0T2YoMSwgMiwgMywgNCwgNSkuZXZlbkNvdW50KCkpIC8vIHByaW50cyAyXG5cbi8vIFRPRE8gY3JlYXRlIGEgZ2VuZXJpYyBleHRlbnNpb24gZnVuY3Rpb24gXCJjb25jYXRlbmF0ZSgpOlN0cmluZ1wiIG9uIExpc3Q8VD4gdGhhdCBjb25jYXRlbmF0ZXMgdGhlIHRvU3RyaW5nIHZhbHVlIG9mIGFsbCBvZiB0aGUgZWxlbWVudHMgb2YgdGhlIGxpc3RcblxuLy8gcHJpbnRsbihsaXN0T2YoXCJIZWxsbyBcIiwgXCJXb3JsZFwiLCBcIkxpbmVcIikuY29uY2F0ZW5hdGUoKSkgLy8gc2hvdWxkIHByaW50IFwiSGVsbG8gV29ybGRMaW5lXCJcbi8vIHByaW50bG4obGlzdE9mKDEwMCwgMjAwLCAzMDApLmNvbmNhdGVuYXRlKCkpIC8vIHNob3VsZCBwcmludCBcIjEwMDIwMDMwMFwiXG5cbi8vIFRPRE8gY3JlYXRlIGEgZ2VuZXJpYyBleHRlbnNpb24gZnVuY3Rpb24gXCJMaXN0Py5tYWtlTm90TnVsbCgpOkxpc3QnICB0aGF0IHJldHVybnMgYW4gZW1wdHkgbGlzdCBpZiB0aGUgcmVjZWl2ZXIgaXMgbnVsbCwgb3IgdGhlIG9yaWdpbmFsIGxpc3Qgb3JoZXJ3aXNlXG5cbi8vIHByaW50bG4obGlzdE9mKDEsIDIsIDMpLm1ha2VOb3ROdWxsKCkpIC8vIHNob3VsZCBwcmludCBbMSwgMiwgM11cbi8vIHZhbCBsOiBMaXN0PEludD4/ID0gbnVsbFxuLy8gcHJpbnRsbihsLm1ha2VOb3ROdWxsKCkpIC8vIHNob3VsZCBwcmludCBbXSJ9",target:"_blank",rel:"noopener noreferrer"},Hl={class:"custom-container details"},wl=l("summary",null,"Solution(s)",-1),Nl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gVGhlICd0aGlzJyBrZXl3b3JkIGluc2lkZSBhbiBleHRlbnNpb24gY29ycmVzcG9uZHMgdG8gdGhlIHJlY2VpdmVyIG9iamVjdCAodGhlIG9iamVjdCB0aGF0IGNhbGxzIHRoZSBleHRlbnNpb24pXG4vLyBUT0RPIGNlZWF0ZSBhbiBleHRlbnNpb24gZnVuY3Rpb24gJ21ha2VRdWVzdGlvbicgb2YgdGhlIFN0cmluZyBjbGFzcyB0aGF0IHJldHVybiBhIG5ldyBTdHJpbmcgdGhhdCBjb250YWlucyB0aGUgb3JpZ2luYWwgU3RyaW5nICsgJz8nIGF0IHRoZSBlbmRcblxuLy8gcHJpbnRsbihcIkhvdyBkbyB5b3UgZG9cIi5tYWtlUXVlc3Rpb24oKSkgLy8gc2hvdWxkIHByaW50IFwiSG93IGRvIHlvdSBkbyA/XCJcblxuLy8gVE9ETyBkZWZpbmUgYW4gZXh0ZW5zaW9uIGZ1bmN0aW9uIG9uIHRoZSBjb21wYW5pb24gb2JqZWN0IHRoYXQgcHJpbnRzIHRoZSBudW1iZXIgb2YgY3JlYXRlZCBtYWdlcy5cbmNsYXNzIE1hZ2UoaHA6IEludCk6IEV4dGVuc2lvbnMwMS5BZHZlbnR1cmVyKGhwKXtcbiAgICBjb21wYW5pb24gb2JqZWN0IHtcbiAgICAgICAgdmFyIGNvdW50ID0gMFxuICAgIH1cbiAgICBpbml0IHtcbiAgICAgICAgTWFnZS5jb3VudCArPSAxXG4gICAgfVxufVxuLy8gaGludDogXCJ0aGlzXCIgYWxsb3dzIHRvIHJlZmVyZW5jZSB0aGUgY29tcGFuaW9uIG9iamVjdFxuZnVuIE1hZ2UuQ29tcGFuaW9uLnByaW50Q291bnQoKSA9IHByaW50bG4odGhpcy5jb3VudClcblxudmFsIG1hZ2UgPSBNYWdlKDEwMClcbnZhbCBtYWdlMiA9IE1hZ2UoMjAwKVxucHJpbnQoXCJNYWdlIGNvdW50OiBcIilcbk1hZ2UucHJpbnRDb3VudCgpIC8vIDJcblxuLy8gVE9ETyBjcmF0ZSBhbiBleHRlbnNpb24gZnVuY3Rpb24gXCJldmVuQ291bnQoKTpJbnRcIiBvbiBMaXN0PEludD4gdGhhdCByZXR1cm4gdGhlIG51bWJlciBvZiBldmVuIHZhbHVlc1xuXG4vL3ByaW50bG4obGlzdE9mKDEsIDIsIDMsIDQsIDUpLmV2ZW5Db3VudCgpKSAvLyBwcmludHMgMlxuXG4vLyBUT0RPIGNyZWF0ZSBhIGdlbmVyaWMgZXh0ZW5zaW9uIGZ1bmN0aW9uIFwiY29uY2F0ZW5hdGUoKTpTdHJpbmdcIiBvbiBMaXN0PFQ+IHRoYXQgY29uY2F0ZW5hdGVzIHRoZSB0b1N0cmluZyB2YWx1ZSBvZiBhbGwgb2YgdGhlIGVsZW1lbnRzIG9mIHRoZSBsaXN0XG5mdW4gPFQ+IExpc3Q8VD4uY29uY2F0ZW5hdGUoKTogU3RyaW5nID0gdGhpcy5tYXAgeyB2IC0+IHYudG9TdHJpbmcoKSB9LnJlZHVjZSB7IGFjYywgcyAtPiBhY2MgKyBzIH1cbnByaW50bG4obGlzdE9mKFwiSGVsbG8gXCIsIFwiV29ybGRcIiwgXCJMaW5lXCIpLmNvbmNhdGVuYXRlKCkpIC8vIHNob3VsZCBwcmludCBcIkhlbGxvIFdvcmxkTGluZVwiXG5wcmludGxuKGxpc3RPZigxMDAsIDIwMCwgMzAwKS5jb25jYXRlbmF0ZSgpKSAvLyBzaG91bGQgcHJpbnQgXCIxMDAyMDAzMDBcIlxuXG4vLyBUT0RPIGNyZWF0ZSBhIGdlbmVyaWMgZXh0ZW5zaW9uIGZ1bmN0aW9uIFwiTGlzdD8ubWFrZU5vdE51bGwoKTpMaXN0JyAgdGhhdCByZXR1cm4gYW4gZW1wdHkgbGlzdCBpZiB0aGUgcmVjZWl2ZXIgaXMgbnVsbCwgb3IgdGhlIG9yaWdpbmFsIGxpc3Qgb3JoZXJ3aXNlXG5mdW4gPFQ+IExpc3Q8VD4/Lm1ha2VOb3ROdWxsKCk6IExpc3Q8VD57XG4gICAgcmV0dXJuIGxpc3RPZjxUPigpXG59XG5wcmludGxuKGxpc3RPZigxLCAyLCAzKS5tYWtlTm90TnVsbCgpKSAvLyBzaG91bGQgcHJpbnQgWzEsIDIsIDNdXG52YWwgbDogTGlzdDxJbnQ+PyA9IG51bGxcbnByaW50bG4obC5tYWtlTm90TnVsbCgpKSAvLyBzaG91bGQgcHJpbnQgW10ifQ==",target:"_blank",rel:"noopener noreferrer"},Sl=l("h4",{id:"exercice-6",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercice-6"},[l("span",null,"Exercice 6")])],-1),kl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gVE9ETyB1c2luZyB0aGUgb2JqZWN0IGRlY2xhcmF0aW9uIHN5bmN0YXgsIGNyZWF0ZSBhIHNpbmdsZXRvbiBEcmlua0Rpc3BlbnNlciB0aGF0IGhhcyB0aGVzZSBtZXRob2RzOiBwdXREcmluayhEcmluayksIExpc3Q8RHJpbms+IGFsbEF2YWlsYWJsZURyaW5rcygpIGFuZCBwaWNrTGFzdERyaW5rKCk6RHJpbmtcbi8vICBTeW50YXg6IG9iamVjdCBTaW5nbGV0b25OYW1lIHsgLi4uIH0gfVxuXG4vLyBUT0RPIHVuY29tbWVudCB0byB0ZXN0IHlvdSBjb2RlXG4vL0RyaW5rRGlzcGVuc2VyLnB1dERyaW5rKE9iamVjdEV4cHJlc3Npb25zMDEuRHJpbmsoMTAwKSlcbi8vRHJpbmtEaXNwZW5zZXIucHV0RHJpbmsoT2JqZWN0RXhwcmVzc2lvbnMwMS5Ecmluayg1MCkpXG4vL0RyaW5rRGlzcGVuc2VyLnB1dERyaW5rKE9iamVjdEV4cHJlc3Npb25zMDEuRHJpbmsoNzApKVxuLy9wcmludGxuKERyaW5rRGlzcGVuc2VyLmFsbEF2YWlsYWJsZURyaW5rcygpLnNpemUpIC8vIDNcbi8vRHJpbmtEaXNwZW5zZXIucHV0RHJpbmsoT2JqZWN0RXhwcmVzc2lvbnMwMS5waWNrTGFzdERyaW5rKCkpXG4vL3ByaW50bG4oRHJpbmtEaXNwZW5zZXIuYWxsQXZhaWxhYmxlRHJpbmtzKCkuc2l6ZSkgLy8gMlxuXG4vLyBUT0RPIGNyZWF0ZSBhIGNsYXNzIG5hbWVkIERyaW5rTWFrZXIuIFdlIHdhbnQgdG8gaGF2ZSBhIHN0YXRpYyBtZXRob2QgXCJjcmVhdGVEcmluaygpOiBPYmplY3RFeHByZXNzaW9uczAxLkRyaW5rXCIgdGhhdCBjcmVhdGVzIGEgbmV3IERyaW5rLlxuLy8gICBBbiBvYmplY3QgZGVjbGFyYXRpb24gaW5zaWRlIGEgY2xhc3MgY2FuIGJlIG1hcmtlZCBhcyBjb21wYW5pb25cbi8vICAgU3ludGF4OlxuLypcbmNsYXNzIENsYXNzTmFtZSB7XG4gICAgY29tcGFuaW9uIG9iamVjdCAob3B0aW9ubmFsIG5hbWUpIHtcbiAgICAgICAgLi4uXG4gICAgfVxufVxuKi9cblxuXG4vLyBUT0RPIHVuY29tbWVudCB0byB0ZXN0XG4vL3ZhbCBkcmluayA9IERyaW5rTWFrZXIuY3JlYXRlRHJpbmsoKVxuLy92YWwgZHJpbmsyID0gRHJpbmtNYWtlci5Db21wYW5pb24uY3JlYXRlRHJpbmsoKVxuXG4vLyBUT0RPIHdoYXQgaXMgdGhlIHR5cGUgb2YgY29tcGFuaW9uT2JqZWN0XG4vL3ZhbCBjb21wYW5pb25PYmplY3QgPSBEcmlua01ha2VyXG4vL2NvbXBhbmlvbk9iamVjdC5jcmVhdGVEcmluaygpXG5cbi8vIFRPRE8gbWFrZSB0aGUgZWFybGllciBjb21wYW5pb24gb2JqZWN0IGltcGxlbWVudCB0aGUgZm9sbG93aW5nIENvdW50ZXIgaW50ZXJmYWNlIGFuZCBjb3VudCB0aGUgbnVtYmVyIG9mIGNyZWF0ZWQgRHJpbmtzXG5pbnRlcmZhY2UgQ291bnRlcntcbiAgICB2YXIgY291bnQ6IEludFxufVxuLy8gVE9ETyB1bmNvbW1lbnQgdG8gdGVzdFxuLy9wcmludGxuKERyaW5rTWFrZXIuY291bnQpIC8vIDMifQ==",target:"_blank",rel:"noopener noreferrer"},Ll={class:"custom-container details"},zl=l("summary",null,"solution(s)",-1),El={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gVE9ETyB1c2luZyB0aGUgb2JqZWN0IGRlY2xhcmF0aW9uIHN5bmN0YXgsIGNyZWF0ZSBhIHNpbmdsZXRvbiBEcmlua0Rpc3BlbnNlciB0aGF0IGhhcyB0aGVzZSBtZXRob2RzOiBwdXREcmluayhEcmluayksIExpc3Q8RHJpbms+IGFsbEF2YWlsYWJsZURyaW5rcygpIGFuZCBwaWNrTGFzdERyaW5rKCk6RHJpbmtcbi8vICBTeW50YXg6IG9iamVjdCBTaW5nbGV0b25OYW1lIHsgLi4uIH0gfVxuXG4vLyBUT0RPIHVuY29tbWVudCB0byB0ZXN0IHlvdSBjb2RlXG4vL0RyaW5rRGlzcGVuc2VyLnB1dERyaW5rKE9iamVjdEV4cHJlc3Npb25zMDEuRHJpbmsoMTAwKSlcbi8vRHJpbmtEaXNwZW5zZXIucHV0RHJpbmsoT2JqZWN0RXhwcmVzc2lvbnMwMS5Ecmluayg1MCkpXG4vL0RyaW5rRGlzcGVuc2VyLnB1dERyaW5rKE9iamVjdEV4cHJlc3Npb25zMDEuRHJpbmsoNzApKVxuLy9wcmludGxuKERyaW5rRGlzcGVuc2VyLmFsbEF2YWlsYWJsZURyaW5rcygpLnNpemUpIC8vIDNcbi8vRHJpbmtEaXNwZW5zZXIucHV0RHJpbmsoT2JqZWN0RXhwcmVzc2lvbnMwMS5waWNrTGFzdERyaW5rKCkpXG4vL3ByaW50bG4oRHJpbmtEaXNwZW5zZXIuYWxsQXZhaWxhYmxlRHJpbmtzKCkuc2l6ZSkgLy8gMlxuXG4vLyBUT0RPIGNyZWF0ZSBhIGNsYXNzIG5hbWVkIERyaW5rTWFrZXIuIFdlIHdhbnQgdG8gaGF2ZSBhIHN0YXRpYyBtZXRob2QgXCJjcmVhdGVEcmluaygpOiBPYmplY3RFeHByZXNzaW9uczAxLkRyaW5rXCIgdGhhdCBjcmVhdGVzIGEgbmV3IERyaW5rLlxuLy8gICBBbiBvYmplY3QgZGVjbGFyYXRpb24gaW5zaWRlIGEgY2xhc3MgY2FuIGJlIG1hcmtlZCBhcyBjb21wYW5pb25cbi8vICAgU3ludGF4OlxuLypcbmNsYXNzIENsYXNzTmFtZSB7XG4gICAgY29tcGFuaW9uIG9iamVjdCAob3B0aW9ubmFsIG5hbWUpIHtcbiAgICAgICAgLi4uXG4gICAgfVxufVxuKi9cbmNsYXNzIERyaW5rTWFrZXIge1xuICAgIGNvbXBhbmlvbiBvYmplY3Q6IENvdW50ZXJ7XG4gICAgICAgIHByaXZhdGUgdmFyIG5iQ3JlYXRlZCA9IDBcbiAgICAgICAgb3ZlcnJpZGUgdmFyIGNvdW50OiBJbnRcbiAgICAgICAgICAgIGdldCgpID0gbmJDcmVhdGVkXG4gICAgICAgICAgICBzZXQodmFsdWUpIHsgbmJDcmVhdGVkID0gdmFsdWV9XG5cbiAgICAgICAgZnVuIGNyZWF0ZURyaW5rKCkgOiBPYmplY3RFeHByZXNzaW9uczAxLkRyaW5re1xuICAgICAgICAgICAgY291bnQgKz0gMVxuICAgICAgICAgICAgcmV0dXJuIE9iamVjdEV4cHJlc3Npb25zMDEuRHJpbmsoMTAwKVxuICAgICAgICB9XG4gICAgfVxufVxuXG4vLyBUT0RPIHVuY29tbWVudCB0byB0ZXN0XG4vL3ZhbCBkcmluayA9IERyaW5rTWFrZXIuY3JlYXRlRHJpbmsoKVxuLy92YWwgZHJpbmsyID0gRHJpbmtNYWtlci5Db21wYW5pb24uY3JlYXRlRHJpbmsoKVxuXG4vLyBUT0RPIHdoYXQgaXMgdGhlIHR5cGUgb2YgY29tcGFuaW9uT2JqZWN0XG4vL3ZhbCBjb21wYW5pb25PYmplY3QgPSBEcmlua01ha2VyXG4vL2NvbXBhbmlvbk9iamVjdC5jcmVhdGVEcmluaygpXG5cbi8vIFRPRE8gbWFrZSB0aGUgZWFybGllciBjb21wYW5pb24gb2JqZWN0IGltcGxlbWVudCB0aGUgZm9sbG93aW5nIENvdW50ZXIgaW50ZXJmYWNlIGFuZCBjb3VudCB0aGUgbnVtYmVyIG9mIGNyZWF0ZWQgRHJpbmtzXG5pbnRlcmZhY2UgQ291bnRlcntcbiAgICB2YXIgY291bnQ6IEludFxufVxuLy8gVE9ETyB1bmNvbW1lbnQgdG8gdGVzdFxuLy9wcmludGxuKERyaW5rTWFrZXIuY291bnQpIC8vIDMifQ==",target:"_blank",rel:"noopener noreferrer"},jl=l("h4",{id:"exercice-7",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#exercice-7"},[l("span",null,"Exercice 7")])],-1),Ul={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoicGFja2FnZSBjb20ud29ybGRsaW5lLmxlYXJuaW5nLmtvdGxpbi5vYmplY3RzQW5kQ29tcGFuaW9uc1xuXG4vLyBPYmplY3RzIGluIGtvdGxpbiBodHRwczovL2tvdGxpbmxhbmcub3JnL2RvY3MvcmVmZXJlbmNlL29iamVjdC1kZWNsYXJhdGlvbnMuaHRtbFxub3BlbiBjbGFzcyBEcmluayh2YXIgdm9sdW1lOiBJbnQpe1xuICAgIG9wZW4gZnVuIGNvbnN1bWUoY29uc3VtZWRWb2x1bWU6IEludCl7XG4gICAgICAgIHRoaXMudm9sdW1lIC09IGNvbnN1bWVkVm9sdW1lXG4gICAgfVxufVxuaW50ZXJmYWNlIENvbG9yZWR7XG4gICAgdmFsIGNvbG9yOlN0cmluZ1xufVxuXG4vLyBhbm9ueW1vdXMgb2JqZWN0cyBoYXZlIGFjY2VzcyB0byB2YXJpYWJsZXMgaW4gdGhlIGVuY2xvc2luZyBzY29wZVxuZnVuIGNyZWF0ZURyaW5rQW5kQ291bnQoKTogRHJpbmt7XG4gICAgdmFyIG5iQ29uc3VtcHRpb24gPSAwXG4gICAgcmV0dXJuIG9iamVjdCA6IERyaW5rKDEwMCl7XG4gICAgICAgIG92ZXJyaWRlIGZ1biBjb25zdW1lKGNvbnN1bWVkVm9sdW1lOiBJbnQpIHtcbiAgICAgICAgICAgIHN1cGVyLmNvbnN1bWUoY29uc3VtZWRWb2x1bWUpXG4gICAgICAgICAgICBuYkNvbnN1bXB0aW9uICs9IDFcbiAgICAgICAgICAgIHByaW50bG4obmJDb25zdW1wdGlvbilcbiAgICAgICAgfVxuICAgIH1cbn1cblxuZnVuIG1haW4oKSB7XG4gICAgLy8gQ3JlYXRpbmcgYW4gaW5zdGFuY2Ugb2YgRHJpbmtcbiAgICB2YWwgc21hbGxCb3R0bGUgPSBEcmluaygxMDApXG4gICAgLy8gVE9ETyBjcmVhdGUgYSBiaWdCb3R0bGUgd2l0aCBhIHZvbHVtZSBvZiA1MDBcblxuICAgIC8vIEtvdGxpbiBhbGxvd3MgdG8gcXVpY2tseSBjcmVhdGUgYW4gYW5vbnltb3VzIG9iamVjdCB0aGF0IGltcGxlbWVudHMgYW5kIGV4aXN0aW5nIGNsYXNzIGFuZC9vciBpbnRlcmZhY2VzXG4gICAgdmFsIHNtYWxsQ2FyYm9uYXRlZERyaW5rQm90dGxlID0gb2JqZWN0OiBEcmluaygxMDApIHtcbiAgICAgICAgb3ZlcnJpZGUgZnVuIGNvbnN1bWUoY29uc3VtZWRWb2x1bWU6IEludCkge1xuICAgICAgICAgICAgc3VwZXIuY29uc3VtZShjb25zdW1lZFZvbHVtZSlcbiAgICAgICAgICAgIHByaW50bG4oXCJQU0hISEhISFwiKVxuICAgICAgICB9XG4gICAgfVxuICAgIHNtYWxsQ2FyYm9uYXRlZERyaW5rQm90dGxlLmNvbnN1bWUoMjApXG5cbiAgICAvLyBUT0RPIGNyZWF0ZWQgYW4gYW5vbnltb3VzIG9iamVjdCBzbWFsbENhcmJvbmF0ZWRSZWREcmlua0JvdHRsZSB0aGF0IGlzIGJvdGggYSBEcmluayBhbmQgQ29sb3JlZFxuICAgIC8vIFRPRE8gQ2FuIGtvdGxpbiBpbmZlciB0aGUgdHlwZSBvZiBzbWFsbENhcmJvbmF0ZWRSZWREcmlua0JvdHRsZSA/XG5cbiAgICAvLyBUT0RPIG1ha2UgdGhpcyBjb2RlIHByaW50IFwiUmVkXCJcbiAgICAvLyBwcmludGxuKHNtYWxsQ2FyYm9uYXRlZFJlZERyaW5rQm90dGxlLmNvbG9yKVxuICAgIFxuICAgIFxuICAgIC8vIFRPRE8gd2hhdCBpcyBwcmludGVkIGFmdGVyIGNhbGxpbmcgdGhlIGZvbGxvd2luZyBjb2RlID9cbiAgICB2YWwgZHJpbmtUaGF0Q291bnRzID0gY3JlYXRlRHJpbmtBbmRDb3VudCgpXG4gICAgZHJpbmtUaGF0Q291bnRzLmNvbnN1bWUoMTApXG4gICAgZHJpbmtUaGF0Q291bnRzLmNvbnN1bWUoMjApXG5cbiAgICAvLyBUT0RPIFRyeSB0byBndWVzcyB3aGF0IGlzIHByaW50ZWQgaWYgd2UgZ2V0IGFub3RoZXIgZHJpbmsgZnJvbSAnY3JlYXRlRHJpbmtBbmRDb3VudCgpJyBhbmQgY2FsbCBjb25zdW1lID9cblxuICAgIFxuICAgIC8vIEtvdGxpbiBhbHNvIGFsbG93cyB0byBjcmVhdGUgYW5vbnltb3VzIG9iamVjdHMgd2l0aG91dCBzdXBlcnR5cGVzXG4gICAgZnVuIG1peChkcmlua1RvTWl4OiBEcmluayl7XG4gICAgXHR2YWwgbWl4dHVyZSA9IG9iamVjdCB7XG4gICAgICAgICAgICB2YWwgZHJpbmsgPSBkcmlua1RvTWl4XG4gICAgICAgICAgICB2YWwgaW5ncmVkaWVudCA9IFwiU2FsdFwiXG4gICAgXHR9XG4gICAgICAgIC8vIFRPRE8gdHJ5IHRvIHByaW50IHRoZSByZXN1bHQgb2YgbWl4dHVyZS5pbmdyZWRpZW50ICsgbWl4dHVyZS5kcmlua1RvTWl4XG4gICAgICAgIHByaW50bG4oKVxuICAgIH1cbiAgICBtaXgoc21hbGxCb3R0bGUpXG4gICAgXG4gICAgXG4gICAgLy8gTGV0J3MgdHJ5IHRvIHJldHVybiBhbiBhZGhvYyBhbm9ueW1vdXMgb2JqZWN0XG4gICAgZnVuIHByZXBhcmVOZXdNaXgoZHJpbmtUb01peDogRHJpbmspID0gb2JqZWN0IHtcbiAgICAgICAgdmFsIGRyaW5rID0gZHJpbmtUb01peFxuICAgICAgICB2YWwgaW5ncmVkaWVudCA9IFwiU2FsdFwiXG4gICAgfVxuICAgIHZhbCBuZXdNaXggPSBwcmVwYXJlTmV3TWl4KHNtYWxsQm90dGxlKVxuICAgIC8vIFRPRE8gdHlwZSB0byBwcmludCB0aGUgY29tcG9uZW50cyBvZiBuZXdNaXguIElzIGl0IHBvc3NpYmxlIHRvIGFjY2VzcyB0aGUgY29udGVudHMgb2YgdGhlIGFub255bW91cyBvYmplY3Qgb3V0c2lkZSB0aGUgbG9jYWwgc2NvcGUgP1xuXG4gICAgLy8gVE9ETyB3aGF0IGhhcHBlbnMgaWYgd2UgcmVtb3ZlIHRoZSBwcml2YXRlIHF1YWxpZmllciB0byBwcmVwYXJlID9cbiAgICBjbGFzcyBEcmlua01peGVyIHtcbiAgICAgICAgcHJpdmF0ZSBmdW4gcGVyYXByZShkcmlua1RvTWl4OiBEcmluaykgPSBvYmplY3Qge1xuICAgICAgICAgICAgdmFsIGRyaW5rID0gZHJpbmtUb01peFxuICAgICAgICAgICAgdmFsIGluZ3JlZGllbnQgPSBcIlNhbHRcIlxuICAgICAgICB9XG5cbiAgICAgICAgZnVuIG1peCgpe1xuICAgICAgICAgICAgdmFsIHByZXBhcmF0aW9uID0gcGVyYXByZShEcmluaygxMDApKVxuICAgICAgICAgICAgcHJpbnRsbihcIiR7cHJlcGFyYXRpb24uZHJpbmt9IGFuZCAke3ByZXBhcmF0aW9uLmluZ3JlZGllbnR9XCIpXG4gICAgICAgIH1cblx0fVxufSJ9",target:"_blank",rel:"noopener noreferrer"},fl={class:"custom-container details"},Ml=l("summary",null,"Solution(s)",-1),Kl={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gT2JqZWN0cyBpbiBrb3RsaW4gaHR0cHM6Ly9rb3RsaW5sYW5nLm9yZy9kb2NzL3JlZmVyZW5jZS9vYmplY3QtZGVjbGFyYXRpb25zLmh0bWxcbm9wZW4gY2xhc3MgRHJpbmsodmFyIHZvbHVtZTogSW50KXtcbiAgICBvcGVuIGZ1biBjb25zdW1lKGNvbnN1bWVkVm9sdW1lOiBJbnQpe1xuICAgICAgICB0aGlzLnZvbHVtZSAtPSBjb25zdW1lZFZvbHVtZVxuICAgIH1cbn1cbmludGVyZmFjZSBDb2xvcmVke1xuICAgIHZhbCBjb2xvcjpTdHJpbmdcbn1cblxuLy8gYW5vbnltb3VzIG9iamVjdHMgaGF2ZSBhY2Nlc3MgdG8gdmFyaWFibGVzIGluIHRoZSBlbmNsb3Npbmcgc2NvcGVcbmZ1biBjcmVhdGVEcmlua0FuZENvdW50KCk6IERyaW5re1xuICAgIHZhciBuYkNvbnN1bXB0aW9uID0gMFxuICAgIHJldHVybiBvYmplY3QgOiBEcmluaygxMDApe1xuICAgICAgICBvdmVycmlkZSBmdW4gY29uc3VtZShjb25zdW1lZFZvbHVtZTogSW50KSB7XG4gICAgICAgICAgICBzdXBlci5jb25zdW1lKGNvbnN1bWVkVm9sdW1lKVxuICAgICAgICAgICAgbmJDb25zdW1wdGlvbiArPSAxXG4gICAgICAgICAgICBwcmludGxuKG5iQ29uc3VtcHRpb24pXG4gICAgICAgIH1cbiAgICB9XG59XG5cbmZ1biBtYWluKCkge1xuICAgIC8vIENyZWF0aW5nIGFuIGluc3RhbmNlIG9mIERyaW5rXG4gICAgdmFsIHNtYWxsQm90dGxlID0gRHJpbmsoMTAwKVxuICAgIC8vIFRPRE8gY3JlYXRlIGEgYmlnQm90dGxlIHdpdGggYSB2b2x1bWUgb2YgNTAwXG5cbiAgICAvLyBLb3RsaW4gYWxsb3dzIHRvIHF1aWNrbHkgY3JlYXRlIGFuIGFub255bW91cyBvYmplY3QgdGhhdCBpbXBsZW1lbnRzIGFuZCBleGlzdGluZyBjbGFzcyBhbmQvb3IgaW50ZXJmYWNlc1xuICAgIHZhbCBzbWFsbENhcmJvbmF0ZWREcmlua0JvdHRsZSA9IG9iamVjdDogRHJpbmsoMTAwKSB7XG4gICAgICAgIG92ZXJyaWRlIGZ1biBjb25zdW1lKGNvbnN1bWVkVm9sdW1lOiBJbnQpIHtcbiAgICAgICAgICAgIHN1cGVyLmNvbnN1bWUoY29uc3VtZWRWb2x1bWUpXG4gICAgICAgICAgICBwcmludGxuKFwiUFNISEhISEhcIilcbiAgICAgICAgfVxuICAgIH1cbiAgICBzbWFsbENhcmJvbmF0ZWREcmlua0JvdHRsZS5jb25zdW1lKDIwKVxuXG4gICAgLy8gVE9ETyBjcmVhdGVkIGFuIGFub255bW91cyBvYmplY3Qgc21hbGxDYXJib25hdGVkUmVkRHJpbmtCb3R0bGUgdGhhdCBpcyBib3RoIGEgRHJpbmsgYW5kIENvbG9yZWRcbiAgICAvLyBUT0RPIENhbiBrb3RsaW4gaW5mZXIgdGhlIHR5cGUgb2Ygc21hbGxDYXJib25hdGVkUmVkRHJpbmtCb3R0bGUgP1xuICAgIHZhbCBzbWFsbENhcmJvbmF0ZWRSZWREcmlua0JvdHRsZTogQ29sb3JlZCA9IG9iamVjdDogRHJpbmsoMTAwKSwgQ29sb3JlZCB7XG4gICAgICAgIG92ZXJyaWRlIHZhbCBjb2xvcjogU3RyaW5nXG4gICAgICAgICAgICBnZXQoKSA9IFwiUmVkXCJcblxuICAgICAgICBvdmVycmlkZSBmdW4gY29uc3VtZShjb25zdW1lZFZvbHVtZTogSW50KSB7XG4gICAgICAgICAgICBzdXBlci5jb25zdW1lKGNvbnN1bWVkVm9sdW1lKVxuICAgICAgICAgICAgcHJpbnRsbihcIlBTSEhISEhIXCIpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBUT0RPIG1ha2UgdGhpcyBjb2RlIHByaW50IFwiUmVkXCJcbiAgICAvLyBwcmludGxuKHNtYWxsQ2FyYm9uYXRlZFJlZERyaW5rQm90dGxlLmNvbG9yKVxuICAgIFxuICAgIFxuICAgIC8vIFRPRE8gd2hhdCBpcyBwcmludGVkIGFmdGVyIGNhbGxpbmcgdGhlIGZvbGxvd2luZyBjb2RlID9cbiAgICB2YWwgZHJpbmtUaGF0Q291bnRzID0gY3JlYXRlRHJpbmtBbmRDb3VudCgpXG4gICAgZHJpbmtUaGF0Q291bnRzLmNvbnN1bWUoMTApXG4gICAgZHJpbmtUaGF0Q291bnRzLmNvbnN1bWUoMjApXG5cbiAgICAvLyBUT0RPIFRyeSB0byBndWVzcyB3aGF0IGlzIHByaW50ZWQgaWYgd2UgZ2V0IGFub3RoZXIgZHJpbmsgZnJvbSAnY3JlYXRlRHJpbmtBbmRDb3VudCgpJyBhbmQgY2FsbCBjb25zdW1lID9cblxuICAgIFxuICAgIC8vIEtvdGxpbiBhbHNvIGFsbG93cyB0byBjcmVhdGUgYW5vbnltb3VzIG9iamVjdHMgd2l0aG91dCBzdXBlcnR5cGVzXG4gICAgZnVuIG1peChkcmlua1RvTWl4OiBEcmluayl7XG4gICAgXHR2YWwgbWl4dHVyZSA9IG9iamVjdCB7XG4gICAgICAgICAgICB2YWwgZHJpbmsgPSBkcmlua1RvTWl4XG4gICAgICAgICAgICB2YWwgaW5ncmVkaWVudCA9IFwiU2FsdFwiXG4gICAgXHR9XG4gICAgICAgIC8vIFRPRE8gdHJ5IHRvIHByaW50IHRoZSByZXN1bHQgb2YgbWl4dHVyZS5pbmdyZWRpZW50ICsgbWl4dHVyZS5kcmlua1RvTWl4XG4gICAgICAgIHByaW50bG4oKVxuICAgIH1cbiAgICBtaXgoc21hbGxCb3R0bGUpXG4gICAgXG4gICAgXG4gICAgLy8gTGV0J3MgdHJ5IHRvIHJldHVybiBhbiBhZGhvYyBhbm9ueW1vdXMgb2JqZWN0XG4gICAgZnVuIHByZXBhcmVOZXdNaXgoZHJpbmtUb01peDogRHJpbmspID0gb2JqZWN0IHtcbiAgICAgICAgdmFsIGRyaW5rID0gZHJpbmtUb01peFxuICAgICAgICB2YWwgaW5ncmVkaWVudCA9IFwiU2FsdFwiXG4gICAgfVxuICAgIHZhbCBuZXdNaXggPSBwcmVwYXJlTmV3TWl4KHNtYWxsQm90dGxlKVxuICAgIC8vIFRPRE8gdHlwZSB0byBwcmludCB0aGUgY29tcG9uZW50cyBvZiBuZXdNaXguIElzIGl0IHBvc3NpYmxlIHRvIGFjY2VzcyB0aGUgY29udGVudHMgb2YgdGhlIGFub255bW91cyBvYmplY3Qgb3V0c2lkZSB0aGUgbG9jYWwgc2NvcGUgP1xuXG4gICAgLy8gVE9ETyB3aGF0IGhhcHBlbnMgaWYgd2UgcmVtb3ZlIHRoZSBwcml2YXRlIHF1YWxpZmllciB0byBwcmVwYXJlID9cbiAgICBjbGFzcyBEcmlua01peGVyIHtcbiAgICAgICAgcHJpdmF0ZSBmdW4gcGVyYXByZShkcmlua1RvTWl4OiBEcmluaykgPSBvYmplY3Qge1xuICAgICAgICAgICAgdmFsIGRyaW5rID0gZHJpbmtUb01peFxuICAgICAgICAgICAgdmFsIGluZ3JlZGllbnQgPSBcIlNhbHRcIlxuICAgICAgICB9XG5cbiAgICAgICAgZnVuIG1peCgpe1xuICAgICAgICAgICAgdmFsIHByZXBhcmF0aW9uID0gcGVyYXByZShEcmluaygxMDApKVxuICAgICAgICAgICAgcHJpbnRsbihcIiR7cHJlcGFyYXRpb24uZHJpbmt9IGFuZCAke3ByZXBhcmF0aW9uLmluZ3JlZGllbnR9XCIpXG4gICAgICAgIH1cblx0fVxufSJ9",target:"_blank",rel:"noopener noreferrer"},Ql=l("h2",{id:"plus-d-exercices-et-de-lecture",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#plus-d-exercices-et-de-lecture"},[l("span",null,"Plus d'exercices et de lecture")])],-1),Dl={href:"https://play.kotlinlang.org/koans/overview",target:"_blank",rel:"noopener noreferrer"},Tl={href:"https://sderosiaux.medium.com/why-referential-transparency-matters-7c179424dab5",target:"_blank",rel:"noopener noreferrer"};function _l(Pl,ql){const I=g("ExternalLinkIcon");return a(),s("div",null,[t,l("p",null,[l("a",o,[e("▶️ this code"),n(I)]),e(" highlights the above features.")]),u,l("p",null,[l("a",d,[e("▶️ Ce code"),n(I)]),e(" illustre les fonctions en Kotlin.")]),G,l("p",null,[l("a",m,[e("▶️ ce code"),n(I)]),e(" illustrate la "),r,e(" et les types optionnels.")]),p,C,l("p",null,[e("Les énumérations permettent de travailler avec un groupe de valeurs de façon cadrée. Contrairement aux énumérations Java, "),l("a",Z,[e("les énumérations Kotlin sont des classes"),n(I)]),e(". Les "),B,e(" de Kotlin fournissent ces fonctionnalités :")]),X,l("p",null,[l("a",h,[e("▶️ ce code"),n(I)]),e(" illustres les enum en Kotlin de façon succincte. Veuillez consulter "),l("a",W,[e("la documentation officielle pour aller plus loin"),n(I)]),e(".")]),y,A,l("p",null,[l("a",V,[e("Veuillez cliquer sur le lien pour consulter l'énoncé"),n(I)])]),l("details",Y,[J,l("p",null,[l("a",R,[e("Solution"),n(I)])])]),x,l("p",null,[l("a",v,[e("Veuillez cliquer sur le lien pour consulter l'énoncé"),n(I)])]),l("details",F,[H,l("p",null,[l("a",w,[e("Solution"),n(I)])])]),N,l("p",null,[l("a",S,[e("▶️ ce code"),n(I)]),e(" illustre la POO en Kotlin.")]),k,l("p",null,[e("Ce sont des classes qui implémentent "),l("a",L,[e("des méthodes communes"),n(I)]),e(":")]),z,l("p",null,[e("Cependant, les data class "),l("a",E,[e("ont des restrictions"),n(I)]),e(":")]),j,l("p",null,[l("a",U,[e("▶️ ce code"),n(I)]),e(" illustrate les data class.")]),f,M,l("p",null,[e("La programmation fonctionnelle s'articule autour de "),l("a",K,[e("ces concepts"),n(I)]),e(" : fonctions pures, récursivité, transparence référentielle, variables immuables, fonctions en tant que citoyens de première classe et fonctions d'ordre supérieur.")]),Q,l("ul",null,[D,T,_,l("li",null,[l("a",P,[e("Transparence référentielle"),n(I)]),e(" : signifie qu'une expression peut être remplacée par son résultat sans modifier le comportement du programme.")])]),q,O,l("p",null,[e("Kotlin n'est pas un langage fonctionnel pur mais il prend en charge certaines fonctionnalités. En effet, Kotlin ne sait pas dire si une fonction est pures ou non, mais il fournit des collections immuables via la bibliothèque "),l("a",$,[e("kotlinx.collections.immutable"),n(I)]),e(" pour nous aider à manipuler des données immuables.")]),l("div",ll,[el,l("p",null,[l("a",Il,[e("Une liste en lecture seule"),n(I)]),e(" ne peut pas ajouter ou supprimer des éléments, mais elle peut modifier les données sous-jacentes.")]),nl]),l("p",null,[e("La librairie "),l("a",cl,[e("Arrow-kt"),n(I)]),e(" permet d'aller encore plus loin en développement fonctionnel.")]),il,gl,l("p",null,[l("a",al,[e("▶️ ce code"),n(I)]),e(" montre comment manipuler une liste avec la programmation déclarative.")]),sl,l("ul",null,[bl,tl,ol,ul,l("li",null,[e("Kotlin generates Java records by annotating a data class with "),dl,e(" and targeting JVM 16, among other requirement "),l("a",Gl,[e("listed here"),n(I)]),e(".")]),ml]),l("p",null,[l("a",rl,[e("▶️ this code"),n(I)]),e(" shows how to convert a Kotlin "),pl,e(" to a Java "),Cl,e(".")]),l("p",null,[e("The official documentation provides exhaustive documentation on "),l("a",Zl,[e("Kotlin and JVM integration"),n(I)])]),Bl,Xl,l("p",null,[l("a",hl,[e("Voir l'exercice"),n(I)])]),l("details",Wl,[yl,l("p",null,[l("a",Al,[e("Solution"),n(I)])])]),Vl,l("p",null,[l("a",Yl,[e("Exercice"),n(I)])]),l("details",Jl,[Rl,l("p",null,[l("a",xl,[e("Solution"),n(I)])])]),vl,l("p",null,[l("a",Fl,[e("Exercice"),n(I)])]),l("details",Hl,[wl,l("p",null,[l("a",Nl,[e("Solution"),n(I)])])]),Sl,l("p",null,[l("a",kl,[e("Exercice"),n(I)])]),l("details",Ll,[zl,l("p",null,[l("a",El,[e("Solution"),n(I)])])]),jl,l("p",null,[l("a",Ul,[e("Exercice"),n(I)])]),l("details",fl,[Ml,l("p",null,[l("a",Kl,[e("Solution"),n(I)])])]),Ql,l("ul",null,[l("li",null,[l("a",Dl,[e("Kotlin Koans"),n(I)])]),l("li",null,[l("a",Tl,[e("Referential transparency"),n(I)])])])])}const $l=i(b,[["render",_l],["__file","index.html.vue"]]),le=JSON.parse(`{"path":"/fr/kotlin-features/","title":"📚 Fonctionnalités du langage Kotlin","lang":"fr-FR","frontmatter":{},"headers":[{"level":2,"title":"Caractéristiques de base","slug":"caracteristiques-de-base","link":"#caracteristiques-de-base","children":[{"level":3,"title":"Constructions de base (variables, flux de contrôle)","slug":"constructions-de-base-variables-flux-de-controle","link":"#constructions-de-base-variables-flux-de-controle","children":[]},{"level":3,"title":"Les fonctions","slug":"les-fonctions","link":"#les-fonctions","children":[]},{"level":3,"title":"Null safety","slug":"null-safety","link":"#null-safety","children":[]},{"level":3,"title":"Énumérations","slug":"enumerations","link":"#enumerations","children":[]},{"level":3,"title":"Exercices","slug":"exercices","link":"#exercices","children":[]}]},{"level":2,"title":"Fonctionnalités intermédiaires","slug":"fonctionnalites-intermediaires","link":"#fonctionnalites-intermediaires","children":[{"level":3,"title":"Programmation orientée objet","slug":"programmation-orientee-objet","link":"#programmation-orientee-objet","children":[]},{"level":3,"title":"Data class","slug":"data-class","link":"#data-class","children":[]},{"level":3,"title":"Programmation fonctionnelle","slug":"programmation-fonctionnelle","link":"#programmation-fonctionnelle","children":[]},{"level":3,"title":"Kotlin and Java interoperability","slug":"kotlin-and-java-interoperability","link":"#kotlin-and-java-interoperability","children":[]},{"level":3,"title":"Exercices","slug":"exercices-1","link":"#exercices-1","children":[]}]},{"level":2,"title":"Plus d'exercices et de lecture","slug":"plus-d-exercices-et-de-lecture","link":"#plus-d-exercices-et-de-lecture","children":[]}],"git":{"updatedTime":1696262637000,"contributors":[{"name":"Yassine Benabbas","email":"yassine.benabbas@worldline.com","commits":5},{"name":"A187839","email":"ibrahim.gharbi@worldline.com","commits":1},{"name":"Ibrahim Gharbi","email":"brah.gharbi@gmail.com","commits":1}]},"filePathRelative":"fr/kotlin-features/README.md"}`);export{$l as comp,le as data};
diff --git a/assets/kmp-compose-desktop-CzocCLNL.png b/assets/kmp-compose-desktop-CzocCLNL.png
new file mode 100644
index 00000000..acef82a2
Binary files /dev/null and b/assets/kmp-compose-desktop-CzocCLNL.png differ
diff --git a/assets/kmp_codelab-CiTPMWjt.js b/assets/kmp_codelab-CiTPMWjt.js
new file mode 100644
index 00000000..69a29a44
--- /dev/null
+++ b/assets/kmp_codelab-CiTPMWjt.js
@@ -0,0 +1 @@
+const s="/learning-kotlin/assets/kmp_codelab-J5AI0da_.png";export{s as _};
diff --git a/assets/kmp_codelab-J5AI0da_.png b/assets/kmp_codelab-J5AI0da_.png
new file mode 100644
index 00000000..9ebc2b8b
Binary files /dev/null and b/assets/kmp_codelab-J5AI0da_.png differ
diff --git a/assets/kotlin-decision-tree-4i7nEr1Z.svg b/assets/kotlin-decision-tree-4i7nEr1Z.svg
new file mode 100644
index 00000000..d3375a77
--- /dev/null
+++ b/assets/kotlin-decision-tree-4i7nEr1Z.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/kotlin-used-for-Bdlavnqs.js b/assets/kotlin-used-for-Bdlavnqs.js
new file mode 100644
index 00000000..c91b22fd
--- /dev/null
+++ b/assets/kotlin-used-for-Bdlavnqs.js
@@ -0,0 +1 @@
+const s="/learning-kotlin/assets/kotlin-used-for-FU2DHhYT.png";export{s as _};
diff --git a/assets/kotlin-used-for-FU2DHhYT.png b/assets/kotlin-used-for-FU2DHhYT.png
new file mode 100644
index 00000000..d0232536
Binary files /dev/null and b/assets/kotlin-used-for-FU2DHhYT.png differ
diff --git a/assets/kotlin-wasm-flag-BKaaN9Pq.png b/assets/kotlin-wasm-flag-BKaaN9Pq.png
new file mode 100644
index 00000000..c51b253b
Binary files /dev/null and b/assets/kotlin-wasm-flag-BKaaN9Pq.png differ
diff --git a/assets/kotlin-wasm-webapp-R4_9ho9v.js b/assets/kotlin-wasm-webapp-R4_9ho9v.js
new file mode 100644
index 00000000..d4ed5a84
--- /dev/null
+++ b/assets/kotlin-wasm-webapp-R4_9ho9v.js
@@ -0,0 +1 @@
+const s="/learning-kotlin/assets/kotlin-wasm-webapp-ViYyGRus.png";export{s as _};
diff --git a/assets/kotlin-wasm-webapp-ViYyGRus.png b/assets/kotlin-wasm-webapp-ViYyGRus.png
new file mode 100644
index 00000000..0910b9d7
Binary files /dev/null and b/assets/kotlin-wasm-webapp-ViYyGRus.png differ
diff --git a/assets/launch-android-app-BS4WBFFP.png b/assets/launch-android-app-BS4WBFFP.png
new file mode 100644
index 00000000..0deef90e
Binary files /dev/null and b/assets/launch-android-app-BS4WBFFP.png differ
diff --git a/assets/logo_worldline-dinT9MYm.js b/assets/logo_worldline-dinT9MYm.js
new file mode 100644
index 00000000..bba4f9b6
--- /dev/null
+++ b/assets/logo_worldline-dinT9MYm.js
@@ -0,0 +1 @@
+const o="/learning-kotlin/assets/logo_worldline-t5KadDQv.png";export{o as _};
diff --git a/assets/logo_worldline-t5KadDQv.png b/assets/logo_worldline-t5KadDQv.png
new file mode 100644
index 00000000..6981642d
Binary files /dev/null and b/assets/logo_worldline-t5KadDQv.png differ
diff --git a/assets/qrcode-devoxxma23-Beeff8NO.gif b/assets/qrcode-devoxxma23-Beeff8NO.gif
new file mode 100644
index 00000000..dc77dac3
Binary files /dev/null and b/assets/qrcode-devoxxma23-Beeff8NO.gif differ
diff --git a/assets/qrcode-mds2023-tbApIRH6.gif b/assets/qrcode-mds2023-tbApIRH6.gif
new file mode 100644
index 00000000..accc55d1
Binary files /dev/null and b/assets/qrcode-mds2023-tbApIRH6.gif differ
diff --git a/assets/qrcode-mixtit24-D6tnJWnS.js b/assets/qrcode-mixtit24-D6tnJWnS.js
new file mode 100644
index 00000000..fb6a7d48
--- /dev/null
+++ b/assets/qrcode-mixtit24-D6tnJWnS.js
@@ -0,0 +1 @@
+const A="/learning-kotlin/assets/Kotlin-Beyond-Android-B6J4ER4N.png",g="/learning-kotlin/assets/Androidmakers2023Kotlinshortlink-7XGfMd7N.svg",s="/learning-kotlin/assets/qrcode-mds2023-tbApIRH6.gif",o="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAACH1JREFUeF7tndF2ozAMRNv//+juycLZQ8HR5UYmSbfTVxtZHo1GMhD6+fHx8fXxpL+vr8eX+vz8VF7SWlt7+7n7ta4cJ9tq0w9MvqH6eFTkghSUylwIIME+OT0EuGXATpl+rQJ0MnREOAJyew1lOPlm1rqtW61Ha+332vW9wsH6Qom/9/WbAly9WGW/C2IIQKFfxkOAFacowEqIbRMYBVhAsTh01ettSwBtbC86tpk6J1rjWVbyq7pNAbc40L5MKSRM7VqqBNiNk7MENG1mZqZU9wFsk2f8JoUhYs+OSdkEzl4sBOASEwKcTCcCisxEAU40gVcrgAnCbFneqhGRaTYO273Ysjnbl5eWgBCgfxeSlI4IFgIMbo4QaF3QowCDmzG2QbRSWB1ZUwJWdK5mvrFvCdHpEYxfo+wnX4lg5nhriU97e5sSQDeVSHZDgDFCIcAJ5hBIZCIKcLKEGKAJVAqKkUrj168qAQQyjZvaZ211S8bMIygRyJB5JmYjTNWzAAoKjc/cDGW0Afnmdwhw4k4gBZjGQ4AFIUPOmZhFAQqGRgEGCkAZ3R2vaqWto5QpZvyd1+5iTte/9K3gKgtNAEcya64PAYgmk8ajAKvsbn7kQuSbBP1dM1GA3/67gC/Tol5Nx439K499tjOfve1OAzrdlxBgNqRsLwRgjMpf7jySwdQUnnBp2pQQ4ASUKQEnQJowpWwCbXtAQdv723kvj7pnM07qQOP7fRkcZmOs7VU/D9fGGr/hJ5CvHO/aDgFWBAzz93W8G4TO9Z1rz9xvr5R6dpJpe9UpgAJqZNYCRRuhoHWykkqr9a0qfbRWNwa09mcIQCE4jocAKyZRgDF5bNZWFLS29PwoQBTg30eiLHsIOrK3vX62mpC9VzZmHVyot6F9H8a3CkCOUe0j5wzo3SaPgDC+dIl+pS+EE46HAL7Js0QPASiFHngkS0G4EnRaG49e4oYZqS5m+G6tAy6dbwTZklE5Sxsh0C1Qnf6DfLW4bH2xtonoiEsI0P+JNgWBFCEEOPET7SjAgkBXIQ44RgGiAE/7WHQlhZbZJKvGHkm4Haeed+sb2aZ9mrVGc5/6UmgIcJTxEGBlhcnYEZMJyGq8c+3NF+q0q/6F1o4C3CEIAWMIRUGw40aWyTbt06w1LAGdt4IJZOOczSJjmxSDzu20Fvle2Q8BVnQJRAoCHRNN/2HXIt9DgDuIVt2wDUIIMEaM1K18I4iCkBKwIPTfKAAFlMYpCzudOJHR1tLKHgXU7pPmb8dpH7N9+6YAFGAap42GAAtCnZ4gBLiTupQ5pCBVFtK13cSIApx4H4CCEAKcawLL9wEsyB3Jr45lJJPdxovWJhzsOHXixh6VAFKj/bh6FjB7cSN9NmgGdNqXCdBorvGF1iJfQ4AVQQM6gUpBoXHjC9kiX0OAEOAbh6g3Uv851LKrw2bKGtoYjVe+zc4yc/ylvqpbCssmcPbGQ4AFgRDgDhMqwkUBFtAsDqQgBzJ2HgdThl85boExd98IRJJh69vWnr22W5ZbD4OuDDDZ7gJVHUFDAEL/DcZDgHGJsM1vFKD5OJeavFGuvFPv0/pKmK0/dMqohKW71swegPZh1cnsm/oPEmd1K9hulOSH7Bkg7FohwLhkRAEeKAFE5CjAmsok2yRX1fEoCjC+yUSYHkrAlR+IIGdMgOloZgnR8a1bh02ps2vZpCtfCbOLG1BtQO18C8SVQTG4dMuH3XcIcCI63aCcWOLflO5aIcAT+o8rlfGlBJgts2YzZu7NT5pvso4CqrMKvstjeh/apz2RqIdBtHE7XgFtN0rzQ4DxuT8EOMGM7omCEiMKsCKwBZoymoJyIq6np9BaXdl9KwJU3wiioBCiBGR1PYFM/Yrxze6zs6+bX2Zvs31Tr4TZxW0zFQIQTX2zS+QMAR4oPzOJHQVg0v+dYWTykWOh6T9CgDtBoyB1SghJ2eygbO3ZfZGvNF6tTSeK9ninCbRAnUz+oQIQmQzI5IfdF61N4yHAICIGtFHJIMJ0GtB21hVfC6d9Tx+PAhypEAW4kx6vZD7V/M59gW7AqaR0fLO2NU7mY9EhwAIvEYaC1ilPZDsEWBEwIFNAifg2KMY3azsECAG+cQDJXf3zaMs+26Fa+9v53azcXk8gUQ3vXt/BgdYmnNQnYsjREIAQWsZnloAQYMWcgKiyuHPtI03hWxHglT8Pr2QYpQv+9Rqp0ZYQRAAbMGOP5lL5Ic0hHF7649AQwB8pu2TcXx8CnDjXd0G/svxEAe4gQNKXErA2pDOPgR02dmshXV/1FJThhkyjprCyT7ZpnG78YC8VAvCxbHYQKvXBgDWb38NeQoAQ4Gn/OLLKJJJwOg7R9SkBC4KlAhCIVONtkDrHQOuL6cRJhu3apgcg29Sv0PUHHMzjYG2c/nf9Zpzq7JXkvHrtEODO7doowPU3gihp1ceiyVhKwBihH6sAtt50pLRbd8nXqoTQ2jTeIb5NKjr3a186L4WSMwY4M3cEWghwTn3UKYBADQEeBB1u5hhVoOaYEqvsAUKAJRQEopbdEODIcQLZjpsssnM7vc6IUNv1bUaTCtPefowChABjNQoBVgRsuaLMqMajAHfQ6QBjM5zW6gSYrqW1OzLeufbmN11/6Fd+yjHQEsQGcTuf1MSCTE1ipwewvuz39mt7AHNjqFtnQ4AVAQO6lVnKWhNEsmWzLgQIAb5xoPNsgEohlT5VAsgYjZOzzwSCfKn20lUEszbNJV+M0v29J/Eu7wNox8X3eEc3X4yME+hki4JaNaBUCikJ0bcQgCD07wxSzZ/ZC5H3IcCdG0UEzMxj4Y9RAGJTd7ySs67UdX0zZ3Fai0pGdT3hMH38XV8L74BIAaJxow4jWx3fpweY3ssMAY4hDAEoRR4cTwlg4J6tAH8AK9x+9UmOIe8AAAAASUVORK5CYII=";export{g as _,s as a,A as b,o as c};
diff --git a/assets/style-DvQwtSJL.css b/assets/style-DvQwtSJL.css
new file mode 100644
index 00000000..92566f14
--- /dev/null
+++ b/assets/style-DvQwtSJL.css
@@ -0,0 +1 @@
+.vp-back-to-top-button{position:fixed!important;bottom:4rem;inset-inline-end:1rem;z-index:100;width:48px;height:48px;padding:8px;border-width:0;border-radius:50%;background:var(--back-to-top-bg-color);color:var(--back-to-top-color);box-shadow:2px 2px 10px 4px var(--back-to-top-shadow);cursor:pointer}@media (max-width: 959px){.vp-back-to-top-button{transform:scale(.8);transform-origin:100% 100%}}@media print{.vp-back-to-top-button{display:none}}.vp-back-to-top-button:hover{color:var(--back-to-top-color-hover)}.vp-back-to-top-button .back-to-top-icon{overflow:hidden;width:100%;height:100%;background:currentcolor;border-radius:50%;-webkit-mask-image:var(--back-to-top-icon);mask-image:var(--back-to-top-icon);-webkit-mask-position:50%;mask-position:50%;-webkit-mask-size:cover;mask-size:cover}.vp-scroll-progress{position:absolute;right:-2px;bottom:-2px;width:52px;height:52px}.vp-scroll-progress svg{width:100%;height:100%}.vp-scroll-progress circle{opacity:.9;transform:rotate(-90deg);transform-origin:50% 50%}.back-to-top-enter-active,.back-to-top-leave-active{transition:opacity .3s}.back-to-top-enter-from,.back-to-top-leave-to{opacity:0}:root{--back-to-top-z-index: 5;--back-to-top-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201024%201024'%3e%3cpath%20d='M512%20843.2c-36.2%200-66.4-13.6-85.8-21.8-10.8-4.6-22.6%203.6-21.8%2015.2l7%20102c.4%206.2%207.6%209.4%2012.6%205.6l29-22c3.6-2.8%209-1.8%2011.4%202l41%2064.2c3%204.8%2010.2%204.8%2013.2%200l41-64.2c2.4-3.8%207.8-4.8%2011.4-2l29%2022c5%203.8%2012.2.6%2012.6-5.6l7-102c.8-11.6-11-20-21.8-15.2-19.6%208.2-49.6%2021.8-85.8%2021.8'/%3e%3cpath%20d='m795.4%20586.2-96-98.2C699.4%20172%20513%2032%20513%2032S324.8%20172%20324.8%20488l-96%2098.2c-3.6%203.6-5.2%209-4.4%2014.2L261.2%20824c1.8%2011.4%2014.2%2017%2023.6%2010.8L419%20744s41.4%2040%2094.2%2040%2092.2-40%2092.2-40l134.2%2090.8c9.2%206.2%2021.6.6%2023.6-10.8l37-223.8c.4-5.2-1.2-10.4-4.8-14M513%20384c-34%200-61.4-28.6-61.4-64s27.6-64%2061.4-64c34%200%2061.4%2028.6%2061.4%2064S547%20384%20513%20384'/%3e%3c/svg%3e");--back-to-top-bg-color: #fff;--back-to-top-color: #3eaf7c;--back-to-top-color-hover: #71cda3;--back-to-top-shadow: rgb(0 0 0 / 20%)}div[class*=language-]:hover:before{display:none}div[class*=language-]:hover .vp-copy-code-button{opacity:1}.vp-copy-code-button{position:absolute;top:.5em;right:.5em;z-index:5;width:2.5rem;height:2.5rem;padding:0;border-width:0;border-radius:.5rem;background:transparent;outline:none;opacity:0;cursor:pointer;transition:opacity .4s}@media print{.vp-copy-code-button{display:none}}.vp-copy-code-button:focus,.vp-copy-code-button.copied{opacity:1}.vp-copy-code-button:hover,.vp-copy-code-button.copied{background:var(--copy-code-hover)}.vp-copy-code-button.copied .vp-copy-icon{-webkit-mask-image:var(--code-copied-icon);mask-image:var(--code-copied-icon)}.vp-copy-code-button.copied:after{content:attr(data-copied);position:absolute;top:0;right:calc(100% + .25rem);display:block;height:1.25rem;padding:.625rem;border-radius:.5rem;background:var(--copy-code-hover);color:var(--copy-code-color);font-weight:500;line-height:1.25rem;white-space:nowrap}.vp-copy-icon{width:1.25rem;height:1.25rem;padding:.625rem;background:currentcolor;color:var(--copy-code-color);font-size:1.25rem;-webkit-mask-image:var(--code-copy-icon);mask-image:var(--code-copy-icon);-webkit-mask-position:50%;mask-position:50%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:1em;mask-size:1em}:root{--code-copy-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20height='20'%20width='20'%20stroke='rgba(128,128,128,1)'%20stroke-width='2'%3e%3cpath%20stroke-linecap='round'%20stroke-linejoin='round'%20d='M9%205H7a2%202%200%200%200-2%202v12a2%202%200%200%200%202%202h10a2%202%200%200%200%202-2V7a2%202%200%200%200-2-2h-2M9%205a2%202%200%200%200%202%202h2a2%202%200%200%200%202-2M9%205a2%202%200%200%201%202-2h2a2%202%200%200%201%202%202'%20/%3e%3c/svg%3e");--code-copied-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20height='20'%20width='20'%20stroke='rgba(128,128,128,1)'%20stroke-width='2'%3e%3cpath%20stroke-linecap='round'%20stroke-linejoin='round'%20d='M9%205H7a2%202%200%200%200-2%202v12a2%202%200%200%200%202%202h10a2%202%200%200%200%202-2V7a2%202%200%200%200-2-2h-2M9%205a2%202%200%200%200%202%202h2a2%202%200%200%200%202-2M9%205a2%202%200%200%201%202-2h2a2%202%200%200%201%202%202m-6%209%202%202%204-4'%20/%3e%3c/svg%3e");--copy-code-color: #9e9e9e;--copy-code-hover: rgb(0 0 0 / 50%)}:root{--external-link-icon-color: #aaa}.external-link-icon{position:relative;display:inline-block;color:var(--external-link-icon-color);vertical-align:middle;top:-1px}@media print{.external-link-icon{display:none}}.external-link-icon-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}:root{--nprogress-color: #29d;--nprogress-z-index: 1031}#nprogress{pointer-events:none}#nprogress .bar{background:var(--nprogress-color);position:fixed;z-index:var(--nprogress-z-index);top:0;left:0;width:100%;height:2px}.vp-page-meta{max-width:var(--content-width);margin:0 auto;padding:.75rem 2.5rem;display:flex;flex-wrap:wrap;justify-content:space-between;overflow:auto}@media (max-width: 959px){.vp-page-meta{padding:2rem}}@media (max-width: 419px){.vp-page-meta{padding:1.5rem}}@media print{.vp-page-meta{margin:0!important;padding-inline:0!important}}@media (max-width: 719px){.vp-page-meta{display:block}}.vp-page-meta .vp-meta-item{flex-grow:1}.vp-page-meta .vp-meta-item .vp-meta-label{font-weight:500}.vp-page-meta .vp-meta-item .vp-meta-label:not(a){color:var(--c-text-lighter)}.vp-page-meta .vp-meta-item .vp-meta-info{color:var(--c-text-quote);font-weight:400}.vp-page-meta .git-info{text-align:end}.vp-page-meta .edit-link{margin-top:.25rem;margin-bottom:.25rem;margin-inline-end:.5rem;font-size:14px}@media print{.vp-page-meta .edit-link{display:none}}.vp-page-meta .edit-link .icon{position:relative;bottom:-.125em;width:1em;height:1em;margin-inline-end:.25em}.vp-page-meta .last-updated,.vp-page-meta .contributors{margin-top:.25rem;margin-bottom:.25rem;font-size:14px}@media (max-width: 719px){.vp-page-meta .last-updated,.vp-page-meta .contributors{font-size:13px;text-align:start}}.vp-page-nav{display:flex;flex-wrap:wrap;max-width:var(--content-width, 740px);min-height:2rem;margin-inline:auto;margin-top:0;padding-block:.5rem;padding-inline:2rem;border-top:1px solid var(--c-border);transition:border-top var(--t-color);padding-top:1rem;padding-bottom:0}@media (max-width: 959px){.vp-page-nav{padding-inline:1rem}}@media print{.vp-page-nav{display:none}}.vp-page-nav .route-link{display:inline-block;flex-grow:1;margin:.25rem;padding:.25rem .5rem;border:1px solid var(--c-border);border-radius:.25rem}.vp-page-nav .route-link:hover{background:var(--c-bg-light)}.vp-page-nav .route-link .hint{color:var(--c-text-quote);font-size:.875rem;line-height:2}.vp-page-nav .prev{text-align:start}.vp-page-nav .next{text-align:end}:root{--c-brand: #3eaf7c;--c-brand-light: #4abf8a;--c-bg: #ffffff;--c-bg-light: #f3f4f5;--c-bg-lighter: #eeeeee;--c-bg-dark: #ebebec;--c-bg-darker: #e6e6e6;--c-bg-navbar: var(--c-bg);--c-bg-sidebar: var(--c-bg);--c-bg-arrow: #cccccc;--c-text: #2c3e50;--c-text-accent: var(--c-brand);--c-text-light: #3a5169;--c-text-lighter: #4e6e8e;--c-text-lightest: #6a8bad;--c-text-quote: #999999;--c-border: #eaecef;--c-border-dark: #dfe2e5;--c-tip: #42b983;--c-tip-bg: var(--c-bg-light);--c-tip-title: var(--c-text);--c-tip-text: var(--c-text);--c-tip-text-accent: var(--c-text-accent);--c-warning: #ffc310;--c-warning-bg: #fffae3;--c-warning-bg-light: #fff3ba;--c-warning-bg-lighter: #fff0b0;--c-warning-border-dark: #f7dc91;--c-warning-details-bg: #fff5ca;--c-warning-title: #f1b300;--c-warning-text: #746000;--c-warning-text-accent: #edb100;--c-warning-text-light: #c1971c;--c-warning-text-quote: #ccab49;--c-danger: #f11e37;--c-danger-bg: #ffe0e0;--c-danger-bg-light: #ffcfde;--c-danger-bg-lighter: #ffc9c9;--c-danger-border-dark: #f1abab;--c-danger-details-bg: #ffd4d4;--c-danger-title: #ed1e2c;--c-danger-text: #660000;--c-danger-text-accent: #bd1a1a;--c-danger-text-light: #b5474d;--c-danger-text-quote: #c15b5b;--c-details-bg: #eeeeee;--c-badge-tip: var(--c-tip);--c-badge-warning: #ecc808;--c-badge-warning-text: var(--c-bg);--c-badge-danger: #dc2626;--c-badge-danger-text: var(--c-bg);--c-code-group-tab-title: rgba(255, 255, 255, .9);--c-code-group-tab-bg: var(--code-bg-color);--c-code-group-tab-outline: var(var(--c-code-group-tab-title));--c-code-group-tab-active-border: var(--c-brand);--t-color: .3s ease;--t-transform: .3s ease;--code-bg-color: #282c34;--code-hl-bg-color: rgba(0, 0, 0, .66);--code-ln-color: #9e9e9e;--code-ln-wrapper-width: 3.5rem;--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;--font-family-code: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;--navbar-height: 3.6rem;--navbar-padding-v: .7rem;--navbar-padding-h: 1.5rem;--sidebar-width: 20rem;--sidebar-width-mobile: calc(var(--sidebar-width) * .82);--content-width: 740px;--homepage-width: 960px}.vp-back-to-top-button{--back-to-top-color: var(--c-brand);--back-to-top-color-hover: var(--c-brand-light);--back-to-top-bg-color: var(--c-bg)}.vp-catalog-wrapper{--catalog-bg-color: var(--c-bg);--catalog-bg-secondary-color: var(--c-bg-dark);--catalog-border-color: var(--c-border);--catalog-active-color: var(--c-brand);--catalog-hover-color: var(--c-brand-light)}.waline-wrapper{--waline-bg-color: var(--c-bg);--waline-bg-color-light: var(--c-bg-light);--waline-text-color: var(--c-color);--waline-border: 1px solid var(--c-border);--waline-border-color: var(--c-border);--waline-theme-color: var(--c-brand);--waline-active-color: var(--c-brand-light)}.DocSearch{--docsearch-primary-color: var(--c-brand);--docsearch-text-color: var(--c-text);--docsearch-highlight-color: var(--c-brand);--docsearch-muted-color: var(--c-text-quote);--docsearch-container-background: rgba(9, 10, 17, .8);--docsearch-modal-background: var(--c-bg-light);--docsearch-searchbox-background: var(--c-bg-lighter);--docsearch-searchbox-focus-background: var(--c-bg);--docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);--docsearch-hit-color: var(--c-text-light);--docsearch-hit-active-color: var(--c-bg);--docsearch-hit-background: var(--c-bg);--docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);--docsearch-footer-background: var(--c-bg)}.external-link-icon{--external-link-icon-color: var(--c-text-quote)}.medium-zoom-overlay{--medium-zoom-bg-color: var(--c-bg)}#nprogress{--nprogress-color: var(--c-brand)}body{--photo-swipe-bullet: var(--c-bg);--photo-swipe-bullet-active: var(--c-brand)}body{--pwa-text-color: var(--c-text);--pwa-bg-color: var(--c-bg);--pwa-border-color: var(--c-brand);--pwa-btn-text-color: var(--c-bg);--pwa-btn-bg-color: var(--c-brand);--pwa-btn-hover-bg-color: var(--c-brand-light)}.redirect-modal-mask{--redirect-bg-color: var(--c-bg);--redirect-bg-color-light: var(--c-bg-light);--redirect-bg-color-lighter: var(--c-bg-lighter);--redirect-text-color: var(--c-text);--redirect-primary-color: var(--c-brand);--redirect-primary-hover-color: var(--c-brand-light);--redirect-primary-text-color: var(--c-bg)}.search-box{--search-bg-color: var(--c-bg);--search-accent-color: var(--c-brand);--search-text-color: var(--c-text);--search-border-color: var(--c-border);--search-item-text-color: var(--c-text-lighter);--search-item-focus-bg-color: var(--c-bg-light)}html.dark{--c-brand: #3aa675;--c-brand-light: #349469;--c-bg: #22272e;--c-bg-light: #2b313a;--c-bg-lighter: #262c34;--c-bg-dark: #343b44;--c-bg-darker: #37404c;--c-text: #adbac7;--c-text-light: #96a7b7;--c-text-lighter: #8b9eb0;--c-text-lightest: #8094a8;--c-border: #3e4c5a;--c-border-dark: #34404c;--c-tip: #318a62;--c-warning: #e0ad15;--c-warning-bg: #2d2f2d;--c-warning-bg-light: #423e2a;--c-warning-bg-lighter: #44442f;--c-warning-border-dark: #957c35;--c-warning-details-bg: #39392d;--c-warning-title: #fdca31;--c-warning-text: #d8d96d;--c-warning-text-accent: #ffbf00;--c-warning-text-light: #ddb84b;--c-warning-text-quote: #ccab49;--c-danger: #fc1e38;--c-danger-bg: #39232c;--c-danger-bg-light: #4b2b35;--c-danger-bg-lighter: #553040;--c-danger-border-dark: #a25151;--c-danger-details-bg: #482936;--c-danger-title: #fc2d3b;--c-danger-text: #ea9ca0;--c-danger-text-accent: #fd3636;--c-danger-text-light: #d9777c;--c-danger-text-quote: #d56b6b;--c-details-bg: #323843;--c-badge-warning: var(--c-warning);--c-badge-warning-text: #3c2e05;--c-badge-danger: var(--c-danger);--c-badge-danger-text: #401416;--code-hl-bg-color: #363b46}html.dark .DocSearch{--docsearch-logo-color: var(--c-text);--docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;--docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, .3);--docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);--docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, .5), 0 -4px 8px 0 rgba(0, 0, 0, .2)}html.dark body{--pwa-shadow-color: rgb(0 0 0 / 30%);--pwa-content-color: #ccc;--pwa-content-light-color: #999}html,body{padding:0;margin:0;background-color:var(--c-bg);transition:background-color var(--t-color)}html.dark{color-scheme:dark}html{font-size:16px}body{font-family:var(--font-family);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:1rem;color:var(--c-text)}a{font-weight:500;color:var(--c-text-accent);text-decoration:none;overflow-wrap:break-word}p a code{font-weight:400;color:var(--c-text-accent)}kbd{font-family:var(--font-family-code);color:var(--c-text);background:var(--c-bg-lighter);border:solid .15rem var(--c-border-dark);border-bottom:solid .25rem var(--c-border-dark);border-radius:.15rem;padding:0 .15em}code{font-family:var(--font-family-code);color:var(--c-text-lighter);padding:.25rem .5rem;margin:0;font-size:.85em;background-color:var(--c-bg-light);border-radius:3px;overflow-wrap:break-word;transition:background-color var(--t-color)}blockquote{font-size:1rem;color:var(--c-text-quote);border-left:.2rem solid var(--c-border-dark);margin:1rem 0;padding:.25rem 0 .25rem 1rem;overflow-wrap:break-word}blockquote>p{margin:0}ul,ol{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25;overflow-wrap:break-word}h1:focus-visible,h2:focus-visible,h3:focus-visible,h4:focus-visible,h5:focus-visible,h6:focus-visible{outline:none}h1 .header-anchor,h2 .header-anchor,h3 .header-anchor,h4 .header-anchor,h5 .header-anchor,h6 .header-anchor{color:inherit;text-decoration:none;position:relative}h1 .header-anchor:hover:before,h2 .header-anchor:hover:before,h3 .header-anchor:hover:before,h4 .header-anchor:hover:before,h5 .header-anchor:hover:before,h6 .header-anchor:hover:before{font-size:.8em;content:"¶";position:absolute;left:-.75em;color:var(--c-brand)}h1 .header-anchor:focus-visible,h2 .header-anchor:focus-visible,h3 .header-anchor:focus-visible,h4 .header-anchor:focus-visible,h5 .header-anchor:focus-visible,h6 .header-anchor:focus-visible{outline:none}h1 .header-anchor:focus-visible:before,h2 .header-anchor:focus-visible:before,h3 .header-anchor:focus-visible:before,h4 .header-anchor:focus-visible:before,h5 .header-anchor:focus-visible:before,h6 .header-anchor:focus-visible:before{content:"¶";position:absolute;left:-.75em;color:var(--c-brand);outline:auto}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color)}h3{font-size:1.35rem}h4{font-size:1.15rem}h5{font-size:1.05rem}h6{font-size:1rem}@media print{a[href^="http://"]:after,a[href^="https://"]:after{content:" (" attr(href) ") "}}p,ul,ol{line-height:1.7;overflow-wrap:break-word}hr{border:0;border-top:1px solid var(--c-border)}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto;transition:border-color var(--t-color)}tr{border-top:1px solid var(--c-border-dark);transition:border-color var(--t-color)}tr:nth-child(2n){background-color:var(--c-bg-light);transition:background-color var(--t-color)}tr:nth-child(2n) code{background-color:var(--c-bg-dark)}th,td{padding:.6em 1em;border:1px solid var(--c-border-dark);transition:border-color var(--t-color)}.arrow{display:inline-block;vertical-align:middle;width:1em;height:1em;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(0,0,0,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E");background-position:center;background-repeat:no-repeat;line-height:normal;transition:all .3s}html.dark .arrow{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(255,255,255,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E")}.arrow.down{transform:rotate(180deg)}.arrow.right{transform:rotate(90deg)}.arrow.left{transform:rotate(-90deg)}.badge{display:inline-block;font-size:14px;font-weight:600;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:var(--c-bg);vertical-align:top;transition:color var(--t-color),background-color var(--t-color)}.badge.tip{background-color:var(--c-badge-tip)}.badge.warning{background-color:var(--c-badge-warning);color:var(--c-badge-warning-text)}.badge.danger{background-color:var(--c-badge-danger);color:var(--c-badge-danger-text)}.badge+.badge{margin-left:5px}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:var(--font-family-code);font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata{color:#999}.token.punctuation{color:#ccc}.token.tag,.token.attr-name,.token.namespace,.token.deleted{color:#ec5975}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:#3eaf7c}.theme-default-content pre,.theme-default-content pre[class*=language-]{line-height:1.375;padding:1.3rem 1.5rem;margin:.85rem 0;border-radius:6px;overflow:auto}.theme-default-content pre code,.theme-default-content pre[class*=language-] code{color:#fff;padding:0;background-color:transparent!important;border-radius:0;overflow-wrap:unset;-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.theme-default-content .line-number{font-family:var(--font-family-code)}div[class*=language-]{position:relative;background-color:var(--code-bg-color);border-radius:6px}div[class*=language-]:before{content:attr(data-title);position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:var(--code-ln-color)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent!important;position:relative;z-index:1}div[class*=language-] .highlight-lines{-webkit-user-select:none;-moz-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.375}div[class*=language-] .highlight-lines .highlight-line{background-color:var(--code-hl-bg-color)}div[class*=language-]:not(.line-numbers-mode) .line-numbers{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line:before{content:" ";position:absolute;z-index:2;left:0;top:0;display:block;width:var(--code-ln-wrapper-width);height:100%}div[class*=language-].line-numbers-mode pre{margin-left:var(--code-ln-wrapper-width);padding-left:1rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers{position:absolute;top:0;width:var(--code-ln-wrapper-width);text-align:center;color:var(--code-ln-color);padding-top:1.25rem;line-height:1.375;counter-reset:line-number}div[class*=language-].line-numbers-mode .line-numbers .line-number{position:relative;z-index:3;-webkit-user-select:none;-moz-user-select:none;user-select:none;height:1.375em}div[class*=language-].line-numbers-mode .line-numbers .line-number:before{counter-increment:line-number;content:counter(line-number);font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;top:0;left:0;width:var(--code-ln-wrapper-width);height:100%;border-radius:6px 0 0 6px;border-right:1px solid var(--code-hl-bg-color)}@media (max-width: 419px){.theme-default-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.code-group__nav{margin-top:.85rem;margin-bottom:calc(-1.7rem - 6px);padding-bottom:calc(1.7rem - 6px);padding-left:10px;padding-top:10px;border-top-left-radius:6px;border-top-right-radius:6px;background-color:var(--c-code-group-tab-bg)}.code-group__nav-tab{border:0;padding:5px;cursor:pointer;background-color:transparent;font-size:.85em;line-height:1.4;color:var(--c-code-group-tab-title);font-weight:600}.code-group__nav-tab:focus{outline:none}.code-group__nav-tab:focus-visible{outline:1px solid var(--c-code-group-tab-outline)}.code-group__nav-tab-active{border-bottom:var(--c-code-group-tab-active-border) 1px solid}@media (max-width: 419px){.code-group__nav{margin-left:-1.5rem;margin-right:-1.5rem;border-radius:0}}.code-group-item{display:none}.code-group-item__active{display:block}.code-group-item>pre{background-color:orange}.custom-container{transition:color var(--t-color),border-color var(--t-color),background-color var(--t-color)}.custom-container .custom-container-title{font-weight:600}.custom-container .custom-container-title:not(:only-child){margin-bottom:-.4rem}.custom-container.tip,.custom-container.warning,.custom-container.danger{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-container.tip{border-color:var(--c-tip);background-color:var(--c-tip-bg);color:var(--c-tip-text)}.custom-container.tip .custom-container-title{color:var(--c-tip-title)}.custom-container.tip a{color:var(--c-tip-text-accent)}.custom-container.tip code{background-color:var(--c-bg-dark)}.custom-container.warning{border-color:var(--c-warning);background-color:var(--c-warning-bg);color:var(--c-warning-text)}.custom-container.warning .custom-container-title{color:var(--c-warning-title)}.custom-container.warning a{color:var(--c-warning-text-accent)}.custom-container.warning blockquote{border-left-color:var(--c-warning-border-dark);color:var(--c-warning-text-quote)}.custom-container.warning code{color:var(--c-warning-text-light);background-color:var(--c-warning-bg-light)}.custom-container.warning details{background-color:var(--c-warning-details-bg)}.custom-container.warning details code{background-color:var(--c-warning-bg-lighter)}.custom-container.warning .external-link-icon{--external-link-icon-color: var(--c-warning-text-quote)}.custom-container.danger{border-color:var(--c-danger);background-color:var(--c-danger-bg);color:var(--c-danger-text)}.custom-container.danger .custom-container-title{color:var(--c-danger-title)}.custom-container.danger a{color:var(--c-danger-text-accent)}.custom-container.danger blockquote{border-left-color:var(--c-danger-border-dark);color:var(--c-danger-text-quote)}.custom-container.danger code{color:var(--c-danger-text-light);background-color:var(--c-danger-bg-light)}.custom-container.danger details{background-color:var(--c-danger-details-bg)}.custom-container.danger details code{background-color:var(--c-danger-bg-lighter)}.custom-container.danger .external-link-icon{--external-link-icon-color: var(--c-danger-text-quote)}.custom-container.details{display:block;position:relative;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:var(--c-details-bg)}.custom-container.details code{background-color:var(--c-bg-darker)}.custom-container.details h4{margin-top:0}.custom-container.details figure:last-child,.custom-container.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-container.details summary{outline:none;cursor:pointer}.home{padding:var(--navbar-height) 2rem 0;max-width:var(--homepage-width);margin:0 auto;display:block}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.8rem auto}.home .hero .actions{display:flex;flex-wrap:wrap;gap:1rem;justify-content:center}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:var(--c-text-lightest)}.home .hero .action-button{display:inline-block;font-size:1.2rem;padding:.8rem 1.6rem;border-width:2px;border-style:solid;border-radius:4px;transition:background-color var(--t-color);box-sizing:border-box}.home .hero .action-button.primary{color:var(--c-bg);background-color:var(--c-brand);border-color:var(--c-brand)}.home .hero .action-button.primary:hover{background-color:var(--c-brand-light)}.home .hero .action-button.secondary{color:var(--c-brand);background-color:var(--c-bg);border-color:var(--c-brand)}.home .hero .action-button.secondary:hover{color:var(--c-bg);background-color:var(--c-brand-light)}.home .features{border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:var(--c-text-light)}.home .feature p{color:var(--c-text-lighter)}.home .theme-default-content{padding:0;margin:0}.home .footer{padding:2.5rem;border-top:1px solid var(--c-border);text-align:center;color:var(--c-text-lighter);transition:border-color var(--t-color)}@media (max-width: 719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width: 419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.page{padding-top:var(--navbar-height);padding-left:var(--sidebar-width)}.navbar{position:fixed;z-index:20;top:0;left:0;right:0;height:var(--navbar-height);box-sizing:border-box;border-bottom:1px solid var(--c-border);background-color:var(--c-bg-navbar);transition:background-color var(--t-color),border-color var(--t-color)}.sidebar{font-size:16px;width:var(--sidebar-width);position:fixed;z-index:10;margin:0;top:var(--navbar-height);left:0;bottom:0;box-sizing:border-box;border-right:1px solid var(--c-border);overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--c-brand) var(--c-border);background-color:var(--c-bg-sidebar);transition:transform var(--t-transform),background-color var(--t-color),border-color var(--t-color)}.sidebar::-webkit-scrollbar{width:7px}.sidebar::-webkit-scrollbar-track{background-color:var(--c-border)}.sidebar::-webkit-scrollbar-thumb{background-color:var(--c-brand)}.sidebar-mask{position:fixed;z-index:9;top:0;left:0;width:100vw;height:100vh;display:none}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1){transform:rotate(45deg) translate3d(5.5px,5.5px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(2){transform:scale3d(0,1,1)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform:rotate(-45deg) translate3d(6px,-6px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1),.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform-origin:center}.theme-container.no-navbar .theme-default-content h1,.theme-container.no-navbar .theme-default-content h2,.theme-container.no-navbar .theme-default-content h3,.theme-container.no-navbar .theme-default-content h4,.theme-container.no-navbar .theme-default-content h5,.theme-container.no-navbar .theme-default-content h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .page{padding-top:0}.theme-container.no-navbar .sidebar{top:0}.theme-container.no-sidebar .sidebar{display:none}@media (max-width: 719px){.theme-container.no-sidebar .sidebar{display:block}}.theme-container.no-sidebar .page{padding-left:0}.theme-default-content a:not(.header-anchor):hover{text-decoration:underline}.theme-default-content img{max-width:100%}.theme-default-content h1,.theme-default-content h2,.theme-default-content h3,.theme-default-content h4,.theme-default-content h5,.theme-default-content h6{margin-top:calc(.5rem - var(--navbar-height));padding-top:calc(1rem + var(--navbar-height));margin-bottom:0}.theme-default-content h1:first-child,.theme-default-content h2:first-child,.theme-default-content h3:first-child,.theme-default-content h4:first-child,.theme-default-content h5:first-child,.theme-default-content h6:first-child{margin-bottom:1rem}.theme-default-content h1:first-child+p,.theme-default-content h1:first-child+pre,.theme-default-content h1:first-child+.custom-container,.theme-default-content h2:first-child+p,.theme-default-content h2:first-child+pre,.theme-default-content h2:first-child+.custom-container,.theme-default-content h3:first-child+p,.theme-default-content h3:first-child+pre,.theme-default-content h3:first-child+.custom-container,.theme-default-content h4:first-child+p,.theme-default-content h4:first-child+pre,.theme-default-content h4:first-child+.custom-container,.theme-default-content h5:first-child+p,.theme-default-content h5:first-child+pre,.theme-default-content h5:first-child+.custom-container,.theme-default-content h6:first-child+p,.theme-default-content h6:first-child+pre,.theme-default-content h6:first-child+.custom-container{margin-top:2rem}@media (max-width: 959px){.sidebar{font-size:15px;width:var(--sidebar-width-mobile)}.page{padding-left:var(--sidebar-width-mobile)}}@media (max-width: 719px){.sidebar{top:0;padding-top:var(--navbar-height);transform:translate(-100%)}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translate(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width: 419px){h1{font-size:1.9rem}}#vp-comment{max-width:var(--content-width);margin:0 auto;padding:2rem 2.5rem}@media (max-width: 959px){#vp-comment{padding:2rem}}@media (max-width: 419px){#vp-comment{padding:1.5rem}}.navbar{--navbar-line-height: calc( var(--navbar-height) - 2 * var(--navbar-padding-v) );padding:var(--navbar-padding-v) var(--navbar-padding-h);line-height:var(--navbar-line-height)}.navbar .logo{height:var(--navbar-line-height);margin-right:var(--navbar-padding-v);vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:var(--c-text);position:relative}.navbar .navbar-items-wrapper{display:flex;position:absolute;box-sizing:border-box;top:var(--navbar-padding-v);right:var(--navbar-padding-h);height:var(--navbar-line-height);padding-left:var(--navbar-padding-h);white-space:nowrap;font-size:.9rem}.navbar .navbar-items-wrapper .search-box{flex:0 0 auto;vertical-align:top}@media screen and (max-width: 719px){.navbar{padding-left:4rem}.navbar .site-name{display:block;width:calc(100vw - 11rem);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.navbar .can-hide{display:none}}.navbar-items{display:inline-block}@media print{.navbar-items{display:none}}.navbar-items a{display:inline-block;line-height:1.4rem;color:inherit}.navbar-items a:hover,.navbar-items a.route-link-active{color:var(--c-text)}.navbar-items .navbar-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:var(--navbar-line-height)}.navbar-items .navbar-item:first-child{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.route-link-active{margin-bottom:-2px;border-bottom:2px solid var(--c-text-accent)}@media (max-width: 719px){.navbar-items .navbar-item{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.route-link-active{margin-bottom:0;border-bottom:none}.navbar-items a:hover,.navbar-items a.route-link-active{color:var(--c-text-accent)}}.toggle-sidebar-button{position:absolute;top:.6rem;left:1rem;display:none;padding:.6rem;cursor:pointer}.toggle-sidebar-button .icon{display:flex;flex-direction:column;justify-content:center;align-items:center;width:1.25rem;height:1.25rem;cursor:inherit}.toggle-sidebar-button .icon span{display:inline-block;width:100%;height:2px;border-radius:2px;background-color:var(--c-text);transition:transform var(--t-transform)}.toggle-sidebar-button .icon span:nth-child(2){margin:6px 0}@media screen and (max-width: 719px){.toggle-sidebar-button{display:block}}.toggle-color-mode-button{display:flex;margin:auto;margin-left:1rem;border:0;background:none;color:var(--c-text);opacity:.8;cursor:pointer}@media print{.toggle-color-mode-button{display:none}}.toggle-color-mode-button:hover{opacity:1}.toggle-color-mode-button .icon{width:1.25rem;height:1.25rem}.DocSearch{transition:background-color var(--t-color)}.navbar-dropdown-wrapper{cursor:pointer}.navbar-dropdown-wrapper .navbar-dropdown-title,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:block;font-size:.9rem;font-family:inherit;cursor:inherit;padding:inherit;line-height:1.4rem;background:transparent;border:none;font-weight:500;color:var(--c-text)}.navbar-dropdown-wrapper .navbar-dropdown-title:hover,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{border-color:transparent}.navbar-dropdown-wrapper .navbar-dropdown-title .arrow,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:none;font-weight:600;font-size:inherit}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item{color:inherit;line-height:1.7rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{margin:.45rem 0 0;border-top:1px solid var(--c-border);padding:1rem 0 .45rem;font-size:.9rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>span{padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a{font-weight:inherit}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a.route-link-active:after{display:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper{padding:0;list-style:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper .navbar-dropdown-subitem{font-size:.9em}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a:hover,.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.route-link-active{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.route-link-active:after{content:"";width:0;height:0;border-left:5px solid var(--c-text-accent);border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item:first-child .navbar-dropdown-subtitle{margin-top:0;padding-top:0;border-top:0}.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title-mobile{margin-bottom:.5rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:none}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:block}.navbar-dropdown-wrapper.mobile .navbar-dropdown{transition:height .1s ease-out;overflow:hidden}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{border-top:0;margin-top:0;padding-top:0;padding-bottom:0}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle,.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item>a{font-size:15px;line-height:2rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem{font-size:14px;padding-left:1rem}.navbar-dropdown-wrapper:not(.mobile){height:1.8rem}.navbar-dropdown-wrapper:not(.mobile):hover .navbar-dropdown,.navbar-dropdown-wrapper:not(.mobile).open .navbar-dropdown{display:block!important}.navbar-dropdown-wrapper:not(.mobile).open:blur{display:none}.navbar-dropdown-wrapper:not(.mobile) .navbar-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:var(--c-bg-navbar);padding:.6rem 0;border:1px solid var(--c-border);border-bottom-color:var(--c-border-dark);text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}.page{padding-bottom:2rem;display:block}.page .theme-default-content{max-width:var(--content-width);margin:0 auto;padding:2rem 2.5rem;padding-top:0}@media (max-width: 959px){.page .theme-default-content{padding:2rem}}@media (max-width: 419px){.page .theme-default-content{padding:1.5rem}}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .navbar-items{display:none;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color);padding:.5rem 0 .75rem}.sidebar .navbar-items a{font-weight:600}.sidebar .navbar-items .navbar-item{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-items{padding:1.5rem 0}@media (max-width: 719px){.sidebar .navbar-items{display:block}.sidebar .navbar-items .navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.route-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-items{padding:1rem 0}}.sidebar-item{cursor:default;border-left:.25rem solid transparent;color:var(--c-text)}.sidebar-item:focus-visible{outline-width:1px;outline-offset:-1px}.sidebar-item.active:not(p.sidebar-heading){font-weight:600;color:var(--c-text-accent);border-left-color:var(--c-text-accent)}.sidebar-item.sidebar-heading{transition:color .15s ease;font-size:1.1em;font-weight:700;padding:.35rem 1.5rem .35rem 1.25rem;width:100%;box-sizing:border-box;margin:0}.sidebar-item.sidebar-heading+.sidebar-item-children{transition:height .1s ease-out;overflow:hidden;margin-bottom:.75rem}.sidebar-item.collapsible{cursor:pointer}.sidebar-item.collapsible .arrow{position:relative;top:-.12em;left:.5em}.sidebar-item:not(.sidebar-heading){font-size:1em;font-weight:400;display:inline-block;margin:0;padding:.35rem 1rem .35rem 2rem;line-height:1.4;width:100%;box-sizing:border-box}.sidebar-item:not(.sidebar-heading)+.sidebar-item-children{padding-left:1rem;font-size:.95em}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading){padding:.25rem 1rem .25rem 1.75rem}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading).active{font-weight:500;border-left-color:transparent}a.sidebar-heading+.sidebar-item-children .sidebar-item:not(.sidebar-heading).active{border-left-color:transparent}a.sidebar-item{cursor:pointer}a.sidebar-item:hover{color:var(--c-text-accent)}.table-of-contents .badge{vertical-align:middle}.dropdown-enter-from,.dropdown-leave-to{height:0!important}.fade-slide-y-enter-active{transition:all .2s ease}.fade-slide-y-leave-active{transition:all .2s cubic-bezier(1,.5,.8,1)}.fade-slide-y-enter-from,.fade-slide-y-leave-to{transform:translateY(10px);opacity:0}:root{--c-brand: #920097;--c-brand-light: #faaffd}html.dark{--c-brand: #920097;--c-brand-light: #faaffd}iframe{width:100%}:root{--search-bg-color: #ffffff;--search-accent-color: #3eaf7c;--search-text-color: #2c3e50;--search-border-color: #eaecef;--search-item-text-color: #5d81a5;--search-item-focus-bg-color: #f3f4f5;--search-input-width: 8rem;--search-result-width: 20rem}.search-box{display:inline-block;position:relative;margin-left:1rem}@media print{.search-box{display:none}}.search-box input{-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:text;width:var(--search-input-width);height:2rem;color:var(--search-text-color);display:inline-block;border:1px solid var(--search-border-color);border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all ease .3s;background:var(--search-bg-color) url("data:image/svg+xml,%3c?xml%20version='1.0'%20encoding='UTF-8'?%3e%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='12'%20height='13'%3e%3cg%20stroke-width='2'%20stroke='%23aaa'%20fill='none'%3e%3cpath%20d='M11.29%2011.71l-4-4'/%3e%3ccircle%20cx='5'%20cy='5'%20r='4'/%3e%3c/g%3e%3c/svg%3e") .6rem .5rem no-repeat;background-size:1rem}@media (max-width: 719px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}}.search-box input:focus{cursor:auto;border-color:var(--search-accent-color)}@media (max-width: 719px){.search-box input:focus{cursor:text;left:0;width:10rem}}@media (max-width: 419px){.search-box input:focus{width:8rem}}.search-box .suggestions{background:var(--search-bg-color);width:var(--search-result-width);position:absolute;top:2rem;right:0;border:1px solid var(--search-border-color);border-radius:6px;padding:.4rem;list-style-type:none}@media (max-width: 419px){.search-box .suggestions{width:calc(100vw - 4rem);right:-.5rem}}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{white-space:normal;color:var(--search-item-text-color)}.search-box .suggestion.focus{background-color:var(--search-item-focus-bg-color)}.search-box .suggestion.focus a{color:var(--search-accent-color)}.search-box .suggestion .page-title{font-weight:600}.search-box .suggestion .page-header{font-size:.9em;margin-left:.25em}@keyframes rotate{0%{transform:rotate(0)}50%{transform:rotate(360deg)}to{transform:rotate(360deg)}}.popup-enter-active,.popup-leave-active{transition:opacity .3s,transform .3s}.popup-enter-from,.popup-leave-to{opacity:0;transform:translateY(50%) scale(.5)}.sw-hint-popup,.sw-update-popup{position:fixed;bottom:1rem;inset-inline-end:1rem;z-index:var(--pwa-z-index);padding:.5rem .75rem;border-width:0;border-radius:.5rem;background:var(--pwa-bg-color);color:var(--pwa-color);box-shadow:0 2px 12px 0 var(--pwa-shadow-color);font-size:1rem;line-height:1.5;cursor:pointer}@media print{.sw-hint-popup,.sw-update-popup{display:none}}.sw-hint-popup .icon-wrapper,.sw-update-popup .icon-wrapper{display:inline-block;vertical-align:middle;width:1.5rem;height:1.5rem;margin-inline-start:.4rem;border-radius:.75rem;background:var(--pwa-btn-bg-color)}.sw-hint-popup .icon-wrapper:hover,.sw-update-popup .icon-wrapper:hover{background:var(--pwa-btn-hover-bg-color)}.sw-hint-popup .icon-wrapper svg,.sw-update-popup .icon-wrapper svg{width:1.2rem;height:1.2rem;margin:.15rem;color:var(--pwa-btn-text-color);animation:rotate 3s ease infinite}:root{--pwa-z-index: 10;--pwa-color: #2c3e50;--pwa-bg-color: #ffffff;--pwa-border-color: #3eaf7c;--pwa-shadow-color: rgb(0 0 0 / 15%);--pwa-btn-text-color: #ffffff;--pwa-btn-bg-color: #3eaf7c;--pwa-btn-hover-bg-color: #4abf8a;--pwa-content-color: #333;--pwa-content-light-color: #666}:root{--medium-zoom-z-index: 100;--medium-zoom-bg-color: #ffffff;--medium-zoom-opacity: 1}.medium-zoom-overlay{background-color:var(--medium-zoom-bg-color)!important;z-index:var(--medium-zoom-z-index)}.medium-zoom-overlay~img{z-index:calc(var(--medium-zoom-z-index) + 1)}.medium-zoom--opened .medium-zoom-overlay{opacity:var(--medium-zoom-opacity)}html.dark{--box-shadow: #0f0e0d;--card-shadow: rgba(0, 0, 0, .3);--black: #fff;--dark-grey: #999;--light-grey: #666;--white: #000;--grey3: #bbb;--grey12: #333;--grey14: #111}:root{--vp-bg: var(--c-bg, #fff);--vp-bgl: var(--c-bg-light, #f3f4f5);--vp-bglt: var(--c-bg-lighter, #eeeeee);--vp-c: var(--c-text, #2c3e50);--vp-cl: var(--c-text-light, #3a5169);--vp-clt: var(--c-text-lighter, #4e6e8e);--vp-brc: var(--c-border, #eaecef);--vp-brcd: var(--c-border-dark, #dfe2e5);--vp-tc: var(--c-brand, #3eaf7c);--vp-tcl: var(--c-brand-light, #4abf8a);--vp-ct: var(--t-color, .3s ease);--vp-tt: var(--t-transform, .3s ease);--box-shadow: #f0f1f2;--card-shadow: rgba(0, 0, 0, .15);--black: #000;--dark-grey: #666;--light-grey: #999;--white: #fff;--grey3: #333;--grey12: #bbb;--grey14: #eee}.theme-default-content figure{position:relative;display:flex;flex-direction:column;width:auto;margin:1rem auto;text-align:center;transition:transform var(--vp-tt)}.theme-default-content figure img{overflow:hidden;margin:0 auto;border-radius:8px}.theme-default-content figure img[tabindex]:hover,.theme-default-content figure img[tabindex]:focus{box-shadow:2px 2px 10px 0 var(--card-shadow)}@media print{.theme-default-content figure>a[href^="http://"]:after,.theme-default-content figure>a[href^="https://"]:after{content:""}}.theme-default-content figure>a .external-link-icon{display:none}.theme-default-content figure figcaption{display:inline-block;margin:6px auto;font-size:.8rem}html:not(.dark) figure:has(img[data-mode=darkmode-only]),html:not(.dark) img[data-mode=darkmode-only]{display:none!important}html.dark figure:has(img[data-mode=lightmode-only]),html.dark img[data-mode=lightmode-only]{display:none!important}
diff --git a/assets/wasm-build-conf-edit-CmamvRv7.png b/assets/wasm-build-conf-edit-CmamvRv7.png
new file mode 100644
index 00000000..56d8bffd
Binary files /dev/null and b/assets/wasm-build-conf-edit-CmamvRv7.png differ
diff --git a/assets/wasm-run-configuration-x_w9-EC1.png b/assets/wasm-run-configuration-x_w9-EC1.png
new file mode 100644
index 00000000..6ca168c9
Binary files /dev/null and b/assets/wasm-run-configuration-x_w9-EC1.png differ
diff --git a/en/backend-development/index.html b/en/backend-development/index.html
new file mode 100644
index 00000000..013d832d
--- /dev/null
+++ b/en/backend-development/index.html
@@ -0,0 +1,308 @@
+
+
+
+
+
+
+
+
+ 📚 Backend development | Learning Kotlin
+
+
+
+
+
+ Many frameworks officially support Kotlin: Springopen in new window, Quarkusopen in new window, Ktoropen in new window, among others listed hereopen in new window.
In addition to that, Kotlin is theoretically compatible with any framework that targets the JVM or JS. For example, this tutorial shows how to use nodejs with Kotlinopen in new window. However, frameworks that do not officially support Kotlin may require some tweaking to use it.
Ktor is a cross-platform Kotlin library for building both HTTP clients and servers. This makes Ktor a useful library to learn for both front-end developers for its HTTP client capabilities and backend-development for its HTTP server capabilities. In the following, we'll create a REST API with Ktor server.
- Create a project on start.ktor.ioopen in new window with the following plugins: Content Negotiation, kotlinx.serialization, and Routing.
- Click on "Generate project".
- Download the archive, unzip it, and open the project with IntelliJ.
- Create a
models
package and add to it a Customer
data class with these immutable properties id: String, firstName: String, lastName: String, email: String
. - Annotate the class with
@Serializable
. - Create a new package named
routes
and add to it a file CustomerRoutes.kt
that will contain the code for the /customer
endpoint. - The code below provides the implementation of some endpoints. Please implement the remaining ones.
- To enable the route call
customerRouting()
in the routing configuration file located in plugins/Routing.kt
. - For simplicity, use a global in-memory list of customers
val store = mutableListOf<Customer>()
. - Run the server by running the main method.
- Test the api on the IDE by using an http file or using any other client.
CustomerRoutes.kt
val store = mutableListOf<Customer>()
+
+fun Route.customerRouting() {
+ route("/customer") {
+ get {
+ call.respond(store)
+ }
+ get("{id?}") {
+ val id = call.parameters["id"] ?: return@get call.respondText(
+ "Missing id",
+ status = HttpStatusCode.BadRequest
+ )
+ val customer =
+ store.find { it.id == id } ?: return@get call.respondText(
+ "No customer with id $id",
+ status = HttpStatusCode.NotFound
+ )
+ call.respond(customer)
+ }
+ post {
+ val customer = call.receive<Customer>()
+ store.add(customer)
+ call.respondText("Customer stored correctly", status = HttpStatusCode.Created)
+ }
+ delete("{id?}") {
+
+ }
+ }
+}
+
plugins/Routing.kt
fun Application.configureRouting() {
+ routing {
+ customerRouting()
+ }
+}
+
return@label
You can specify which level you want to return with an explicit label using return@lambda
.
lambdaA {
+ lambdaB {
+ lambdaC {
+ val randomInt = Random.nextInt(0, 100)
+ if (randomInt > 50) return@lambdaC else return@lambdaB
+ }
+ printf("In lambdaB")
+ }
+}
+
This code runs another exampleopen in new window.
CustomerTest.http
POST http://127.0.0.1:8080/customer
+
+
+{
+ "id": "100",
+ "firstName": "Jane",
+ "lastName": "Smith",
+ "email": "jane.smith@company.com"
+}
+
+
+###
+POST http://127.0.0.1:8080/customer
+Content-Type: application/json
+
+{
+ "id": "200",
+ "firstName": "John",
+ "lastName": "Smith",
+ "email": "john.smith@company.com"
+}
+
+###
+POST http://127.0.0.1:8080/customer
+Content-Type: application/json
+
+{
+ "id": "300",
+ "firstName": "Mary",
+ "lastName": "Smith",
+ "email": "mary.smith@company.com"
+}
+
+
+###
+GET http://127.0.0.1:8080/customer
+Accept: application/json
+
+###
+GET http://127.0.0.1:8080/customer/200
+Accept: application/json
+
+###
+GET http://127.0.0.1:8080/customer/500
+Accept: application/json
+
+###
+DELETE http://127.0.0.1:8080/customer/100
+
+###
+DELETE http://127.0.0.1:8080/customer/500
+
This page has detailed stepsopen in new window
Spring is a famous framework for developing server-side applications: APIs, server generated web pages, microservices, etc. It relies on the the Java ecosystem to build and run, thus making it compatible with Kotlin. Even better, Spring officially supports Kotlin. It even allows in start a new project with Kotlin and Gradle-Kotlin. In the next section, we'll use this starter to recreate our above REST API with Spring.
- Create a project on start.spring.io (also called Spring initializr)open in new window with the following dependencies: Spring Web and Spring Boot DevTools.
- Choose Kotlin as the language and Kotlin-Grade as the project manager.
- Add these dependencies: Spring Web, Spring Boot DevTools, h2 database and Spring Data JPA.
- Click on "Generate". Download the archive, unzip it, and open the project with IntelliJ (preferably) or VSCode.
- Create
Customer
data class in the model
package without the @Serializable
annotation. - Create a
controller
package that contains a CustomerController
class which provides a CRUD using a global list. You can find a skeleton below. - 💡 In Spring, Rest controllers serve the purpose of Ktor routes, where a controller defines a REST resource.
- Define the same endpoints as in the previous PW.
- Start the REST API server by running
.\gradlew bootRun
or from your IDE. - Please test the endpoints with a REST client. You can find http files here in JetBrains formatopen in new window or VSCode's REST Client extensionopen in new window
CustomerController.kt
val store = mutableListOf<Customer>()
+
+@RestController
+@RequestMapping("/customer")
+class CustomerController {
+ @GetMapping
+ fun getAll() = store
+
+ @GetMapping("{id}")
+ fun getById(@PathVariable id: String) { }
+
+ @PostMapping
+ fun addOne(@RequestBody customer: Customer) { }
+
+ @DeleteMapping("{id}")
+ fun deleteOne(@PathVariable id: String) { }
+}
+
Models or model package ? plural or not ?
Both are ok as long as you follow the same convention in the project.
Let's go a little bit further by storing data in a database and writing some tests.
We'll use the H2 in-memory database for the sake of simplicity, since it does not require a server to run. Classes will mapped to database tables with JPA annotations. The database API we'll be using is called JPARepository
. It is a lightweight API that provides common CRUD features by just defining an interface.
On the testing side, we'll see two different syntaxes. The default one that is more familiar with Java style and the DSL one which is more readable and more familiar with Kotlin developers.
- Create a new Spring project using Spring initializropen in new window with Kotlin and the following dependencies: Spring Data JPA, H2 Database, Spring Boot DevTools, Spring Web
- Open the project and add this class in the
model
package @Entity class Product(@Id @GeneratedValue var id: Long? = null, var name: String, var price: Int)
. This single defines the class as well as the minimal JPA annotations (@Entity
, @Id
and @GeneratedValue
) to generate the corresponding table. - In the
repository
package, declare the ProductRepository
interface as follows interface ProductRepository: JpaRepository<Product, Long>
. This is enough for Spring to generate an implementation with common features as we'll see later. - Next, create a
ProductService
class which will contain the business logic. In terms of architecture, the controller calls a service which in turn rely on other services or repositories.
ProductService.kt
@Service
+class ProductService(@Autowired val productRepository: ProductRepository) {
+ fun getAll() = productRepository.findAll()
+
+
+ fun getById(id: Long) = productRepository.findByIdOrNull(id)
+}
+
- In the controller package, create a
ProductController
class that is mapped to /product
and injects the with @Autowired
. Reply to @Get
as follows.
ProductController.kt
@RestController
+@RequestMapping("/product")
+class ProductController(@Autowired val productService: ProductService) {
+ @GetMapping fun getAll() = productService.getAll()
+
+ @GetMapping("{id}")
+ fun getById(@PathVariable id: Long) =
+ productService.getById(id) ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
+}
+
Kotlin makes getById(@PathVariable id: Long) more concise
The Elvis operator ?:
allows to simplify the code. Here is a longer version as reference.
@GetMapping("{id}")
+fun getById(@PathVariable id: Long): Product {
+ val product = productService.getById(id)
+ if (product != null){
+ return product
+ }
+ throw ResponseStatusException(HttpStatus.NOT_FOUND)
+}
+
In addition to that, Spring provides @ControllerAdvice
to change the exception message. You can see an example hereopen in new window.
- Let's run the project. Before running the project, we need to add a plugin that allows Kotlin classes to generate a default constructor
id("org.jetbrains.kotlin.plugin.jpa") version "1.8.10"
. The plugins should look as follows:
plugins {
+ id("org.jetbrains.kotlin.plugin.jpa") version "1.8.10"
+ id("org.springframework.boot") version "3.0.4"
+ id("io.spring.dependency-management") version "1.1.0"
+ kotlin("jvm") version "1.8.10"
+ kotlin("plugin.spring") version "1.8.10"
+}
+
- As an exercise, implement these endpoints: POST a single product, DELETE by id (
/product/{id}
) and GET by id (/product/{id}
). - Hint:
ProductController
already provides the necessary methods.
- Call the different endpoints with a REST client.
Spring frameworks helps perform different types of tests by providing different classes out of the box:
- Unit testing of services, repositories and the REST API. This is done through mock utilities such as
MockMVC
. - Integration testing of the REST API using
TestRestTemplate
. In this situation, a full server is run and tested.
Most, if not all classes provided by Spring provide an elegant syntax for Java developers. Some of them go further by taking advantage of Kotlin specific features. In the following, we're going to focus on parts that provide Kotlin DSLs, namely unit testing the REST API with MockMVC
.
- Create a test class
ProductControllerUnitTests
with this initial content. MockMvc
allows to unit test the REST API. The @AutoConfigureMockMvc
annotation allows spring to configure it automatically
@SpringBootTest
+@AutoConfigureMockMvc
+class ProductControllerTests(
+ @Autowired val mockMvc: MockMvc,
+ @Autowired val productRepository: ProductRepository) {
+
+ @BeforeEach
+ fun reset(){
+ productRepository.deleteAll()
+ }
+}
+
- Add these two tests. The first one uses a classic approach while the second take advantage of Kotlin DSL capabilities. In addition to that, we name using a more readable string literal
@Test
+fun testWithClassicApproach(){
+ mockMvc.perform(get("/product"))
+ .andExpect(status().isOk)
+ .andExpect(content().string(containsString("[]")))
+}
+
@Test
+fun `test GET a single product`() {
+ mockMvc.get("/product/1").andExpect {
+ status { isOk() }
+ jsonPath("$.name") { value("A") }
+ jsonPath("$.price") { value(1) }
+ content { contentType(MediaType.APPLICATION_JSON) }
+ }
+}
+
- As an exercise, unit tests for the other endpoints.
The request builder of JpaRepository
Spring repositories implement requests based on the name of their methods. For example, to get all products sorted by name, we can add this method to the interface.
interface ProductRepository: JpaRepository<Product, Long> {
+ fun findAllByOrderByNameAsc(): List<Product>;
+}
+
The official documentationopen in new window provides more detailed explanations and examples.
Thanks to Kotlin/JS, we can write apps that target nodejs using Kotlin. We can even import npm libraries as long as you declare the JS API surface that you'll be using in Kotlin. This is called external declaration (You can think of it as an equivalent of TypeScript's type definitions) that declares the symbols that we want to access in Kotlin thanks to @JsModuleopen in new window and @JsNonModuleopen in new window annotations.
Defining such external declarations can be a hassle and there seems to be no official automatic generator (dukatopen in new window has been removed in kotlin 1.8.20). In that case, we have two options, either we write the external declaration ourselves or import it as a dependency if available. Fortunately for express developers, chrisnkrueger/kotlin-expressopen in new window provides declarations for the express library.
There are two gradle plugins that allow to create nodeJs projects: the kotlin("js")
one and the kotlin("multiplatform")
one. The difference between the two plugins is that the former only supports JS or WASM while the latter supports more platforms but requires to configure source sets. Thus, the former may seem easier to setup but the latter is better in the long run because it allows us to get more familiar with Kotlin Multiplatform (KMP).
At the time of writing, I didn't find an official wizard or starter project. So we'll create one from scratch using gradle init
.
- Create a new Gradle project using IntelliJ or by running
gradle init
in a empty folder (see below for the replies to the gradle init
command).
gradle init
gradle init
+Starting a Gradle Daemon, 1 incompatible and 1 stopped Daemons could not be reused, use --status for details
+
+Select type of project to generate:
+ 1: basic
+ 2: application
+ 3: library
+ 4: Gradle plugin
+Enter selection (default: basic) [1..4] 1
+
+Select build script DSL:
+ 1: Kotlin
+ 2: Groovy
+Enter selection (default: Kotlin) [1..2] 1
+
+Project name (default: starter): rest-api-kotlin-nodejs
+
+Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no] yes
+
+
+> Task :init
+To learn more about Gradle by exploring our Samples at https://docs.gradle.org/8.3/samples
+
+BUILD SUCCESSFUL in 24s
+2 actionable tasks: 2 executed
+
- In build.gradle.kts, add and configure the
kotlin("multiplatform")
plugin. Also add the express
and dev.chriskrueger:kotlin-express
dependencies.
build.gradle.kts
plugins {
+ kotlin("multiplatform") version "1.9.20-Beta"
+}
+
+repositories {
+ mavenCentral()
+}
+
+group = "tech.worldline.demo"
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ js {
+ nodejs {
+ }
+ binaries.executable()
+ useCommonJs()
+ }
+
+ sourceSets {
+ val jsMain by getting {
+ dependencies {
+ implementation(npm("express", "> 4.0.0 < 5.0.0"))
+ implementation("dev.chriskrueger:kotlin-express:1.2.0")
+ }
+ }
+ }
+}
+
Some notes on the build file
- create a main.kt file in src/jsMain/kotlin with the following content:
main.kt
data class Message(val id: Int, val message: String)
+
+val messages = mutableListOf(Message(0, "I love Kotlin/JS"))
+
+fun main() {
+ val app = express.Express()
+
+
+ app.get("/hello") { _, res ->
+ res.send(messages)
+ }
+
+
+ app.listen(3000) {
+ console.log("server start at port 3000")
+ }
+}
+
Execution failed for task ':kotlinStoreYarnLock'
If you get this error:
Execution failed for task ':kotlinStoreYarnLock'.
+> yarn.lock was changed. Run the `kotlinUpgradeYarnLock` task to actualize yarn.lock file
+
Run ./gradlew kotlinUpgradeYarnLock
so that yarn.lock is updated
Let's add a post endpoint which reads the body as a json. In order to read the body as json, we must add this possibility to express by importing the npm library body-parseropen in new window and by calling app.use(bodyParser.json())
. Once this setup is complete, req.body
will contain the content of the body. However, there is no available external definition for bodyParser as of the time of writing. Thus, we must create or own external definition.
- First, add the body-parser dependncy in the build file
implementation(npm("body-parser", "> 1.0.0 < 2.0.0"))
- Next, we would write:
app.use(bodyparser.json())
to activate the library. Let's guess what a minimal definition of bodyparser
can be.
BodyParser.kt
+external class BodyParser {
+
+ fun json(): Any
+
+}
+
+
+@JsModule("body-parser")
+external val bodyParser: BodyParser
+
- Finally, we just need to add the BodyParser.kt file into the project and use it in our server.
main.kt
app.use(bodyParser.json())
+app.post("/hello") { req, res ->
+
+ if (req.body as? Message == null) {
+ println("failed to get the body from Kotlin")
+ }
+
+
+ println("req.body from JS ${js("req.body.id")} - ${js("req.body.message")}")
+ val id = js("req.body.id") as? Int
+ val message = js("req.body.message") as? String
+ if (message != null && id != null) {
+ messages.add(Message(id, message))
+ res.status(201).end()
+ } else {
+ res.status(400).send(js("{cause : 'error'}") as Any)
+ }
+}
+
- Add PUT and DELETE endpoints
These official tutorials go even further:
References
+
+
+
diff --git a/en/front-development/index.html b/en/front-development/index.html
new file mode 100644
index 00000000..9687b320
--- /dev/null
+++ b/en/front-development/index.html
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+ 📚 Frontend development | Learning Kotlin
+
+
+
+
+
+ Kotlin supports a wide selection of frontend frameworks across all platforms: mobile, desktop and web.
Please find below a glimpse of the possibilities that you can do right from IntelliJ:
- On the Desktop side
- On the Web
- Ktoropen in new window can use templates engines such as FreeMarkeropen in new window to create server pages.
- With KotlinJS, developers can create React, nodsjs, or vanilla JS Apps using Kotlin.
- Kotlin WASM compiles into Web Assembly. It can complement KotlinJS for computation intensive tasks.
- On Mobiles
Kotlin supports cross platform frontend development thanks to Kotlin MultiPlatform (KMP)
"The Kotlin Multiplatform technology is designed to simplify the development of cross-platform projects. It reduces time spent writing and maintaining the same code for different platforms while retaining the flexibility and benefits of native programming." ₁open in new window
KMPopen in new window relies on Kotlin native and other Kotlin features to help developers create projects that target multiple platforms using a common Kotlin code-base.
KMPMany combinations of targets and use cases are possible:
- Kotlin/JS can also target the web and even use web frameworks (such as react) in Kolitn.
- Kotlin WASM is another possibility to target the web but this will generate WASM instead of pure JS code.
- It can be used for example to develop computation intensive libraries.
- Maybe we can do even more in the future with as all these technologies (Kotlin, WASM and Kotlin/WASM) evolve. - For example, WASIopen in new window allows WASM to communicate with the operating system. - This means that me may see Kotlin/WASM project projects in the future that can target both the browser and the OS.
- Let's keep watching 😄.
- Let's create a Kotlin/WASM app. By cloning
git clone git@github.com:Kotlin/kotlin-wasm-examples.git
and opening the browser-example folder in your IDE. - Open the project and run the
wasmJsBrowserRun
task. - The development server should start and you can open your WASM powered webapp on http://localhost:8080/open in new window
- ⚠️ You may need to activate some flags on your browser for the app to work. If you see a blank page, please read the browser logs to check for the instructions.
Alt text- Please check the contents of src/wasmJsMain/kotlin/Simple.kt to understand how the page is coded.
- Next, let's check the generated wasm file which is available in build/js/packages/project_name/kotlin
Both Kotlin/WASM and Kotlin/JS IntelliJ work somewhat similarly.
- Both rely on the KMP plugin
- Kotlin/WASM is enabled by adding a
wasmjs
section in the build.gradle.kts
file, while Kotlin/JS is enabled by adding a js
section. - The Kotlin code will compile to WASM and / or JS. Kotlin/JS generates only JS while Kotin/WASM generates both JS and WASM.
- In both cases, the entry point of the generated code is a JS file called module_name.js.
- The index.html in the resources folder loads the generated JS explained above (the one named module_name.js).
- The task
wasmBrowserDevelopmentRun
or jsWasmBrowserDevelopmentRun
run a local server that hosts both the index.html files and the generated JS and WASM files.
"Compose Multiplatform simplifies and accelerates UI development for Desktop and Web applications, and allows extensive UI code sharing between Android, iOS, Desktop and Web. It's a modern toolkit for building native UI. Quickly bring your app to life with less code, powerful tools, and intuitive Kotlin APIs."
imgButton(
+ onClick = {
+ logger.info("Pressed!")
+ }
+) {
+ Text("Hello $platform")
+}
+
It is based on Android Jetpack Composeopen in new window declarative UI approach ( which is similar also to iOS SwiftUIopen in new window ) 1open in new window
Compose multiplatform vs Jetpack Compose
While very similar, Compose multiplatform is different from Jetpack Compose as the latter is only compatible with Android. Google provides a JetPack compose tutorialopen in new window for Android development.
Compose HTML is not cross-platform
Compose HTML is UI a library targeting Kotlin/JS which is not compatible with Compose Multiplatform (it is a different API). For cross-platform UI development with Compose Multiplatform, compose Web is the choice.
We'll create a multiplatform app using the official templateopen in new window. At the time of writing, this template does not include a compose web target.
- Please check that your environment is correctly setup as explained hereopen in new window.
- On Windows and Linux, we don't need to install iOS/macOS related tools but and we won't be able to run iOS/macOS targets.
- If we don't want to install Android Studio, we need at least to install the Android SDK either through the official installer or from the "Languages and Framework -> Android SDK" menu in the settings.
- Open the official templateopen in new window and either download a zip or use the "use this template" options on GitHub.
- Open the downloaded projet. You'll note that it contains these modules:
- a shared module (or subproject) that contains common code as well as
- and another module for earch targeted platform: androidApp, iOSApp and desktopApp (When web will be included in the template, we should also see a webApp project). These contain the source code of the apps itself (such as the main activity in Android, the
@main App
in iOS and the main function in desktopJVM) and well as platform specific resources that cannot be placed in the shared module. Some examples of such files are the AndroidManifest.xml for android and the info.plist in iOS.
- In order to run the desktopApp, open a terminal on the project root folder and launch this command:
./gradlew desktopApp:run
. - In order to run the Android App, the simplest way is to launch it from IntelliJ
. It is also possible define a gradle taskopen in new window that installs the app on the device and issues a command to the device to launch it. - In order to run the iOS App, the simplest way is to run it on the simulator using IntelliJ. In order to run it on a real device, the TramID needs to be defined as explained hereopen in new window
Alt textCompose multiplatform is a component based declarative UI framework. Each component is called a Composable
and is defined as a function annotated with @Composable
.
In compose multiplatform, the main component (the component at the root of the App) is usually found in shared/src/commonMain/Kotlin/App.kt.
- Take a look at shared/src/commonMain/Kotlin/App.kt, run the app and try to understand how compose works.
- Let's create a new composable called
RandomNumberList
.
@Composable
+fun RandomNumberList(){
+
+ val myRandomValues = List(5) { Random.nextInt(0, 30) }
+
+ LazyColumn {
+ items(myRandomValues.size){
+ Text(text = "$it")
+ }
+ }
+}
+
- Place this composable below
AnimatedVisibility
and Button
and run the app.
- Exercise: Make the "Hello, .." button switch between showing the list and and the image.
Hello compose demo
+
+
+
diff --git a/en/index.html b/en/index.html
new file mode 100644
index 00000000..f152af3f
--- /dev/null
+++ b/en/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Welcome | Learning Kotlin
+
+
+
+
+
+
+
+
+
diff --git a/en/kotlin-features-advanced/index.html b/en/kotlin-features-advanced/index.html
new file mode 100644
index 00000000..dd225750
--- /dev/null
+++ b/en/kotlin-features-advanced/index.html
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+ 📚 Advanced and other Kotlin features | Learning Kotlin
+
+
+
+
+
+ Kotlin allows to delegate the getter and setter of a property to another object, which is called a delegate. It is a class that defines the getValue
and setValue
methods.
Kotlin provides standard delegates such lazy properties and observable properties.
▶️ this codeopen in new window illustrates delegate properties.
Kotlin provides a high level concurrency model called Coroutines. The developer can delegated the management of threads to the compiler and runtime and using higher level constructs than threads to express asynchronous operations.
Coroutines in Kotlin revolve around these concepts:
- A coroutine is an instance of suspendable computation.
- Kotlin has many methods for creating a coroutine such as
launch
.
- A coroutine must exist within a coroutine scope.
- For example
runBlocking
creates a coroutine scope whithin which coroutines can be launched.
- A coroutine can run suspend functions which can suspend the coroutine but do not block the thread.
- For example: the
delay
suspend the coroutine but does not block the thread on which it is running. - Suspend functions are operations that may take time such http requests and file system calls.
- The
suspend
qualifier defines a suspend function. It runs within a coroutine and can call other suspend functions. Flow
allows to generate a list of asynchronous values.Deferred
and Channel
transfer a single value and a stream of values, respectively, between coroutines.
▶️ this codeopen in new window show how to create a coroutine and suspend function and how to use them.
▶️ this codeopen in new window illustrated flows.
▶️ this codeopen in new window illustrates channels and deferred.
As seen previously, function extension add behavior to existing classes. Inside the definition of the function extension, we can reference the extension receiver (or this) implicitly.
fun String.countCharacters() = length
+println("hello".countCharacters())
+
We can define this extension with a function literal (or lambda) in instead of a named function (declared with fun
).
var extFn: String.() -> Int
+extFn = { length }
+println("hello".extFn())
+println(extFn("hello"))
+
extFn
is a function literal (lambda) that has access to the receiver (this). That's why it's called a function literal with receiver.
extFn("hello")
or extFn("hello")
call the extension as expected from extension functions.
The type of a function literal with receiver is funName: ReceiverType.(arg1Type, arg2Type, etc.) -> ReturnType
and is called with funName(receiverValue, arg1Value, etc.)
or receiverValue.funName(arg1Value, etc.)
. However, this is not the interesting aspect.
The important part is extFn = { length }
which can be put as a function argument in a higher order function. The developer that calls the higher order function must define extFn
, which in turn has access to the receiver. This allows for a nice style of programming. ▶️ this codeopen in new window shows an example.
Type-safe buildersopen in new window combine well-named builder functions and functions literals with receiver to create type-safe, statically typed builders in Kotlin.
Builder pattern
The builder patternopen in new window is a way to construct complex objects.
+val text = StringBuilder("Temp")
+ .append(1)
+ .append(true)
+ .append("friend")
+ .toString()
+
This code showsopen in new window a basic type-safe builder.
Kotlin docsopen in new window provides an example of an advanced type-safe builder that builds an HTML page.
- open the java-integration-exercise projects in the materials folder.
- Have a look at the Java class we provided you in the
src/main/java/com/worldline/learning/kotlin/java2kotlin
package. (yes, that's the Pokemon class) - Convert that Java class in Kotlin using IntelliJ's awesome copy-pasta tool! (just copy paste the java code in a kotlin file, one is provided at
src/main/kotlin/com/worldline/learning/kotlin/java2kotlin
) - Have a look at the generated Kotlin code, and note the major differences you spot!
+
+
+
diff --git a/en/kotlin-features/index.html b/en/kotlin-features/index.html
new file mode 100644
index 00000000..c65e36fd
--- /dev/null
+++ b/en/kotlin-features/index.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+ 📚 Kotlin language features | Learning Kotlin
+
+
+
+
+
+ Kotlin is an object oriented language with functional features. This chapter covers important and relevant features of the language slit into basic and intermediate. Another chapter covers advanced features.
- Kotlin is statically typed and supports implicit typing.
- Static typing: types cannot change on runtime (it is the opposite of dynamic typing).
- Implicit typing: the compiler can infer the type whenever possible.
var
creates mutable variables.val
creates immutable variables or constants.- Semi-colons are optional.
- Kotlin supports top level declaration of variables and functions (They can be declared outside of classes).
- String interpolation is available with this syntax
${expression}
. if
, and when
statements are expressions (they can return a value). when
is equivalent to the switch
statement of other languages- The ternary operator is not available but the
if
statement replaces-it.
- for-each is the only type of for loop available.
- Object oriented programming is supported as in Java with some additional features such as extensions.
- The compiler supports Null Safety. It allows to write code free from null pointer errors with a compile time guarantee.
- Functional programming is supported (Higher-order functions and functions as 1st class items, etc.).
Use val by default
Replace by var
only if needed.
▶️ this codeopen in new window highlights the above features.
In the this section, the terms 'argument' and 'parameter' are used interchangeably.
Functions in Kotlin have the following features:
- Declaration:
fun functionName(arg1: type1 = defaultvalue1, ...) : retunrType
. - Call a function by passing the value in the declaration order.
- Use argument labels for more clarity, however, it also allows for arbitrary ordering of arguments.
- Optional arguments have a default value and can be omitted during the call.
- Functions are first class items or citizen: they can be assigned to a variable, passed as a function parameter, or returned from a function.
- 💡 A function that takes a function as an argument or returns one is a higher order function.
- A function type can be expressed as follows:
(typeOfParam1, typeOfParam2, etc) -> returnType
(The empty return type is Unit
). - Anonymous functions use the following syntax
{ argName1, argName2, etc. -> // code }
- Also called or lambda functions or literal functions
- The last function argument can be put after the closing after the closing parenthesis
compute(9, 5) { x, y -> x * y }
▶️ this codeopen in new window highlights the above features.
The next section talk about null safety.
In a nutshell, null safety is a compiler feature that eliminates the infamous Null pointer exception or npe. The Kotlin compiler reports errors and warnings when we manipulate nullable (also called optional) values. Here is a list of null safety features provided by Kotlin:
- All types are non-nullable by default; we cannot assign
null
to a variable or an argument. - For example, this code fails
var s: String = null
.
- A type can be made nullable by suffixing it with a ?. For example:
var s: String? = null
. - Kotlin forbids calling a method or a property of a non-nullable type, unless we do one of these possibilities:
- Use optional chaining with the ? suffix.
- Provide a default value with the elvis ?: operator.
- Smart-cast the nullable into a non-nullable.
- Use the !! operator that eliminates compiler checks. This should never be used.
Never unwrap with !!
Use other safe techniques instead.
▶️ this codeopen in new window illustrates null safety and how to use optional types.
Java `Optional` does not provide compile time null checks
Optional
wrap null values on runtime. The Java compiler (as of version 17) does not provide unwrapping features such as smart casting. It is still possible to have a npe like this: Optional<String> s = null; s.isPresent()
;
Enumerations allow to work with a group of values in a type-safe fashion. Unlike Java enums, Kotlin enums are classesopen in new window. Kotlin enum classes provide these features:
when
statements support enumerations.- Enum constants can declare their own anonymous classes with their corresponding methods, as well as with overriding base methods.
- An enum class can implement an interface but it cannot derive from a class
- There are methods for listing the defined enum constants and getting an enum constant by its name.
- Every enum constant has properties for obtaining its name and position (starting with 0).
▶️ this codeopen in new window illustrated the features above. For further reading please consult the official documentationopen in new window.
Please click on this link to view the exerciseopen in new window
Please open to see the solution(s)
Solutionopen in new window
Please click on this link to view the exerciseopen in new window
Please open to see the solution(s)
Solutionopen in new window
Kotlin allows to write concise OOP code and has the following features:
- Available common features: classes, inheritance, interfaces, and abstract classes.
- Native support of properties: do not define getters and setters unless needed.
- Just add
get()
and set(value)
functions next to the property declaration.
- Constructor arguments are defined next to the class name
class ClassName(arg1, atg2, )
- Prefixing a constructor arguments with
val
or var
makes it a property (val
makes it read-only). - The constructor name is
init
and does not require parameters. - The compiler checks that all non-nullable properties are initialized by the end of the constructor.
- ⚠️ The compiler does not check the initialization of
lateinit
properties. Thus, accessing them before while uninitialized causes an exception.
- Prefix classes with
open
to allow inheritance. - Kotlin enables the
public
access level by default. - The equality operator
==
calls equals()
(as opposed to Java which uses reference equality). - A companion object contains static methods and properties.
- Extensions add function and properties to existing classes.
- 💡 They replace inheritance in many situations.
- For example, we can add functions to the String class instead of creating a new
StringUtils
class.
- Sealed classes and interfaces cannot be extended or implemented by third parties.
Do not define accessors unless needed
As opposed to Java, Kotlin supports properties and allows to add accessors later without refactoring the code that calls these properties. Thus, by default, just define the name of properties without accessors and use them directly.
▶️ this codeopen in new window illustrates some features.
Data classes are final (cannot be inherited from) classes that provide standard functionalityopen in new window:
equals()
and hashCode()
toString()
of the form "class(field=value, ...)"
componentN()
that correspond to the properties in their order of declaration.copy()
However, they have the following constraintsopen in new window:
- The primary constructor needs to have at least one parameter.
- All primary constructor parameters need to be marked as val or var.
- They cannot be abstract, open, sealed, or inner (💡 but extensions are possible).
▶️ this codeopen in new window illustrates some features.
Functional programming revolves around these conceptsopen in new window: pure functions, recursion, referential transparency, immutable variables, functions as first-class citizens, and higher-order functions.
Let's briefly explain these concepts:
- Immutable variables means that we cannot change the value of a variable or its properties once it has been created. If we want to do so, we must create a new instance with the new value.
- Pure functions are functions that do not have side effects and will thus return always the same output given the same input.
- Functions are first class citizens: they can be assigned to a variable or used in higher-order functions (passed as a function parameter to another function or returned from a function).
- Referential transparencyopen in new window: means that an expression can be replaced by its result without changing the behavior of the program. Transparency refers to the fact that the implementation of the expression is irrelevant.
💡 Pure functional languages provide these features natively and enforces them (at build time).
Kotlin is not a pure functional languages but it supports some features. For example, Kotlin does not have compile time verification of pure functions, but it provides immutable collections through the kotlinx.collections.immutableopen in new window library.
listOf generates read-only lists, which are not immutable
A read-only listopen in new window cannot add or remove elements, but it can change the underlying data.
@Test
+fun givenReadOnlyList_whenCastToMutableList_checkNewElementsAdded(){
+ val list: List<String> = listOf("This", "Is", "Totally", "Immutable")
+ (list as MutableList<String>)[2] = "Not"
+ assertEquals(listOf("This", "Is", "Not", "Immutable"), list)
+}
+
The Arrow-ktopen in new window library add more functional programming features.
Declarative programming is a famous style within functional programming. It consists of writing code as a chaining of function calls in this style val result = f(x).g(y). ...
. Higher order functions replace many situation where we would use loops. This favors readable code which is easy to debug an maintain.
▶️ this codeopen in new window show an example of list manipulation using declarative programming.
- Kotlin is designed with Java interoperability in mind.
- Kotlin code may require some annotations to be called from Java.
- It is possible to mix Java and Kotlin in the same project.
- JetBrain's IntelliJ and Android Studio can convert to Kotlin when pasting java code.
- Kotlin generates Java records by annotating a data class with
@JvmRecord
and targeting JVM 16, among other requirement listed hereopen in new window. - It is much more easier and natural to call Java from Kotlin.
- For example: Java accessors are converted to Kotlin properties.
▶️ this codeopen in new window shows how to convert a Kotlin List
to a Java ArrayList
.
The official documentation provides exhaustive documentation on Kotlin and JVM integrationopen in new window
Please click on this link to view the exerciseopen in new window
Please open to see the solution(s)
Solutionopen in new window
Please click on this link to view the exerciseopen in new window
Please open to see the solution(s)
Solutionopen in new window
Please click on this link to view the exerciseopen in new window
Please open to see the solution(s)
Solutionopen in new window
Please click on this link to view the exerciseopen in new window
Please open to see the solution(s)
Solutionopen in new window
Please click on this link to view the exerciseopen in new window
Please open to see the solution(s)
Solutionopen in new window
+
+
+
diff --git a/en/other-technologies/index.html b/en/other-technologies/index.html
new file mode 100644
index 00000000..59bc6f10
--- /dev/null
+++ b/en/other-technologies/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ 🛠 Let's make a cross-plaform app ! | Learning Kotlin
+
+
+
+
+
+
+
+
+
diff --git a/en/presentation/index.html b/en/presentation/index.html
new file mode 100644
index 00000000..78816515
--- /dev/null
+++ b/en/presentation/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ 🚀 Presentation of Kotlin | Learning Kotlin
+
+
+
+
+
+ Kotlin is a modern programming language developed by JetBrains.
- Kotlin compiles to many targets: the JVM, JS, WASM, Android, iOS, Desktop, native code, etc.
- Many server frameworks officially support Kotlin such as Spring and Quarkus.
- It is the first-class language for writing Android.
- Kotlin can compile to native with Kotlin native or with GraalVMopen in new window.
- KMP allows to share code between different platforms.
- Kotlin can be considered as both fullstack and corss-platform language.
- Inter-operable with Java.
- Provides modern features such as compile-time null safety and data classes.
- Supports Object oriented programming and functional programming.
- Kotlin KEEPopen in new window is its open source evolution process.
- February 15, 2016: Kotlin 1.0 was released.
- January 04, 2017: Spring introduced Kotlin support in Spring 5.
- At Google I/O 2017, Google announced first-class support for Kotlin on Android.
- On 7 May 2019, Google announced that Kotlin the preferred language for Android app developers.
- June 2022: Kotlin 1.7 was released with the version of the new K2 compiler.
Source: Amyra Sheldonopen in new window
- As of 2022, Kotlin is used by 7.8% of the industry experts.
- According to StackOverflow, Kotlin was the 4th most beloved language in 2020 with 62.9% votes.
- Kotlin is now listed among the top 20 programming languages by Redmonk.
- Kotlin is among the top 3 languages that most businesses are planning to migrate their apps to in 2022.
- Kotlin is used by global companies like Google, NetFlix, Amazon, Trello, and more.
- Pinterest and Uber are among the companies that migrated their apps to Kotlin from Java.
kotlin used for what?Please find more statics here:
Here are some arguments that motivate switching from Java (version 17 LTS at the time of writing) to Kotlin.
- Kotlin supports more targets than Java.
- Kotlin has compile time null-safety (Java Optionals are runtime wrappers for nullable values and null annotations have less features).
- Kotlin strings support interpolation.
- Casting in Kotlin is smart.
- Kotlin functional programming features are better. It even allows to define Type-Safe builders and DSLs.
- Kotlin can be mixed with Java code, thus helping the migration process.
You can read more arguments in these articles:
decision tree
+
+
+
diff --git a/en/workshops/index.html b/en/workshops/index.html
new file mode 100644
index 00000000..f7c67022
--- /dev/null
+++ b/en/workshops/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ 📅 Workshops | Learning Kotlin
+
+
+
+
+
+
+
+
+
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 00000000..0e4322cb
Binary files /dev/null and b/favicon.ico differ
diff --git a/fr/backend-development/index.html b/fr/backend-development/index.html
new file mode 100644
index 00000000..b1f22cc9
--- /dev/null
+++ b/fr/backend-development/index.html
@@ -0,0 +1,231 @@
+
+
+
+
+
+
+
+
+ 📚 Développement du backend | Apprendre Kotlin
+
+
+
+
+
+ De nombreux frameworks supportent officiellement Kotlin comme Springopen in new window, Quarkusopen in new window et Ktoropen in new window, parmi d'autres listés iciopen in new window.
En outre, Kotlin est théoriquement compatible avec tout framework qui cible la JVM ou JS. Cependant, les frameworks qui ne supportent pas officiellement Kotlin peuvent nécessiter quelques ajustements pour l'utiliser.
Ktor est une bibliothèque Kotlin multiplateforme permettant de développer des clients et des serveurs HTTP. Cela fait de Ktor une bibliothèque utile à la fois aux développeurs frontend, pour la partie client HTTP, ainsi qu'aux développeurs backend, pour la partie serveur HTTP. Dans ce qui suit, nous allons créer une API REST avec le serveur Ktor.
- Créez un projet sur start.ktor.ioopen in new window avec les plugins suivants : Content Negotiation, kotlinx.serialization, et Routing.
- Cliquez sur "Generate project".
- Téléchargez l'archive, décompressez-la et ouvrez le projet avec votre IDE préféré.
- Créez un package
models
et ajoutez-y une classe de données Customer
avec ces propriétés immuables id : String, firstName : String, lastName : Chaîne, email : Chaîne
. - Annotez la classe avec
@Serializable
. - Créez un nouveau package nommé
routes
et ajoutez-y un fichier CustomerRoutes.kt
qui contiendra le code pour l'endpoint /customer
. - Le code ci-dessous fournit l'implémentation de certains endpoints. Veuillez implémenter les autres.
- Pour activer la route, appelez
customerRouting()
dans le fichier de configuration du routage situé dans plugins/Routing.kt
. - Pour plus de simplicité, utilisez une liste globale de clients en mémoire
val store = mutableListOf<Customer>()
. - Lancer le serveur en exécutant la méthode main.
- Tester l'API sur l'IDE en utilisant un fichier http ou en utilisant n'importe quel autre client.
CustomerRoutes.kt
val store = mutableListOf<Customer>()
+
+fun Route.customerRouting() {
+ route("/customer") {
+ get {
+ call.respond(store)
+ }
+ get("{id?}") {
+ val id = call.parameters["id"] ? : return@get call.respondText(
+ "Missing id",
+ status = HttpStatusCode.BadRequest
+ )
+ val customer =
+ store.find { it.id == id } ? : return@get call.respondText(
+ "Pas de client avec l'id $id",
+ status = HttpStatusCode.NotFound
+ )
+ call.respond(customer)
+ }
+ post {
+ val customer = call.receive<Customer>()
+ store.add(customer)
+ call.respondText("Customer stored correctly", status = HttpStatusCode.Created)
+ }
+ delete("{id?}") {
+
+ }
+ }
+}
+
+
plugins/Routing.kt
fun Application.configureRouting() {
+ routing {
+ customerRouting()
+ }
+}
+
return@label
Vous pouvez spécifier le niveau que vous voulez retourner avec un label explicite en utilisant return@lambda
.
lambdaA {
+ lambdaB {
+ lambdaC {
+ val randomInt = Random.nextInt(0, 100)
+ if (randomInt > 50) return@lambdaC else return@lambdaB
+ }
+ printf("In lambdaB")
+ }
+}
+
Ce code exécute un autre exempleopen in new window.
CustomerTest.http
POST http://127.0.0.1:8080/customer
+Content-Type : application/json
+
+{
+ "id" : "100",
+ "firstName" : "Jane",
+ "lastName" : "Smith",
+ "email" : "jane.smith@company.com"
+}
+
+
+###
+POST http://127.0.0.1:8080/customer
+Content-Type : application/json
+
+{
+ "id" : "200",
+ "firstName" : "John",
+ "lastName" : "Smith",
+ "email" : "john.smith@company.com"
+}
+
+###
+POST http://127.0.0.1:8080/customer
+Content-Type : application/json
+
+{
+ "id" : "300",
+ "firstName" : "Mary",
+ "lastName" : "Smith",
+ "email" : "mary.smith@company.com"
+}
+
+
+###
+GET http://127.0.0.1:8080/customer
+Accept : application/json
+
+###
+GET http://127.0.0.1:8080/customer/200
+Accepte : application/json
+
+###
+GET http://127.0.0.1:8080/customer/500
+Accepte : application/json
+
+###
+DELETE http://127.0.0.1:8080/customer/100
+
+###
+DELETE http://127.0.0.1:8080/customer/500
+
Cette page contient des étapes détailléesopen in new window
Grâce à Kotlin/JS, nous pouvons écrire des applications qui ciblent nodejs en utilisant Kotlin.
On peut même importer des librairies npm à condition de déclarer les API JS que l'on va utiliser en Kotlin. C'est ce qu'on appelle une déclaration externe (vous pouvez la considérer comme un équivalent des définitions de type de TypeScript) qui déclare les symboles auxquels nous voulons accéder en Kotlin grâce aux annotations @JsModuleopen in new window et @JsNonModuleopen in new window. Définir de telles déclarations externes peut s'avérer fastidieux et il ne semble pas y avoir de générateur automatique officiel et stable (dukatopen in new window a été supprimé dans kotlin 1.8.20). Dans ce cas, nous avons deux options, soit écrire la déclaration externe nous-même, soit l'importer en tant que dépendance si elle est disponible.
Heureusement pour nous, le prochain TP utilise la librairie Express pour laquelle nous pouvons trouver une déclaration de type externe.
- Dans IntelliJ, créer un nouveau projet nodejs
- Une fois le projet chargé, éditer build.gradle.ts comme suit :
implementation(npm("express", "> 4.0.0 < 5.0.0"));
+implementation("dev.chriskrueger:kotlin-express:1.2.0");
+
- Modifiez main.kt comme suit. Cela crée un serveur API REST qui écoute le port 3000 et fournit une route GET /hello.
data class Message(val id : Int, val message : String)
+
+fun main() {
+ val messages = listOf(Message(0, "I love Kotlin/JS"))
+ val app = express.Express()
+ app.get("/hello") { req, res ->
+ res.send(messages)
+ }
+
+ app.listen(3000) {
+ console.log("server start at port 3000")
+ }
+}
+
- Exécutez la tâche
nodeRun
depuis votre IDE ou depuis la ligne de commande (si vous avez installé Gradle). - Si vous rencontrez une erreur avec Yarn lock, exécutez la tâche
kotlinUpgradeYarnLock
puis réessayez.
- Ajouter des routes en POST, PUT et DELETE
- En ce qui concerne le corps du POST, Express positionne
req.body
à undefined
à moins que nous ne spécifions un body parser.
Spring est un framework célèbre pour le développement d'applications côté serveur : API REST, pages web générées par le serveur, microservices, etc. Il s'appuie sur l'écosystème Java pour la compilation et l'exécution, ce qui le rend compatible avec Kotlin. Mieux encore, Spring supporte officiellement Kotlin. On peut même démarrer un nouveau projet avec Kotlin et Gradle-Kotlin. Dans la prochaine section, nous utiliserons ce projet pour recréer notre API REST plus haut avec Spring.
- Créez un projet sur start.spring.io (aussi appelé Spring initializr)open in new window avec les dépendances suivantes : Spring Web et Spring Boot DevTools.
- Choisissez Kotlin comme langage et Kotlin-Grade comme gestionnaire de projet.
- Ajoutez les dépendances suivantes : Spring Web et Spring Boot DevTools.
- Cliquez sur Generate. Téléchargez l'archive, décompressez-la et ouvrez le projet avec IntelliJ (de préférence) ou VSCode.
- Vérifiez que la partie plugins
build.gradle.kts
utilise la dernière version de Kotlin. Voici à quoi cela devrait ressembler avec Kotlin 1.8.10 :
plugins {
+ id("org.springframework.boot") version "3.0.4"
+ id("io.spring.dependency-management") version "1.1.0"
+ kotlin("jvm") version "1.8.10"
+ kotlin("plugin.spring") version "1.8.10"
+}
+
- Créez la
data class Customer
dans le package model
(sans l'annotation @Serializable
). - Créez un paquetage
controller
qui contient une classe CustomerController
qui fournit un CRUD en utilisant une liste globale. - Vous pouvez trouver un squelette ci-dessous.
- 💡 Dans Spring, les contrôleurs Rest servent de routes Ktor, où un contrôleur définit une ressource REST.
- Définissez les mêmes routes que dans le TP précédent.
- Démarrez le serveur de l'API REST en exécutant :
- Sur Powershell :
.\gradlew.bat bootRun
- Tout shell Unix :
.\gradlew bootRun
- Ou bien, vérifiez si votre IDE fournit déjà des configurations d'exécution pour les projets Spring Boot.
- Veuillez tester les routes avec un client REST. Vous pouvez trouver des fichiers http ici au format JetBrainsopen in new window ou au format de l'extension REST Client de VSCodeopen in new window
CustomerController.kt
val store = mutableListOf<Customer>()
+
+@RestController
+@RequestMapping("/customer")
+class CustomerController {
+ @GetMapping
+ fun getAll() = store
+
+ @GetMapping("{id}")
+ fun getById(@PathVariable id : String) { }
+
+ @PostMapping
+ fun addOne(@RequestBody customer : Customer) { }
+
+ @DeleteMapping("{id}")
+ fun deleteOne(@PathVariable id : String) { }
+}
+
Allons un peu plus loin en stockant des données dans une base de données et en écrivant quelques tests.
Nous utiliserons la base de données en mémoire H2 pour des raisons de simplicité, puisqu'elle ne nécessite pas de serveur pour fonctionner. Les classes seront mappées aux tables de la base de données avec des annotations JPA. L'API de base de données que nous utiliserons s'appelle JPARepository
. C'est une API légère qui fournit des fonctionnalités CRUD communes à partir d'une simple une interface.
- Créez un nouveau projet Spring en utilisant Spring initializropen in new window avec Kotlin et les dépendances suivantes : Spring Data JPA, H2 Database, Spring Boot DevTools, Spring Web.
- Ouvrez le projet et ajoutez cette classe dans le package
model
@Entity class Product(@Id @GeneratedValue var id : Long ? = null, var name : String, var price : Int)
. Ceci définit la classe ainsi que les annotations JPA minimales (@Entity
, @Id
et @GeneratedValue
) pour générer la table correspondante. - Dans le package
repository
, déclarez l'interface ProductRepository
comme suit interface ProductRepository : JpaRepository<Produit, Long>
. C'est suffisant pour que Spring génère une implémentation avec des caractéristiques communes comme nous le verrons plus tard. - Ensuite, créez une classe
ProductService
qui contiendra la logique métier. En termes d'architecture, le contrôleur appelle un service qui, à son tour, s'appuie sur d'autres services ou référentiels.
ProductService.kt
@Service
+class ProductService(@Autowired val productRepository: ProductRepository) {
+ fun getAll() = productRepository.findAll()
+
+
+ fun getById(id: Long) = productRepository.findByIdOrNull(id)
+}
+
- Dans le package controller, créez une classe
ProductController
qui est mappée à /product
et injectée avec @Autowired
. Répondez à @Get
comme suit.
ProductController.kt
@RestController
+@RequestMapping("/product")
+class ProductController(@Autowired val productService : ProductService) {
+ @GetMapping fun getAll() = productService.getAll()
+
+ @GetMapping("{id}")
+ fun getById(@PathVariable id : Long) =
+ productService.getById(id) ? : throw ResponseStatusException(HttpStatus.NOT_FOUND)
+}
+
Kotlin rend getById(@PathVariable id : Long) plus concis
L'opérateur Elvis ?:
permet de simplifier le code. Voici une version plus longue en guise de référence.
@GetMapping("{id}")
+fun getById(@PathVariable id : Long) : Produit {
+ val product = productService.getById(id)
+ if (product != null){
+ return product
+ }
+ throw ResponseStatusException(HttpStatus.NOT_FOUND)
+}
+
En outre, Spring fournit @ControllerAdvice
pour modifier le message d'exception. Vous pouvez voir un [exemple ici] (https://spring.io/guides/tutorials/rest/).
- Exécutons le projet. Avant de lancer le projet, nous devons ajouter un plugin qui permet aux classes Kotlin de générer un constructeur par défaut
id("org.jetbrains.kotlin.plugin.jpa") version "1.8.10"
. Les plugins devraient ressembler à ce qui suit :
plugins {
+ id("org.jetbrains.kotlin.plugin.jpa") version "1.8.10"
+ id("org.springframework.boot") version "3.0.4"
+ id("io.spring.dependency-management") version "1.1.0"
+ kotlin("jvm") version "1.8.10"
+ kotlin("plugin.spring") version "1.8.10"
+}
+
+
- En guise d'exercice, implémentez ces routes : POST d'un seul produit, DELETE par id (
/produit/{id}
) et GET par id (/produit/{id}
). - Indice :
ProductController
fournit déjà les méthodes nécessaires.
- Appelez les différents points de terminaison avec un client REST.
- Tester votre API Rest avec un client HTTP
Les frameworks Spring permettent d'effectuer différents types de tests en fournissant différentes classes dès le départ :
- Tests unitaires/de composants des services et de l'API REST. Cela se fait par le biais d'utilitaires de bouchonnage tels que
MockMVC
. - Tests d'intégration de l'API REST en utilisant
TestRestTemplate
. Dans ce cas, un serveur complet est exécuté et testé.
La plupart des classes fournies par Spring, si ce n'est toutes, offrent une syntaxe élégante pour les développeurs Java. Certaines d'entre elles vont plus loin en tirant parti des caractéristiques spécifiques de Kotlin. Dans ce qui suit, nous allons nous concentrer sur les parties qui fournissent des DSLs Kotlin, à savoir le test unitaire de l'API REST avec MockMVC
.
- Créer une classe de test
ProductControllerUnitTests
avec le contenu initial ci-dessous. MockMvc
permet de tester unitairement l'API REST. L'annotation @AutoConfigureMockMvc
permet à Spring de la configurer automatiquement.
@SpringBootTest
+@AutoConfigureMockMvc
+classe ProductControllerTests(
+ @Autowired val mockMvc : MockMvc,
+ @Autowired val productRepository : ProductRepository) {
+
+ @BeforeEach
+ fun reset(){
+ productRepository.deleteAll()
+ }
+}
+
- Ajoutez les deux tests ci-dessous. Le premier utilise une approche classique tandis que le second tire parti des capacités du DSL de Kotlin. De plus, nous utilisons une chaîne littérale plus lisible.
@Test
+fun testWithClassicApproach(){
+ mockMvc.perform(get("/product"))
+ .andExpect(status().isOk)
+ .andExpect(content().string(containsString("[]")))
+}
+
@Test
+fun `test GET a single product`() {
+ mockMvc.get("/product/1").andExpect {
+ status { isOk() }
+ jsonPath("$.name") { value("A") }
+ jsonPath("$.price") { value(1) }
+ content { contentType(MediaType.APPLICATION_JSON) }
+ }
+}
+
- En guise d'exercice, écrire des tests pour les autres points d'accès.
Le constructeur de requêtes de JpaRepository
Les repository Spring implémentent des requêtes basées sur le nom de leurs méthodes. Par exemple, pour obtenir tous les produits triés par nom, nous pouvons ajouter cette méthode à l'interface.
interface ProductRepository : JpaRepository<Produit, Long> {
+ fun findAllByOrderByNameAsc() : List<Produit> ;
+}
+
La [documentation officielle] (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation) fournit des explications et des exemples plus détaillés.
Ces tutoriels officiels vont encore plus loin :
+
+
+
diff --git a/fr/front-development/index.html b/fr/front-development/index.html
new file mode 100644
index 00000000..50cf0ecb
--- /dev/null
+++ b/fr/front-development/index.html
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+ 📚 Développement frontend | Apprendre Kotlin
+
+
+
+
+
+ Kotlin supporte une large sélection de frameworks frontaux sur toutes les plateformes : mobile, desktop et web. Vous trouverez ci-dessous un aperçu des possibilités que vous pouvez faire directement à partir d'IntelliJ :
- Côté bureau
- Sur le web
- Sur les mobiles
Comme nous pouvons le voir, Kotlin propose plusieurs options. L'option la plus séduisante en terme de partage de code est Compose Multiplatform. Ceci est possible notamment grâce à KMP
- KMP (Kotlin Multiplatform) permet de partager une base de code unique sur plusieurs cibles.
- KMPopen in new window s'appuie sur Kotlin native et d'autres fonctionnalités de Kotlin pour aider les développeurs à créer des projets destinés à plusieurs plates-formes en utilisant une base de code Kotlin commune.
KMP- De nombreuses combinaisons de cibles et de cas d'utilisation sont possibles :
- KMM est une ancienne dénomination qui est dépréciée.
Dans la suite de ce chapitre, nous explorerons les différentes possibilités individuellement et on fera un projet KMP dans le chapitre suivant.
- Kotlin/JS peut également cibler le web et même utiliser des frameworks web (tels que react) dans Kolitn.
- Kotlin WASM est une autre possibilité de cibler le web, mais il génère WASM au lieu de code JS pur.
- Il peut être utilisé par exemple pour développer des bibliothèques à forte intensité de calcul.
- Nous pourrons peut-être faire encore plus à l'avenir grâce à l'évolution de toutes ces technologies (Kotlin, WASM et Kotlin/WASM). - Par exemple, [WASI] (https://wasi.dev/) permet à WASM de communiquer avec le système d'exploitation. - Cela signifie que je pourrais voir des projets Kotlin/WASM à l'avenir qui peuvent cibler à la fois le navigateur et le système d'exploitation.
- Continuons à observer 😄.
- Les assistants de création de projet Kotlin/WASM et Kotlin/JS sur IntelliJ fonctionnent de manière similaire:
- L'IDE génère un fichier Kotlin qui sera compilé par la suite en WASM et/ou JS. Kotlin/JS ne génère que du JS tandis que Kotin/WASM génère à la fois du JS et du WASM.
- Dans les deux cas, le point d'entrée du code généré est un fichier JS appelé nom_du_module.js.
- L'IDE génère également dans le dossier des ressources un fichier index.html dont le but est de charger le JS généré (le fichier nom_du_module.js).
- La tâche
wasmBrowserDevelopmentRun
ou jsWasmBrowserDevelopmentRun
lancera un serveur local qui hébergera à la fois les fichiers index.html et les fichiers JS et WASM générés.
- Créons une application Kotlin/WASM. Tout d'abord, activez l'assistant Kotlin/WASM en activant kotlin.wasm.wizard dans le registre d'IntelliJ (ouvrez le registre en appuyant deux fois sur shift et en tapant "registry" dans la boîte de recherche). Alternativement, clonez ce projetopen in new window.

- Vérifiez qu'on est sur la dernière version de Kotlin dans build.gradle.kts (l'assistant peut le configurer à une version antérieure).
- Ouvrez src/wasmMain/kotlin/sample.kt et cliquez sur le bouton lancer qui apparaît à côté de la fonction
main
. - Si la compilation échoue parce que l'IDE a utilisé la mauvaise tâche gradle, veuillez la changer en
wasmBrowserDevelopmentRun
et essayez de l'exécuter à nouveau.


- Le serveur de développement devrait démarrer et vous pouvez ouvrir votre application web sur http://localhost:8080/open in new window
- ⚠️ Il se peut que vous deviez activer certains drapeaux sur votre navigateur pour que l'application fonctionne. Si vous voyez une page blanche, veuillez lire les journaux du navigateur pour vérifier les instructions.

- Le fichier wasm généré est disponible dans build/js/packages/nom_du_projet/kotlin
- WASM étant un format binaire, nous devons d'abord le convertir au format texte.
- Nous pouvons soit installer [WABT (The WebAssembly Binary Toolkit ou wabbit)] (https://github.com/WebAssembly/wabt) et utiliser l'outil wasm2wattool
wasm2wat --enable-all -v .\kotlin-wasm-demo-wasm.wasm -o wasm.wat
, - ou utiliser un convertisseur en ligne comme celui-ciopen in new window
- ❗ Cependant, je n'ai pas réussi à le faire fonctionner
L'assistant Kotlin/JS crée une application très similaire à celle de Kotlin/WASM. Dans un prochain PW, nous créerons une application complète avec Ktor et Kotlin/JS.
Compose multiplatformopen in new window est une famille de frameworks d'interface utilisateur déclaratifs pour Android (Jetpack Compose), le bureau (Compose Desktop) et le web (Compose Web). Il dispose d'un support expérimental pour iOS et Web Canvas.
Compose multiplatform vs Jetpack Compose
Bien que très similaire, Compose multiplatform est différent de Jetpack Compose car ce dernier n'est compatible qu'avec Android. Google fournit un JetPack compose tutorialopen in new window pour le développement Android.
Compose Web vs Compose for Web Canvas
- La surface de l'API de Compose Web est différente des autres cibles de Compose car elle travaille directement avec le DOM.
- Compose for Web Canvas a la même surface d'API que celle du Desktop, Android et iOS car il dessine sur un Canvas et ne manipule pas le DOM.
Cela signifie que le premier a un meilleur support web et que le second a plus de code réutilisable.
- Créez un nouveau projet IntelliJ -> Compose Multiplaform.
- Choisissez "Single platform" -> "Web" et remplissez les autres champs.
- Choisissez Finish
- IntelliJ peut prendre un certain temps pour préparer le projet et peut demander d'installer des plugins supplémentaires.
- Lancez le serveur de développement de l'application web à l'aide de la commande
./gradlew jsBrowserRun --continuous
. - Modifiez
Main.kt
comme suit et lancez l'application. - Ouvrez cette adresse :
localhost:8080
.
fun main() {
+ renderComposable(rootElementId = "root") {
+ Div({ style { padding(25.px) } }) {
+ var expanded by remember { mutableStateOf(false) }
+ Button(
+ attrs = {
+ onClick { expanded = !expanded }
+ }
+ ) { Text("Cliquez sur moi") }
+ Div({ style { display(if (expanded) DisplayStyle.Block else DisplayStyle.None) } }) {
+ Text("Cliquez sur moi !")
+ }
+ }
+ }
+}
+
compose multiplatform demo- Créez un nouveau projet sur IntelliJ -> Compose Multiplatform.
- Choisissez "multiple platforms" et remplissez les autres champs. Choisissez ensuite Finish.
- IntelliJ commence à préparer le projet et peut demander l'installation de plugins.
- Une fois le projet prêt, lancez l'application Android en utilisant le bouton vert run.
- Lancez l'application desktop en exécutant la fonction principale du projet desktop (qui devrait se trouver dans
Main.kt
). - Modifiez
App.kt
dans le projet principal comme suit et lancez l'application.
@Composable
+fun App() {
+ val platformName = getPlatformName()
+ Card {
+ var expanded by remember { mutableStateOf(false) }
+ Column(Modifier.clickable { expanded = !expanded }) {
+ Texte(
+ text="Cliquez sur moi !",
+ style = MaterialTheme.typography.h2
+ )
+ AnimatedVisibility(expanded){
+ Texte(
+ text = "Bonjour, ${platformName} 🎊",
+ style = MaterialTheme.typography.h1
+ )
+ }
+ }
+ }
+}
+
compose multiplatform demo
+
+
+
diff --git a/fr/index.html b/fr/index.html
new file mode 100644
index 00000000..fdc07c3e
--- /dev/null
+++ b/fr/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Apprendre Kotlin
+
+
+
+
+
+
+
+
+
diff --git a/fr/kotlin-features-advanced/index.html b/fr/kotlin-features-advanced/index.html
new file mode 100644
index 00000000..0ac86956
--- /dev/null
+++ b/fr/kotlin-features-advanced/index.html
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+ 📚 Fonctionnalités avancées de Kotlin | Apprendre Kotlin
+
+
+
+
+
+ Kotlin permet de déléguer le getter et le setter d'une propriété à un autre objet, appelé délégué. C'est une classe qui définit les méthodes getValue
et setValue
.
Kotlin fournit des délégués standard tels que des propriétés paresseuses et des propriétés observables.
▶️ this codeopen in new window illustrates delegate properties.
Kotlin fournit un modèle de concurrence de haut niveau appelé Coroutines. Le développeur peut déléguer la gestion des threads au compilateur et à l'exécution et utiliser des constructions de niveau supérieur aux threads pour exprimer des opérations asynchrones.
Les coroutines de Kotlin tournent autour de ces concepts :
- Une coroutine est une instance de calcul suspendable.
- Kotlin a de nombreuses méthodes pour créer une coroutine telle que
launch
.
- Une coroutine doit exister dans une portée de coroutine.
- Par exemple,
runBlocking
crée une portée de coroutine dans laquelle les coroutines peuvent être lancées.
- Une coroutine peut exécuter des fonctions de suspension qui peuvent suspendre la coroutine mais ne bloquent pas le thread.
- Par exemple : le
delay
suspend la coroutine mais ne bloque pas le thread sur lequel elle s'exécute. - Les fonctions de suspension sont des opérations qui peuvent prendre du temps telles que les requêtes http et les appels au système de fichiers.
- Le qualificateur
suspend
définit une fonction de suspension. Il s'exécute dans une coroutine et peut appeler d'autres fonctions de suspension. Flow
permet de générer une liste de valeurs asynchrones.Deferred
et Channel
transfèrent respectivement une valeur unique et un flux de valeurs entre coroutines.
▶️ this codeopen in new window show how to create a coroutine and suspend function and how to use them.
▶️ this codeopen in new window illustrated flows.
▶️ this codeopen in new window illustrates channels and deferred.
Comme vu précédemment, les extensions de fonctions ajoute du comportement à des classes existantes sans utiliser l'héritage. À l'intérieur de la définition de l'extension de fonction, nous pouvons référencer implicitement le récepteur d'extension (this
).
fun String.countCharacters() = length
+println("hello".countCharacters())
+
Nous pouvons définir cette extension avec une fonction littérale (ou lambda) au lieu d'une fonction classique (déclarée avec fun
).
var extFn: String.() -> Int
+extFn = { length }
+println("hello".extFn())
+println(extFn("hello"))
+
extFn
est une fonction littérale (lambda) qui a accès au récepteur (this
). C'est pourquoi on l'appelle une fonction littérale avec récepteur.
extFn("hello")
ou extFn("hello")
appelle l'extension comme prévu par les fonctions d'extension.
Le type d'une fonction littérale avec récepteur est funName: ReceiverType.(arg1Type, arg2Type, etc.) -> ReturnType
et est appelé avec funName(receiverValue, arg1Value, etc.)
ou receiverValue.funName(arg1Value, etc.) .)
. Cependant, ce n'est pas l'aspect le plus intéressant.
La partie importante est extFn = { length }
qui peut être placée comme argument de fonction dans une fonction d'ordre supérieur. Le développeur qui appelle la fonction d'ordre supérieur doit définir extFn
, qui à son tour a accès au récepteur. Cela permet un style de programmation assez intéressant.
▶️ ce codeopen in new window montre un example.
Les Type-safe buildersopen in new window combinent les monteurs bien nommées et les fonctions littérales avec récepteur pour créer des monteur avec un typage statique et sécurisé. La syntaxe particulière possible avec technique permet de définir une sorte de sous-langage aussi appelé DSL (domain specific language).
Ce code montreopen in new window un type-safe builder basique.
Kotlin docsopen in new window fournit un exemple plus avancé d'un monteur de documents HTML.
- open the java-integration-exercise projects in the materials folder.
- Have a look at the Java class we provided you in the
src/main/java/com/worldline/learning/kotlin/java2kotlin
package. (yes, that's the Pokemon class) - Convert that Java class in Kotlin using IntelliJ's awesome copy-pasta tool! (just copy paste the java code in a kotlin file, one is provided at
src/main/kotlin/com/worldline/learning/kotlin/java2kotlin
) - Have a look at the generated Kotlin code, and note the major differences you spot!
+
+
+
diff --git a/fr/kotlin-features/index.html b/fr/kotlin-features/index.html
new file mode 100644
index 00000000..3d513c8c
--- /dev/null
+++ b/fr/kotlin-features/index.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+ 📚 Fonctionnalités du langage Kotlin | Apprendre Kotlin
+
+
+
+
+
+ Kotlin est un langage qui support les paradigmes orienté objet et fonctionnel. Ce chapitre couvre les caractéristiques basiques et intermédiaires. Le chapitre suivant couvrira les fonctionnalités avancées.
- Kotlin est typé statiquement et prend en charge le typage implicite.
- Typage statique : les types ne peuvent pas changer à l'exécution (c'est l'inverse du typage dynamique).
- Typage implicite : le compilateur peut inférer le type tant que c'est possible.
var
crée des variables ré-assignables.val
crée des variables qu'on ne peut plus ré-assigner.- Les points-virgules sont facultatifs.
- Kotlin prend en charge la déclaration de haut niveau des variables et des fonctions (elles peuvent être déclarées en dehors des classes).
- L'interpolation de chaîne de caractères est disponible avec cette syntaxe
${expression}
. if
et when
sont des expressions (elles peuvent renvoyer une valeur). when
est équivalent à l'instruction switch
des autres langages- L'opérateur ternaire n'est pas disponible. L'expression
if
le remplace.
- for-each est le seul type de boucle for disponible.
- La programmation orientée objet est prise en charge comme en Java avec quelques fonctionnalités supplémentaires telles que les extensions.
- Le compilateur prend en charge la Null Safety. Il permet d'écrire du code sans erreur de pointeur nulle vérifié à la compilation.
- La programmation fonctionnelle est prise en charge (fonctions d'ordre supérieur et fonctions en tant qu'éléments de 1ère classe, etc.).
Utiliser val par défaut
Utiliser var
uniquement si vous réassignez une variable ou argument.
▶️ this codeopen in new window highlights the above features.
Dans cette section, les termes 'argument' et 'paramètre' sont utilisés de manière interchangeable.
Les fonctions de Kotlin ont les caractéristiques suivantes :
- Déclaration :
fun functionName(arg1 : type1 = defaultvalue1, ...) : retunrType
. - Appeler une fonction en passant la valeur dans l'ordre de déclaration.
- Utilisez des étiquettes d'argument pour plus de clarté, cependant, cela permet également un classement arbitraire des arguments.
- Les arguments optionnels ont une valeur par défaut et peuvent être omis lors de l'appel.
- Les fonctions sont des éléments de première classe ou citoyens : elles peuvent être affectées à une variable, passées en tant que paramètre de fonction ou renvoyées par une fonction.
- 💡 Une fonction qui prend une fonction comme argument ou en renvoie une est une fonction d'ordre supérieur.
- Un type de fonction peut être exprimé comme suit :
(typeOfParam1, typeOfParam2, etc) -> returnType
(Le type de retour vide est Unit
). - Les fonctions anonymes utilisent la syntaxe suivante
{ argName1, argName2, etc. -> // code }
- Aussi appelées fonctions lambda ou fonctions littérales
- Le dernier argument de la fonction peut être mis après la fermeture après la parenthèse fermante
compute(9, 5) { x, y -> x * y }
▶️ Ce codeopen in new window illustre les fonctions en Kotlin.
La prochaine section abordera le null safety.
null safety est une fonctionnalité du compilateur qui élimine la fameuse Null pointer exception ou npe. En effet, le compilateur signale des erreurs et des avertissements lorsque nous manipulons des types nullables (également appelées types optionnels) dès qu'il y a un risque de npe à l'exécution. Ainsi, afin de mettre Voici une liste des fonctionnalités de sécurité null fournies par Kotlin :
- Tous les types ne sont pas nullables par défaut ; nous ne pouvons pas affecter
null
à une variable ou à un argument. - Par exemple, ce code échoue
var s: String = null
.
- Un type peut être rendu nullable en le suffixant avec un ?. Par exemple :
var s : chaîne ? = nul
. - Kotlin interdit d'appeler une méthode ou une propriété de type non nullable, sauf si l'on fait l'une de ces possibilités :
- Utilisez le chaînage optionnel avec le suffixe ?.
- Fournissez une valeur par défaut avec l'opérateur elvis ?:.
- Smart-cast le nullable dans un non-nullable.
- Utilisez l'opérateur !! qui élimine les vérifications du compilateur. Cela ne devrait jamais être utilisé.
Ne jamais déballer avec !!
Car cela équivaut à désactiver la null safety. Utilisez les autres possibilités à la place.
▶️ ce codeopen in new window illustrate la null safety et les types optionnels.
La classe `Optional` de Java ne fournit aucun protection à la compilation
Ce code lance une npe en Java: Optional<String> s = null; s.isPresent();
. Le compilateur Java (au moins à la version version 17) ne propose pas d'équivalent à ce que propose Kotlin comme le smart casting.
Les énumérations permettent de travailler avec un groupe de valeurs de façon cadrée. Contrairement aux énumérations Java, les énumérations Kotlin sont des classesopen in new window. Les enum class
de Kotlin fournissent ces fonctionnalités :
- Les expressions when prennent en charge les énumérations.
- Une
enum class
peut définir des méthodes et implémenter des interfaces mais elle ne peut pas dériver d'une classe. - Il existe des méthodes pour lister les constantes d'une
enum class
. - Chaque constante d'une énumération a des propriétés pour obtenir son nom et sa position (en commençant par 0).
▶️ ce codeopen in new window illustres les enum en Kotlin de façon succincte. Veuillez consulter la documentation officielle pour aller plus loinopen in new window.
Veuillez cliquer sur le lien pour consulter l'énoncéopen in new window
Déplier pour consulter la solution
Solutionopen in new window
Veuillez cliquer sur le lien pour consulter l'énoncéopen in new window
Déplier pour consulter la solution
Solutionopen in new window
Kotlin permet d'écrire du code Orienté Object concis grâce aux caractéristiques suivantes :
- Concepts disponibles : classes, héritage, interfaces et classes abstraites.
- Prise en charge possée des propriétés : les getters et les setters sont automatiquement implémentés.
- On peut les personnaliser les accesseurs en définissant les fonctions
get()
et set(value)
à côté de la déclaration de la propriété.
- Les arguments du constructeur sont définis à côté du nom de la classe
class ClassName(arg1, atg2, )
- Préfixer les arguments d'un constructeur avec
val
ou var
en fait une propriété (val
la rend non ré-assignable). - Le nom du constructeur est
init
et ne nécessite pas de paramètres. - Le compilateur vérifie que toutes les propriétés non nullables sont initialisées à la fin du constructeur.
- ⚠️ Le compilateur ne vérifie pas l'initialisation des propriétés
lateinit
. Ainsi, y accéder avant alors qu'elles ne sont pas initialisés provoque une exception.
- Une classe doit être préfixée avec
open
pour permettre l'héritage. - Kotlin utilise le niveau d'accès
public
par défaut. - L'opérateur d'égalité
==
appelle implicitement la méthode equals()
(contrairement à Java qui utilise l'égalité de référence). - Un objet compagnon contient des méthodes et des propriétés statiques.
- Les extensions ajoutent des fonctions et des propriétés aux classes existantes.
- 💡 Ils remplacent l'héritage dans de nombreuses situations.
- Par exemple, nous pouvons ajouter des fonctions à la classe String au lieu de créer une nouvelle classe
StringUtils
.
- Les classes et interfaces scellées ne peuvent pas être étendues ou implémentées par des tiers.
Ne définir les accesseurs que si vous avez un comportement personnalisé
Kotlin prend en charge les propriétés de façon plus poussée que Java et permet d'ajouter des accesseurs ultérieurement sans refactoriser le code qui appelle ces propriétés. Ainsi, par défaut, il suffit de définir le nom des propriétés sans accesseurs et on peut les utiliser directement.
▶️ ce codeopen in new window illustre la POO en Kotlin.
Ce sont des classes qui implémentent des méthodes communesopen in new window:
equals()
, hashCode()
, copy()
et toString()
componentN()
qui est une syntaxe alternative pour récupérer les propriétés.
Cependant, les data class ont des restrictionsopen in new window:
- Le constructeur principal doit avoir au moins un paramètre.
- Tous les paramètres du constructeur principal doivent être marqués comme
val
ou var
. - Une Data class ne peut pas être abstraite, ouverte à l'héritage, scellée ou interne (💡 mais des extensions sont possibles).
▶️ ce codeopen in new window illustrate les data class.
La programmation fonctionnelle s'articule autour de ces conceptsopen in new window : fonctions pures, récursivité, transparence référentielle, variables immuables, fonctions en tant que citoyens de première classe et fonctions d'ordre supérieur.
Expliquons brièvement ces concepts :
- Les variables immuables signifient qu'on ne peut pas changer la valeur d'une variable ou ses propriétés une fois qu'elle a été créée. Si nous voulons le faire, nous devons créer une nouvelle instance avec la nouvelle valeur.
- Les fonctions pures sont des fonctions qui n'ont pas d'effets secondaires et renverront donc toujours la même sortie étant donné la même entrée.
- Les fonctions sont des citoyennes de première classe : elles peuvent être affectées à une variable ou utilisées dans des fonctions d'ordre supérieur (passées en tant qu'un argument de fonction ou retournées dans un fonction).
- Transparence référentielleopen in new window : signifie qu'une expression peut être remplacée par son résultat sans modifier le comportement du programme.
💡 Les langages fonctionnels purs fournissent ces fonctionnalités de manière native et les appliquent (au moment de la construction).
Kotlin n'est pas un langage fonctionnel pur mais il prend en charge certaines fonctionnalités. En effet, Kotlin ne sait pas dire si une fonction est pures ou non, mais il fournit des collections immuables via la bibliothèque kotlinx.collections.immutableopen in new window pour nous aider à manipuler des données immuables.
`listOf` génère des listes en lecture seule, mais qui sont mutables
Une liste en lecture seuleopen in new window ne peut pas ajouter ou supprimer des éléments, mais elle peut modifier les données sous-jacentes.
@Test
+fun givenReadOnlyList_whenCastToMutableList_checkNewElementsAdded(){
+ val list: List<String> = listOf("This", "Is", "Totally", "Immutable")
+ (list as MutableList<String>)[2] = "Not"
+ assertEquals(listOf("This", "Is", "Not", "Immutable"), list)
+}
+
La librairie Arrow-ktopen in new window permet d'aller encore plus loin en développement fonctionnel.
La programmation déclarative est un style célèbre dans la programmation fonctionnelle. Il consiste à écrire du code sous la forme d'un enchaînement d'appels de fonction dans ce style val result = f(x).g(y). ...
. Les fonctions d'ordre supérieur remplacent de nombreuses situations où nous utiliserions des boucles. Cela favorise le code lisible qui est facile à déboguer et à maintenir.
▶️ ce codeopen in new window montre comment manipuler une liste avec la programmation déclarative.
- Kotlin is designed with Java interoperability in mind.
- Kotlin code may require some annotations to be called from Java.
- It is possible to mix Java and Kotlin in the same project.
- JetBrain's IntelliJ and Android Studio can convert to Kotlin when pasting java code.
- Kotlin generates Java records by annotating a data class with
@JvmRecord
and targeting JVM 16, among other requirement listed hereopen in new window. - It is much more easier and natural to call Java from Kotlin.
- For example: Java accessors are converted to Kotlin properties.
▶️ this codeopen in new window shows how to convert a Kotlin List
to a Java ArrayList
.
The official documentation provides exhaustive documentation on Kotlin and JVM integrationopen in new window
Voir l'exerciceopen in new window
Solution(s)
Solutionopen in new window
Exerciceopen in new window
Solutions(s)
Solutionopen in new window
Exerciceopen in new window
Solution(s)
Solutionopen in new window
Exerciceopen in new window
solution(s)
Solutionopen in new window
Exerciceopen in new window
Solution(s)
Solutionopen in new window
+
+
+
diff --git a/fr/other-technologies/index.html b/fr/other-technologies/index.html
new file mode 100644
index 00000000..1241b4ba
--- /dev/null
+++ b/fr/other-technologies/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ 🛠 Construisons une app multiplateforme ! | Apprendre Kotlin
+
+
+
+
+
+
+
+
+
diff --git a/fr/presentation/index.html b/fr/presentation/index.html
new file mode 100644
index 00000000..e417a7c9
--- /dev/null
+++ b/fr/presentation/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ 🚀 Présentation de Kotlin | Apprendre Kotlin
+
+
+
+
+
+ Kotlin est un langage de programmation moderne développé par JetBrains.
- Kotlin est capable de compiler vers différentes cibles : la JVM, JS, Android, iOS, Les OS de bureau, etc.
- Pris en charge officiellement par de grands frameworks back-end, tels que Spring et Quarkus,
- Langage préféré pour le développement Android,
- Kotlin Native compile en code natif,
- Kotlin peut être considéré comme un langage fullstack
- Interopérable avec Java.
- Fournit des fonctionnalités modernes telles que la null safety à la compilation.
- Prend en charge la programmation orientée objet et la programmation fonctionnelle.
- Processus d'évolution open source: Kotlin KEEPopen in new window.
- 15 février 2016 : sortie de Kotlin 1.0.
- 4 janvier 2017 : Spring a introduit le support de Kotlin dans Spring 5.
- Lors de Google I/O 2017, Google a annoncé une prise en charge officielle de Kotlin sur Android.
- Le 7 mai 2019, Google a annoncé que Kotlin était le langage préféré des développeurs d'applications Android.
- Juin 2022 : Kotlin 1.7 est sorti avec la version du nouveau compilateur K2.
Source : Amyra Sheldonopen in new window
- En 2022, Kotlin est utilisé par 7,8 % des experts de l'industrie.
- Selon StackOverflow, Kotlin était le 4ème langage le plus apprécié en 2020 avec 62,9 % des votes.
- Kotlin est désormais répertorié parmi les 20 meilleurs langages de programmation par Redmonk.
- Kotlin fait partie des 3 principaux langages vers lesquels la plupart des entreprises prévoient de migrer leurs applications en 2022.
- Kotlin est utilisé par des entreprises mondiales telles que Google, Netflix, Amazon, Trello, etc.
- Plusieurs entreprises dont Googleopen in new window, Pinterest et Uber ont migré leurs applications de Java vers Kotlin.
kotlin utilisé pour quoi ?Veuillez trouver plus de statiques ici :
Voici quelques arguments qui motivent le passage de Java (version 17 LTS au moment de la rédaction) à Kotlin.
- Kotlin prend en charge plus de cibles que Java.
- Kotlin protège des références null à la compilation (les
Optional
Java sont ne sont pas de protections à la compilation). - Les chaînes de caractères Kotlin prennent en charge l'interpolation.
- Les fonctionnalités de programmation fonctionnelle de Kotlin sont meilleures. Il permet même de définir des constructeurs et des DSL (Domain Specific Language) dont le typage est sécurisé (type-safe).
- Kotlin peut être mélangé avec du code Java, facilitant ainsi le processus de migration.
Vous pouvez lire plus d'arguments dans ces articles :
+
+
+
diff --git a/fr/workshops/index.html b/fr/workshops/index.html
new file mode 100644
index 00000000..fc3e84f8
--- /dev/null
+++ b/fr/workshops/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ 📅 Workshops | Apprendre Kotlin
+
+
+
+
+
+
+
+
+
diff --git a/icon-192x192.png b/icon-192x192.png
new file mode 100644
index 00000000..45057eb6
Binary files /dev/null and b/icon-192x192.png differ
diff --git a/icon-256x256.png b/icon-256x256.png
new file mode 100644
index 00000000..2053f3a2
Binary files /dev/null and b/icon-256x256.png differ
diff --git a/icon-384x384.png b/icon-384x384.png
new file mode 100644
index 00000000..1447d286
Binary files /dev/null and b/icon-384x384.png differ
diff --git a/icon-512x512.png b/icon-512x512.png
new file mode 100644
index 00000000..6474ce29
Binary files /dev/null and b/icon-512x512.png differ
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..eb72ae0e
--- /dev/null
+++ b/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Welcome
+
+
+
+
+
+
+
+
+
diff --git a/kotlin_logo.png b/kotlin_logo.png
new file mode 100644
index 00000000..22d7a18d
Binary files /dev/null and b/kotlin_logo.png differ
diff --git a/logo_worldline.png b/logo_worldline.png
new file mode 100644
index 00000000..6981642d
Binary files /dev/null and b/logo_worldline.png differ
diff --git a/manifest.webmanifest b/manifest.webmanifest
new file mode 100644
index 00000000..95213a27
--- /dev/null
+++ b/manifest.webmanifest
@@ -0,0 +1 @@
+{"name":"Kotlin training","short_name":"Kotlin training","description":"A site built with vuepress","lang":"en-US","start_url":"/learning-kotlin/index.html","scope":"/learning-kotlin/","display":"standalone","theme_color":"#f635a4","background_color":"#e8d4f2","orientation":"portrait-primary","prefer_related_applications":false,"icons":[{"src":"icon-192x192.png","sizes":"192x192","type":"image/png"},{"src":"icon-256x256.png","sizes":"256x256","type":"image/png"},{"src":"icon-384x384.png","sizes":"384x384","type":"image/png"},{"src":"icon-512x512.png","sizes":"512x512","type":"image/png"}]}
diff --git a/service-worker.js b/service-worker.js
new file mode 100644
index 00000000..d1147962
--- /dev/null
+++ b/service-worker.js
@@ -0,0 +1 @@
+if(!self.define){let e,s={};const i=(i,a)=>(i=new URL(i+".js",a).href,s[i]||new Promise((s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()})).then((()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e})));self.define=(a,r)=>{const d=e||("document"in self?document.currentScript.src:"")||location.href;if(s[d])return;let t={};const f=e=>i(e,d),n={module:{uri:d},exports:t,require:f};s[d]=Promise.all(a.map((e=>n[e]||f(e)))).then((e=>(r(...e),t)))}}define(["./workbox-1ab968a5"],(function(e){"use strict";self.addEventListener("message",(e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()})),e.clientsClaim(),e.precacheAndRoute([{url:"assets/404.html-CtZ3hJcg.js",revision:"df020b8fcff2f781e0596295ba646991"},{url:"assets/Androidmakers2023Kotlinshortlink-7XGfMd7N.svg",revision:"d6f2cc50c4311d68a92f93e0d12b284b"},{url:"assets/app-jR2rC7Ae.js",revision:"8b89bca9a3faeaa7dce83dca7644d4dc"},{url:"assets/index-DTEEl-sV.js",revision:"46a193641571106d3b7b43f9bc2a2735"},{url:"assets/index.html-8yMvNPCQ.js",revision:"2396dd6121ed200c55f11079436a34c0"},{url:"assets/index.html-B1GaKHtm.js",revision:"a5763fd85f71a5daf0b3a7cb546b8780"},{url:"assets/index.html-B8Y7MGp7.js",revision:"ae75596e2fc418cc29eea6f258365341"},{url:"assets/index.html-BBhIJwI8.js",revision:"239f3f168b1bb335fb01f1545ff34edd"},{url:"assets/index.html-BEuoD-gh.js",revision:"a3e7e348d53563cfb0db15f056e3247a"},{url:"assets/index.html-BGxKf9ec.js",revision:"d91a40e20b415d25e662d26e5a6bb694"},{url:"assets/index.html-BOBxFfL8.js",revision:"78fcd59b1d86b87390ccf74c93b8e8c3"},{url:"assets/index.html-C_I161o0.js",revision:"270c402fdc28b419845ce19a73fe158a"},{url:"assets/index.html-Cd7wtrxg.js",revision:"0358a7ba31c8ac19861a4638473ed230"},{url:"assets/index.html-CDMPFPby.js",revision:"1f550b455678c82b8e19e5af48e4ca77"},{url:"assets/index.html-CrQuKRiG.js",revision:"6fc2e796eb81f3953b436e5936d9ebcc"},{url:"assets/index.html-D9WLw91N.js",revision:"e69f40e5959e089ec342ea710666d3bb"},{url:"assets/index.html-DJx1WQoN.js",revision:"7e0f2f040eea212e70e35af2dfe1ba68"},{url:"assets/index.html-DuVz8Jxd.js",revision:"8b027476933f01f6a0d6928738546471"},{url:"assets/index.html-hW5i8q7d.js",revision:"629daa970b323a4b9004facc6df36497"},{url:"assets/index.html-mnsf-FXr.js",revision:"906749101432583f6341fe757ffb87d4"},{url:"assets/index.html-pJuZpkgy.js",revision:"5faf5fbc60a3f534799f02475c0011a9"},{url:"assets/kmp_codelab-CiTPMWjt.js",revision:"6105fc434ff6ae7dfe6a0c1067618361"},{url:"assets/kotlin-decision-tree-4i7nEr1Z.svg",revision:"f257bed572584c782ad1e37c483ba4b6"},{url:"assets/kotlin-used-for-Bdlavnqs.js",revision:"92007e36f0c0d3b7b6265af81a141307"},{url:"assets/kotlin-wasm-webapp-R4_9ho9v.js",revision:"4be4df631b1ce26e600722ef2854991e"},{url:"assets/logo_worldline-dinT9MYm.js",revision:"aa655bfd05e03a82f745b7aa0a99c90f"},{url:"assets/qrcode-mixtit24-D6tnJWnS.js",revision:"daddde81cfadffa280234567273cf293"},{url:"assets/style-DvQwtSJL.css",revision:"8317c950420418b8004844002f50ff64"},{url:"index.html",revision:"5e673679e5ae40fb87ea482c46b03d54"},{url:"404.html",revision:"805d530c7996d8f651bb5d621730fbdd"}],{}),e.cleanupOutdatedCaches()}));
diff --git a/workbox-1ab968a5.js b/workbox-1ab968a5.js
new file mode 100644
index 00000000..b46d3f76
--- /dev/null
+++ b/workbox-1ab968a5.js
@@ -0,0 +1 @@
+define(["exports"],(function(t){"use strict";try{self["workbox:core:7.0.0"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=` :: ${JSON.stringify(e)}`),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self["workbox:routing:7.0.0"]&&_()}catch(t){}const n=t=>t&&"object"==typeof t?t:{handle:t};class i{constructor(t,e,s="GET"){this.handler=n(e),this.match=t,this.method=s}setCatchHandler(t){this.catchHandler=n(t)}}class r extends i{constructor(t,e,s){super((({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)}),e,s)}}class o{constructor(){this.t=new Map,this.i=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",(t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map((e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})})));t.waitUntil(s),t.ports&&t.ports[0]&&s.then((()=>t.ports[0].postMessage(!0)))}}))}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const n=s.origin===location.origin,{params:i,route:r}=this.findMatchingRoute({event:e,request:t,sameOrigin:n,url:s});let o=r&&r.handler;const c=t.method;if(!o&&this.i.has(c)&&(o=this.i.get(c)),!o)return;let a;try{a=o.handle({url:s,request:t,event:e,params:i})}catch(t){a=Promise.reject(t)}const h=r&&r.catchHandler;return a instanceof Promise&&(this.o||h)&&(a=a.catch((async n=>{if(h)try{return await h.handle({url:s,request:t,event:e,params:i})}catch(t){t instanceof Error&&(n=t)}if(this.o)return this.o.handle({url:s,request:t,event:e});throw n}))),a}findMatchingRoute({url:t,sameOrigin:e,request:s,event:n}){const i=this.t.get(s.method)||[];for(const r of i){let i;const o=r.match({url:t,sameOrigin:e,request:s,event:n});if(o)return i=o,(Array.isArray(i)&&0===i.length||o.constructor===Object&&0===Object.keys(o).length||"boolean"==typeof o)&&(i=void 0),{route:r,params:i}}return{}}setDefaultHandler(t,e="GET"){this.i.set(e,n(t))}setCatchHandler(t){this.o=n(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s("unregister-route-but-not-found-with-method",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s("unregister-route-route-not-registered");this.t.get(t.method).splice(e,1)}}let c;const a=()=>(c||(c=new o,c.addFetchListener(),c.addCacheListener()),c);const h={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},u=t=>[h.prefix,t,h.suffix].filter((t=>t&&t.length>0)).join("-"),l=t=>t||u(h.precache),f=t=>t||u(h.runtime);function w(t,e){const s=e();return t.waitUntil(s),s}try{self["workbox:precaching:7.0.0"]&&_()}catch(t){}function d(t){if(!t)throw new s("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new s("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const i=new URL(n,location.href),r=new URL(n,location.href);return i.searchParams.set("__WB_REVISION__",e),{cacheKey:i.href,url:r.href}}class p{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:t,state:e})=>{e&&(e.originalRequest=t)},this.cachedResponseWillBeUsed=async({event:t,state:e,cachedResponse:s})=>{if("install"===t.type&&e&&e.originalRequest&&e.originalRequest instanceof Request){const t=e.originalRequest.url;s?this.notUpdatedURLs.push(t):this.updatedURLs.push(t)}return s}}}class y{constructor({precacheController:t}){this.cacheKeyWillBeUsed=async({request:t,params:e})=>{const s=(null==e?void 0:e.cacheKey)||this.h.getCacheKeyForURL(t.url);return s?new Request(s,{headers:t.headers}):t},this.h=t}}let g;async function R(t,e){let n=null;if(t.url){n=new URL(t.url).origin}if(n!==self.location.origin)throw new s("cross-origin-copy-response",{origin:n});const i=t.clone(),r={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=e?e(r):r,c=function(){if(void 0===g){const t=new Response("");if("body"in t)try{new Response(t.body),g=!0}catch(t){g=!1}g=!1}return g}()?i.body:await i.blob();return new Response(c,o)}function m(t,e){const s=new URL(t);for(const t of e)s.searchParams.delete(t);return s.href}class v{constructor(){this.promise=new Promise(((t,e)=>{this.resolve=t,this.reject=e}))}}const q=new Set;try{self["workbox:strategies:7.0.0"]&&_()}catch(t){}function U(t){return"string"==typeof t?new Request(t):t}class L{constructor(t,e){this.u={},Object.assign(this,e),this.event=e.event,this.l=t,this.p=new v,this.R=[],this.m=[...t.plugins],this.v=new Map;for(const t of this.m)this.v.set(t,{});this.event.waitUntil(this.p.promise)}async fetch(t){const{event:e}=this;let n=U(t);if("navigate"===n.mode&&e instanceof FetchEvent&&e.preloadResponse){const t=await e.preloadResponse;if(t)return t}const i=this.hasCallback("fetchDidFail")?n.clone():null;try{for(const t of this.iterateCallbacks("requestWillFetch"))n=await t({request:n.clone(),event:e})}catch(t){if(t instanceof Error)throw new s("plugin-error-request-will-fetch",{thrownErrorMessage:t.message})}const r=n.clone();try{let t;t=await fetch(n,"navigate"===n.mode?void 0:this.l.fetchOptions);for(const s of this.iterateCallbacks("fetchDidSucceed"))t=await s({event:e,request:r,response:t});return t}catch(t){throw i&&await this.runCallbacks("fetchDidFail",{error:t,event:e,originalRequest:i.clone(),request:r.clone()}),t}}async fetchAndCachePut(t){const e=await this.fetch(t),s=e.clone();return this.waitUntil(this.cachePut(t,s)),e}async cacheMatch(t){const e=U(t);let s;const{cacheName:n,matchOptions:i}=this.l,r=await this.getCacheKey(e,"read"),o=Object.assign(Object.assign({},i),{cacheName:n});s=await caches.match(r,o);for(const t of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await t({cacheName:n,matchOptions:i,cachedResponse:s,request:r,event:this.event})||void 0;return s}async cachePut(t,e){const n=U(t);var i;await(i=0,new Promise((t=>setTimeout(t,i))));const r=await this.getCacheKey(n,"write");if(!e)throw new s("cache-put-with-no-response",{url:(o=r.url,new URL(String(o),location.href).href.replace(new RegExp(`^${location.origin}`),""))});var o;const c=await this.q(e);if(!c)return!1;const{cacheName:a,matchOptions:h}=this.l,u=await self.caches.open(a),l=this.hasCallback("cacheDidUpdate"),f=l?await async function(t,e,s,n){const i=m(e.url,s);if(e.url===i)return t.match(e,n);const r=Object.assign(Object.assign({},n),{ignoreSearch:!0}),o=await t.keys(e,r);for(const e of o)if(i===m(e.url,s))return t.match(e,n)}(u,r.clone(),["__WB_REVISION__"],h):null;try{await u.put(r,l?c.clone():c)}catch(t){if(t instanceof Error)throw"QuotaExceededError"===t.name&&await async function(){for(const t of q)await t()}(),t}for(const t of this.iterateCallbacks("cacheDidUpdate"))await t({cacheName:a,oldResponse:f,newResponse:c.clone(),request:r,event:this.event});return!0}async getCacheKey(t,e){const s=`${t.url} | ${e}`;if(!this.u[s]){let n=t;for(const t of this.iterateCallbacks("cacheKeyWillBeUsed"))n=U(await t({mode:e,request:n,event:this.event,params:this.params}));this.u[s]=n}return this.u[s]}hasCallback(t){for(const e of this.l.plugins)if(t in e)return!0;return!1}async runCallbacks(t,e){for(const s of this.iterateCallbacks(t))await s(e)}*iterateCallbacks(t){for(const e of this.l.plugins)if("function"==typeof e[t]){const s=this.v.get(e),n=n=>{const i=Object.assign(Object.assign({},n),{state:s});return e[t](i)};yield n}}waitUntil(t){return this.R.push(t),t}async doneWaiting(){let t;for(;t=this.R.shift();)await t}destroy(){this.p.resolve(null)}async q(t){let e=t,s=!1;for(const t of this.iterateCallbacks("cacheWillUpdate"))if(e=await t({request:this.request,response:e,event:this.event})||void 0,s=!0,!e)break;return s||e&&200!==e.status&&(e=void 0),e}}class b{constructor(t={}){this.cacheName=f(t.cacheName),this.plugins=t.plugins||[],this.fetchOptions=t.fetchOptions,this.matchOptions=t.matchOptions}handle(t){const[e]=this.handleAll(t);return e}handleAll(t){t instanceof FetchEvent&&(t={event:t,request:t.request});const e=t.event,s="string"==typeof t.request?new Request(t.request):t.request,n="params"in t?t.params:void 0,i=new L(this,{event:e,request:s,params:n}),r=this.U(i,s,e);return[r,this.L(r,i,s,e)]}async U(t,e,n){let i;await t.runCallbacks("handlerWillStart",{event:n,request:e});try{if(i=await this._(e,t),!i||"error"===i.type)throw new s("no-response",{url:e.url})}catch(s){if(s instanceof Error)for(const r of t.iterateCallbacks("handlerDidError"))if(i=await r({error:s,event:n,request:e}),i)break;if(!i)throw s}for(const s of t.iterateCallbacks("handlerWillRespond"))i=await s({event:n,request:e,response:i});return i}async L(t,e,s,n){let i,r;try{i=await t}catch(r){}try{await e.runCallbacks("handlerDidRespond",{event:n,request:s,response:i}),await e.doneWaiting()}catch(t){t instanceof Error&&(r=t)}if(await e.runCallbacks("handlerDidComplete",{event:n,request:s,response:i,error:r}),e.destroy(),r)throw r}}class C extends b{constructor(t={}){t.cacheName=l(t.cacheName),super(t),this.C=!1!==t.fallbackToNetwork,this.plugins.push(C.copyRedirectedCacheableResponsesPlugin)}async _(t,e){const s=await e.cacheMatch(t);return s||(e.event&&"install"===e.event.type?await this.O(t,e):await this.N(t,e))}async N(t,e){let n;const i=e.params||{};if(!this.C)throw new s("missing-precache-entry",{cacheName:this.cacheName,url:t.url});{const s=i.integrity,r=t.integrity,o=!r||r===s;n=await e.fetch(new Request(t,{integrity:"no-cors"!==t.mode?r||s:void 0})),s&&o&&"no-cors"!==t.mode&&(this.k(),await e.cachePut(t,n.clone()))}return n}async O(t,e){this.k();const n=await e.fetch(t);if(!await e.cachePut(t,n.clone()))throw new s("bad-precaching-response",{url:t.url,status:n.status});return n}k(){let t=null,e=0;for(const[s,n]of this.plugins.entries())n!==C.copyRedirectedCacheableResponsesPlugin&&(n===C.defaultPrecacheCacheabilityPlugin&&(t=s),n.cacheWillUpdate&&e++);0===e?this.plugins.push(C.defaultPrecacheCacheabilityPlugin):e>1&&null!==t&&this.plugins.splice(t,1)}}C.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:t})=>!t||t.status>=400?null:t},C.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:t})=>t.redirected?await R(t):t};class E{constructor({cacheName:t,plugins:e=[],fallbackToNetwork:s=!0}={}){this.K=new Map,this.P=new Map,this.T=new Map,this.l=new C({cacheName:l(t),plugins:[...e,new y({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this.l}precache(t){this.addToCacheList(t),this.W||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this.W=!0)}addToCacheList(t){const e=[];for(const n of t){"string"==typeof n?e.push(n):n&&void 0===n.revision&&e.push(n.url);const{cacheKey:t,url:i}=d(n),r="string"!=typeof n&&n.revision?"reload":"default";if(this.K.has(i)&&this.K.get(i)!==t)throw new s("add-to-cache-list-conflicting-entries",{firstEntry:this.K.get(i),secondEntry:t});if("string"!=typeof n&&n.integrity){if(this.T.has(t)&&this.T.get(t)!==n.integrity)throw new s("add-to-cache-list-conflicting-integrities",{url:i});this.T.set(t,n.integrity)}if(this.K.set(i,t),this.P.set(i,r),e.length>0){const t=`Workbox is precaching URLs without revision info: ${e.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(t)}}}install(t){return w(t,(async()=>{const e=new p;this.strategy.plugins.push(e);for(const[e,s]of this.K){const n=this.T.get(s),i=this.P.get(e),r=new Request(e,{integrity:n,cache:i,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:s},request:r,event:t}))}const{updatedURLs:s,notUpdatedURLs:n}=e;return{updatedURLs:s,notUpdatedURLs:n}}))}activate(t){return w(t,(async()=>{const t=await self.caches.open(this.strategy.cacheName),e=await t.keys(),s=new Set(this.K.values()),n=[];for(const i of e)s.has(i.url)||(await t.delete(i),n.push(i.url));return{deletedURLs:n}}))}getURLsToCacheKeys(){return this.K}getCachedURLs(){return[...this.K.keys()]}getCacheKeyForURL(t){const e=new URL(t,location.href);return this.K.get(e.href)}getIntegrityForCacheKey(t){return this.T.get(t)}async matchPrecache(t){const e=t instanceof Request?t.url:t,s=this.getCacheKeyForURL(e);if(s){return(await self.caches.open(this.strategy.cacheName)).match(s)}}createHandlerBoundToURL(t){const e=this.getCacheKeyForURL(t);if(!e)throw new s("non-precached-url",{url:t});return s=>(s.request=new Request(t),s.params=Object.assign({cacheKey:e},s.params),this.strategy.handle(s))}}let O;const x=()=>(O||(O=new E),O);class N extends i{constructor(t,e){super((({request:s})=>{const n=t.getURLsToCacheKeys();for(const i of function*(t,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:n=!0,urlManipulation:i}={}){const r=new URL(t,location.href);r.hash="",yield r.href;const o=function(t,e=[]){for(const s of[...t.searchParams.keys()])e.some((t=>t.test(s)))&&t.searchParams.delete(s);return t}(r,e);if(yield o.href,s&&o.pathname.endsWith("/")){const t=new URL(o.href);t.pathname+=s,yield t.href}if(n){const t=new URL(o.href);t.pathname+=".html",yield t.href}if(i){const t=i({url:r});for(const e of t)yield e.href}}(s.url,e)){const e=n.get(i);if(e){return{cacheKey:e,integrity:t.getIntegrityForCacheKey(e)}}}}),t.strategy)}}function k(t){const e=x();!function(t,e,n){let o;if("string"==typeof t){const s=new URL(t,location.href);o=new i((({url:t})=>t.href===s.href),e,n)}else if(t instanceof RegExp)o=new r(t,e,n);else if("function"==typeof t)o=new i(t,e,n);else{if(!(t instanceof i))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});o=t}a().registerRoute(o)}(new N(e,t))}t.cleanupOutdatedCaches=function(){self.addEventListener("activate",(t=>{const e=l();t.waitUntil((async(t,e="-precache-")=>{const s=(await self.caches.keys()).filter((s=>s.includes(e)&&s.includes(self.registration.scope)&&s!==t));return await Promise.all(s.map((t=>self.caches.delete(t)))),s})(e).then((t=>{})))}))},t.clientsClaim=function(){self.addEventListener("activate",(()=>self.clients.claim()))},t.precacheAndRoute=function(t,e){!function(t){x().precache(t)}(t),k(e)}}));