diff --git a/404.html b/404.html new file mode 100644 index 00000000..d556fc83 --- /dev/null +++ b/404.html @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + +

404

Looks like we've got some broken links.
Take me home
+ + + diff --git a/assets/404.html-CtZ3hJcg.js b/assets/404.html-CtZ3hJcg.js new file mode 100644 index 00000000..1b545990 --- /dev/null +++ b/assets/404.html-CtZ3hJcg.js @@ -0,0 +1 @@ +import{_ as t,o as e,c as o,a}from"./app-jR2rC7Ae.js";const n={},c=a("p",null,"404 Not Found",-1),l=[c];function s(_,r){return e(),o("div",null,l)}const d=t(n,[["render",s],["__file","404.html.vue"]]),h=JSON.parse('{"path":"/404.html","title":"","lang":"en-US","frontmatter":{"layout":"NotFound"},"headers":[],"git":{},"filePathRelative":null}');export{d as comp,h as data}; diff --git a/assets/Androidmakers2023Kotlinshortlink-7XGfMd7N.svg b/assets/Androidmakers2023Kotlinshortlink-7XGfMd7N.svg new file mode 100644 index 00000000..53959f78 --- /dev/null +++ b/assets/Androidmakers2023Kotlinshortlink-7XGfMd7N.svg @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/Kotlin-Beyond-Android-B6J4ER4N.png b/assets/Kotlin-Beyond-Android-B6J4ER4N.png new file mode 100644 index 00000000..cb22227b Binary files /dev/null and b/assets/Kotlin-Beyond-Android-B6J4ER4N.png differ diff --git a/assets/app-jR2rC7Ae.js b/assets/app-jR2rC7Ae.js new file mode 100644 index 00000000..1e8ff35c --- /dev/null +++ b/assets/app-jR2rC7Ae.js @@ -0,0 +1,32 @@ +function __vite__mapDeps(indexes) { + if (!__vite__mapDeps.viteFileDeps) { + __vite__mapDeps.viteFileDeps = ["assets/index.html-C_I161o0.js","assets/logo_worldline-dinT9MYm.js","assets/index.html-CDMPFPby.js","assets/index.html-DuVz8Jxd.js","assets/kotlin-wasm-webapp-R4_9ho9v.js","assets/index.html-CrQuKRiG.js","assets/kmp_codelab-CiTPMWjt.js","assets/index.html-BOBxFfL8.js","assets/kotlin-used-for-Bdlavnqs.js","assets/index.html-B1GaKHtm.js","assets/qrcode-mixtit24-D6tnJWnS.js","assets/index.html-BGxKf9ec.js","assets/index.html-BEuoD-gh.js","assets/index.html-Cd7wtrxg.js","assets/index.html-mnsf-FXr.js"] + } + return indexes.map((i) => __vite__mapDeps.viteFileDeps[i]) +} +/** +* @vue/shared v3.4.26 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**//*! #__NO_SIDE_EFFECTS__ */function Ro(e,t){const n=new Set(e.split(","));return t?r=>n.has(r.toLowerCase()):r=>n.has(r)}const ye={},fn=[],et=()=>{},ua=()=>!1,Gn=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),Io=e=>e.startsWith("onUpdate:"),Ae=Object.assign,Oo=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},fa=Object.prototype.hasOwnProperty,de=(e,t)=>fa.call(e,t),ee=Array.isArray,dn=e=>Or(e)==="[object Map]",ki=e=>Or(e)==="[object Set]",se=e=>typeof e=="function",Te=e=>typeof e=="string",rn=e=>typeof e=="symbol",we=e=>e!==null&&typeof e=="object",Ei=e=>(we(e)||se(e))&&se(e.then)&&se(e.catch),Ci=Object.prototype.toString,Or=e=>Ci.call(e),da=e=>Or(e).slice(8,-1),Si=e=>Or(e)==="[object Object]",$o=e=>Te(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,pn=Ro(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),$r=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},pa=/-(\w)/g,nt=$r(e=>e.replace(pa,(t,n)=>n?n.toUpperCase():"")),ha=/\B([A-Z])/g,on=$r(e=>e.replace(ha,"-$1").toLowerCase()),Jn=$r(e=>e.charAt(0).toUpperCase()+e.slice(1)),Jr=$r(e=>e?`on${Jn(e)}`:""),Ht=(e,t)=>!Object.is(e,t),Yr=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:r,value:n})},ma=e=>{const t=parseFloat(e);return isNaN(t)?e:t},ga=e=>{const t=Te(e)?Number(e):NaN;return isNaN(t)?e:t};let cs;const Li=()=>cs||(cs=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function Yn(e){if(ee(e)){const t={};for(let n=0;n{if(n){const r=n.split(ba);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t}function Ge(e){let t="";if(Te(e))t=e;else if(ee(e))for(let n=0;nTe(e)?e:e==null?"":ee(e)||we(e)&&(e.toString===Ci||!se(e.toString))?JSON.stringify(e,Ti,2):String(e),Ti=(e,t)=>t&&t.__v_isRef?Ti(e,t.value):dn(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[r,o],s)=>(n[Xr(r,s)+" =>"]=o,n),{})}:ki(t)?{[`Set(${t.size})`]:[...t.values()].map(n=>Xr(n))}:rn(t)?Xr(t):we(t)&&!ee(t)&&!Si(t)?String(t):t,Xr=(e,t="")=>{var n;return rn(e)?`Symbol(${(n=e.description)!=null?n:t})`:e};/** +* @vue/reactivity v3.4.26 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let Xe;class Ea{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this.parent=Xe,!t&&Xe&&(this.index=(Xe.scopes||(Xe.scopes=[])).push(this)-1)}get active(){return this._active}run(t){if(this._active){const n=Xe;try{return Xe=this,t()}finally{Xe=n}}}on(){Xe=this}off(){Xe=this.parent}stop(t){if(this._active){let n,r;for(n=0,r=this.effects.length;n=4))break}this._dirtyLevel===1&&(this._dirtyLevel=0),zt()}return this._dirtyLevel>=4}set dirty(t){this._dirtyLevel=t?4:0}run(){if(this._dirtyLevel=0,!this.active)return this.fn();let t=$t,n=en;try{return $t=!0,en=this,this._runnings++,us(this),this.fn()}finally{fs(this),this._runnings--,en=n,$t=t}}stop(){this.active&&(us(this),fs(this),this.onStop&&this.onStop(),this.active=!1)}}function xa(e){return e.value}function us(e){e._trackId++,e._depsLength=0}function fs(e){if(e.deps.length>e._depsLength){for(let t=e._depsLength;t{const n=new Map;return n.cleanup=e,n.computed=t,n},kr=new WeakMap,tn=Symbol(""),go=Symbol("");function Je(e,t,n){if($t&&en){let r=kr.get(e);r||kr.set(e,r=new Map);let o=r.get(n);o||r.set(n,o=Mi(()=>r.delete(n))),Oi(en,o)}}function yt(e,t,n,r,o,s){const i=kr.get(e);if(!i)return;let l=[];if(t==="clear")l=[...i.values()];else if(n==="length"&&ee(e)){const a=Number(r);i.forEach((c,u)=>{(u==="length"||!rn(u)&&u>=a)&&l.push(c)})}else switch(n!==void 0&&l.push(i.get(n)),t){case"add":ee(e)?$o(n)&&l.push(i.get("length")):(l.push(i.get(tn)),dn(e)&&l.push(i.get(go)));break;case"delete":ee(e)||(l.push(i.get(tn)),dn(e)&&l.push(i.get(go)));break;case"set":dn(e)&&l.push(i.get(tn));break}No();for(const a of l)a&&$i(a,4);Ho()}function La(e,t){const n=kr.get(e);return n&&n.get(t)}const Aa=Ro("__proto__,__v_isRef,__isVue"),Ni=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(rn)),ds=Ta();function Ta(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const r=pe(this);for(let s=0,i=this.length;s{e[t]=function(...n){Bt(),No();const r=pe(this)[t].apply(this,n);return Ho(),zt(),r}}),e}function Pa(e){rn(e)||(e=String(e));const t=pe(this);return Je(t,"has",e),t.hasOwnProperty(e)}class Hi{constructor(t=!1,n=!1){this._isReadonly=t,this._isShallow=n}get(t,n,r){const o=this._isReadonly,s=this._isShallow;if(n==="__v_isReactive")return!o;if(n==="__v_isReadonly")return o;if(n==="__v_isShallow")return s;if(n==="__v_raw")return r===(o?s?Ka:Bi:s?ji:Di).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(r)?t:void 0;const i=ee(t);if(!o){if(i&&de(ds,n))return Reflect.get(ds,n,r);if(n==="hasOwnProperty")return Pa}const l=Reflect.get(t,n,r);return(rn(n)?Ni.has(n):Aa(n))||(o||Je(t,"get",n),s)?l:Ke(l)?i&&$o(n)?l:l.value:we(l)?o?Nr(l):Xn(l):l}}class Fi extends Hi{constructor(t=!1){super(!1,t)}set(t,n,r,o){let s=t[n];if(!this._isShallow){const a=Hn(s);if(!Er(r)&&!Hn(r)&&(s=pe(s),r=pe(r)),!ee(t)&&Ke(s)&&!Ke(r))return a?!1:(s.value=r,!0)}const i=ee(t)&&$o(n)?Number(n)e,Mr=e=>Reflect.getPrototypeOf(e);function sr(e,t,n=!1,r=!1){e=e.__v_raw;const o=pe(e),s=pe(t);n||(Ht(t,s)&&Je(o,"get",t),Je(o,"get",s));const{has:i}=Mr(o),l=r?Fo:n?Bo:Fn;if(i.call(o,t))return l(e.get(t));if(i.call(o,s))return l(e.get(s));e!==o&&e.get(t)}function ir(e,t=!1){const n=this.__v_raw,r=pe(n),o=pe(e);return t||(Ht(e,o)&&Je(r,"has",e),Je(r,"has",o)),e===o?n.has(e):n.has(e)||n.has(o)}function lr(e,t=!1){return e=e.__v_raw,!t&&Je(pe(e),"iterate",tn),Reflect.get(e,"size",e)}function ps(e){e=pe(e);const t=pe(this);return Mr(t).has.call(t,e)||(t.add(e),yt(t,"add",e,e)),this}function hs(e,t){t=pe(t);const n=pe(this),{has:r,get:o}=Mr(n);let s=r.call(n,e);s||(e=pe(e),s=r.call(n,e));const i=o.call(n,e);return n.set(e,t),s?Ht(t,i)&&yt(n,"set",e,t):yt(n,"add",e,t),this}function ms(e){const t=pe(this),{has:n,get:r}=Mr(t);let o=n.call(t,e);o||(e=pe(e),o=n.call(t,e)),r&&r.call(t,e);const s=t.delete(e);return o&&yt(t,"delete",e,void 0),s}function gs(){const e=pe(this),t=e.size!==0,n=e.clear();return t&&yt(e,"clear",void 0,void 0),n}function ar(e,t){return function(r,o){const s=this,i=s.__v_raw,l=pe(i),a=t?Fo:e?Bo:Fn;return!e&&Je(l,"iterate",tn),i.forEach((c,u)=>r.call(o,a(c),a(u),s))}}function cr(e,t,n){return function(...r){const o=this.__v_raw,s=pe(o),i=dn(s),l=e==="entries"||e===Symbol.iterator&&i,a=e==="keys"&&i,c=o[e](...r),u=n?Fo:t?Bo:Fn;return!t&&Je(s,"iterate",a?go:tn),{next(){const{value:f,done:d}=c.next();return d?{value:f,done:d}:{value:l?[u(f[0]),u(f[1])]:u(f),done:d}},[Symbol.iterator](){return this}}}}function St(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function Ma(){const e={get(s){return sr(this,s)},get size(){return lr(this)},has:ir,add:ps,set:hs,delete:ms,clear:gs,forEach:ar(!1,!1)},t={get(s){return sr(this,s,!1,!0)},get size(){return lr(this)},has:ir,add:ps,set:hs,delete:ms,clear:gs,forEach:ar(!1,!0)},n={get(s){return sr(this,s,!0)},get size(){return lr(this,!0)},has(s){return ir.call(this,s,!0)},add:St("add"),set:St("set"),delete:St("delete"),clear:St("clear"),forEach:ar(!0,!1)},r={get(s){return sr(this,s,!0,!0)},get size(){return lr(this,!0)},has(s){return ir.call(this,s,!0)},add:St("add"),set:St("set"),delete:St("delete"),clear:St("clear"),forEach:ar(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(s=>{e[s]=cr(s,!1,!1),n[s]=cr(s,!0,!1),t[s]=cr(s,!1,!0),r[s]=cr(s,!0,!0)}),[e,n,t,r]}const[Na,Ha,Fa,Da]=Ma();function Do(e,t){const n=t?e?Da:Fa:e?Ha:Na;return(r,o,s)=>o==="__v_isReactive"?!e:o==="__v_isReadonly"?e:o==="__v_raw"?r:Reflect.get(de(n,o)&&o in r?n:r,o,s)}const ja={get:Do(!1,!1)},Ba={get:Do(!1,!0)},za={get:Do(!0,!1)};const Di=new WeakMap,ji=new WeakMap,Bi=new WeakMap,Ka=new WeakMap;function Wa(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function Va(e){return e.__v_skip||!Object.isExtensible(e)?0:Wa(da(e))}function Xn(e){return Hn(e)?e:jo(e,!1,Ia,ja,Di)}function zi(e){return jo(e,!1,$a,Ba,ji)}function Nr(e){return jo(e,!0,Oa,za,Bi)}function jo(e,t,n,r,o){if(!we(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const s=o.get(e);if(s)return s;const i=Va(e);if(i===0)return e;const l=new Proxy(e,i===2?r:n);return o.set(e,l),l}function Tn(e){return Hn(e)?Tn(e.__v_raw):!!(e&&e.__v_isReactive)}function Hn(e){return!!(e&&e.__v_isReadonly)}function Er(e){return!!(e&&e.__v_isShallow)}function Ki(e){return e?!!e.__v_raw:!1}function pe(e){const t=e&&e.__v_raw;return t?pe(t):e}function Ua(e){return Object.isExtensible(e)&&xi(e,"__v_skip",!0),e}const Fn=e=>we(e)?Xn(e):e,Bo=e=>we(e)?Nr(e):e;class Wi{constructor(t,n,r,o){this.getter=t,this._setter=n,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this.effect=new Mo(()=>t(this._value),()=>Pn(this,this.effect._dirtyLevel===2?2:3)),this.effect.computed=this,this.effect.active=this._cacheable=!o,this.__v_isReadonly=r}get value(){const t=pe(this);return(!t._cacheable||t.effect.dirty)&&Ht(t._value,t._value=t.effect.run())&&Pn(t,4),zo(t),t.effect._dirtyLevel>=2&&Pn(t,2),t._value}set value(t){this._setter(t)}get _dirty(){return this.effect.dirty}set _dirty(t){this.effect.dirty=t}}function qa(e,t,n=!1){let r,o;const s=se(e);return s?(r=e,o=et):(r=e.get,o=e.set),new Wi(r,o,s||!o,n)}function zo(e){var t;$t&&en&&(e=pe(e),Oi(en,(t=e.dep)!=null?t:e.dep=Mi(()=>e.dep=void 0,e instanceof Wi?e:void 0)))}function Pn(e,t=4,n){e=pe(e);const r=e.dep;r&&$i(r,t)}function Ke(e){return!!(e&&e.__v_isRef===!0)}function ce(e){return Vi(e,!1)}function sn(e){return Vi(e,!0)}function Vi(e,t){return Ke(e)?e:new Ga(e,t)}class Ga{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:pe(t),this._value=n?t:Fn(t)}get value(){return zo(this),this._value}set value(t){const n=this.__v_isShallow||Er(t)||Hn(t);t=n?t:pe(t),Ht(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:Fn(t),Pn(this,4))}}function X(e){return Ke(e)?e.value:e}const Ja={get:(e,t,n)=>X(Reflect.get(e,t,n)),set:(e,t,n,r)=>{const o=e[t];return Ke(o)&&!Ke(n)?(o.value=n,!0):Reflect.set(e,t,n,r)}};function Ui(e){return Tn(e)?e:new Proxy(e,Ja)}class Ya{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:n,set:r}=t(()=>zo(this),()=>Pn(this));this._get=n,this._set=r}get value(){return this._get()}set value(t){this._set(t)}}function Xa(e){return new Ya(e)}function Hr(e){const t=ee(e)?new Array(e.length):{};for(const n in e)t[n]=Za(e,n);return t}class Qa{constructor(t,n,r){this._object=t,this._key=n,this._defaultValue=r,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return La(pe(this._object),this._key)}}function Za(e,t,n){const r=e[t];return Ke(r)?r:new Qa(e,t,n)}/** +* @vue/runtime-core v3.4.26 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function Mt(e,t,n,r){try{return r?e(...r):e()}catch(o){Qn(o,t,n)}}function tt(e,t,n,r){if(se(e)){const o=Mt(e,t,n,r);return o&&Ei(o)&&o.catch(s=>{Qn(s,t,n)}),o}if(ee(e)){const o=[];for(let s=0;s>>1,o=Be[r],s=jn(o);spt&&Be.splice(t,1)}function rc(e){ee(e)?hn.push(...e):(!Tt||!Tt.includes(e,e.allowRecurse?Yt+1:Yt))&&hn.push(e),Gi()}function vs(e,t,n=Dn?pt+1:0){for(;njn(n)-jn(r));if(hn.length=0,Tt){Tt.push(...t);return}for(Tt=t,Yt=0;Yte.id==null?1/0:e.id,oc=(e,t)=>{const n=jn(e)-jn(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function Ji(e){vo=!1,Dn=!0,Be.sort(oc);try{for(pt=0;ptTe(m)?m.trim():m)),f&&(o=n.map(ma))}let l,a=r[l=Jr(t)]||r[l=Jr(nt(t))];!a&&s&&(a=r[l=Jr(on(t))]),a&&tt(a,e,6,o);const c=r[l+"Once"];if(c){if(!e.emitted)e.emitted={};else if(e.emitted[l])return;e.emitted[l]=!0,tt(c,e,6,o)}}function Yi(e,t,n=!1){const r=t.emitsCache,o=r.get(e);if(o!==void 0)return o;const s=e.emits;let i={},l=!1;if(!se(e)){const a=c=>{const u=Yi(c,t,!0);u&&(l=!0,Ae(i,u))};!n&&t.mixins.length&&t.mixins.forEach(a),e.extends&&a(e.extends),e.mixins&&e.mixins.forEach(a)}return!s&&!l?(we(e)&&r.set(e,null),null):(ee(s)?s.forEach(a=>i[a]=null):Ae(i,s),we(e)&&r.set(e,i),i)}function Dr(e,t){return!e||!Gn(t)?!1:(t=t.slice(2).replace(/Once$/,""),de(e,t[0].toLowerCase()+t.slice(1))||de(e,on(t))||de(e,t))}let Re=null,Xi=null;function Sr(e){const t=Re;return Re=e,Xi=e&&e.type.__scopeId||null,t}function xe(e,t=Re,n){if(!t||e._n)return e;const r=(...o)=>{r._d&&Ts(-1);const s=Sr(t);let i;try{i=e(...o)}finally{Sr(s),r._d&&Ts(1)}return i};return r._n=!0,r._c=!0,r._d=!0,r}function Qr(e){const{type:t,vnode:n,proxy:r,withProxy:o,propsOptions:[s],slots:i,attrs:l,emit:a,render:c,renderCache:u,props:f,data:d,setupState:m,ctx:g,inheritAttrs:y}=e,E=Sr(e);let T,x;try{if(n.shapeFlag&4){const k=o||r,D=k;T=ot(c.call(D,k,u,f,m,d,g)),x=l}else{const k=t;T=ot(k.length>1?k(f,{attrs:l,slots:i,emit:a}):k(f,null)),x=t.props?l:ic(l)}}catch(k){$n.length=0,Qn(k,e,1),T=ie(qe)}let v=T;if(x&&y!==!1){const k=Object.keys(x),{shapeFlag:D}=v;k.length&&D&7&&(s&&k.some(Io)&&(x=lc(x,s)),v=Dt(v,x,!1,!0))}return n.dirs&&(v=Dt(v,null,!1,!0),v.dirs=v.dirs?v.dirs.concat(n.dirs):n.dirs),n.transition&&(v.transition=n.transition),T=v,Sr(E),T}const ic=e=>{let t;for(const n in e)(n==="class"||n==="style"||Gn(n))&&((t||(t={}))[n]=e[n]);return t},lc=(e,t)=>{const n={};for(const r in e)(!Io(r)||!(r.slice(9)in t))&&(n[r]=e[r]);return n};function ac(e,t,n){const{props:r,children:o,component:s}=e,{props:i,children:l,patchFlag:a}=t,c=s.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&a>=0){if(a&1024)return!0;if(a&16)return r?bs(r,i,c):!!i;if(a&8){const u=t.dynamicProps;for(let f=0;fe.__isSuspense;function Zi(e,t){t&&t.pendingBranch?ee(e)?t.effects.push(...e):t.effects.push(e):rc(e)}const pc=Symbol.for("v-scx"),hc=()=>ze(pc);function mc(e,t){return Wo(e,null,t)}const ur={};function De(e,t,n){return Wo(e,t,n)}function Wo(e,t,{immediate:n,deep:r,flush:o,once:s,onTrack:i,onTrigger:l}=ye){if(t&&s){const O=t;t=(...H)=>{O(...H),D()}}const a=Me,c=O=>r===!0?O:Qt(O,r===!1?1:void 0);let u,f=!1,d=!1;if(Ke(e)?(u=()=>e.value,f=Er(e)):Tn(e)?(u=()=>c(e),f=!0):ee(e)?(d=!0,f=e.some(O=>Tn(O)||Er(O)),u=()=>e.map(O=>{if(Ke(O))return O.value;if(Tn(O))return c(O);if(se(O))return Mt(O,a,2)})):se(e)?t?u=()=>Mt(e,a,2):u=()=>(m&&m(),tt(e,a,3,[g])):u=et,t&&r){const O=u;u=()=>Qt(O())}let m,g=O=>{m=v.onStop=()=>{Mt(O,a,4),m=v.onStop=void 0}},y;if(tr)if(g=et,t?n&&tt(t,a,3,[u(),d?[]:void 0,g]):u(),o==="sync"){const O=hc();y=O.__watcherHandles||(O.__watcherHandles=[])}else return et;let E=d?new Array(e.length).fill(ur):ur;const T=()=>{if(!(!v.active||!v.dirty))if(t){const O=v.run();(r||f||(d?O.some((H,b)=>Ht(H,E[b])):Ht(O,E)))&&(m&&m(),tt(t,a,3,[O,E===ur?void 0:d&&E[0]===ur?[]:E,g]),E=O)}else v.run()};T.allowRecurse=!!t;let x;o==="sync"?x=T:o==="post"?x=()=>Ue(T,a&&a.suspense):(T.pre=!0,a&&(T.id=a.uid),x=()=>Fr(T));const v=new Mo(u,et,x),k=Pi(),D=()=>{v.stop(),k&&Oo(k.effects,v)};return t?n?T():E=v.run():o==="post"?Ue(v.run.bind(v),a&&a.suspense):v.run(),y&&y.push(D),D}function gc(e,t,n){const r=this.proxy,o=Te(e)?e.includes(".")?el(r,e):()=>r[e]:e.bind(r,r);let s;se(t)?s=t:(s=t.handler,n=t);const i=er(this),l=Wo(o,s.bind(r),n);return i(),l}function el(e,t){const n=t.split(".");return()=>{let r=e;for(let o=0;o{Qt(r,t,n)});else if(Si(e))for(const r in e)Qt(e[r],t,n);return e}function xr(e,t){if(Re===null)return e;const n=Wr(Re)||Re.proxy,r=e.dirs||(e.dirs=[]);for(let o=0;o{e.isMounted=!0}),Br(()=>{e.isUnmounting=!0}),e}const Qe=[Function,Array],tl={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Qe,onEnter:Qe,onAfterEnter:Qe,onEnterCancelled:Qe,onBeforeLeave:Qe,onLeave:Qe,onAfterLeave:Qe,onLeaveCancelled:Qe,onBeforeAppear:Qe,onAppear:Qe,onAfterAppear:Qe,onAppearCancelled:Qe},bc={name:"BaseTransition",props:tl,setup(e,{slots:t}){const n=Kr(),r=vc();return()=>{const o=t.default&&rl(t.default(),!0);if(!o||!o.length)return;let s=o[0];if(o.length>1){for(const d of o)if(d.type!==qe){s=d;break}}const i=pe(e),{mode:l}=i;if(r.isLeaving)return Zr(s);const a=ys(s);if(!a)return Zr(s);const c=bo(a,i,r,n);_o(a,c);const u=n.subTree,f=u&&ys(u);if(f&&f.type!==qe&&!Xt(a,f)){const d=bo(f,i,r,n);if(_o(f,d),l==="out-in"&&a.type!==qe)return r.isLeaving=!0,d.afterLeave=()=>{r.isLeaving=!1,n.update.active!==!1&&(n.effect.dirty=!0,n.update())},Zr(s);l==="in-out"&&a.type!==qe&&(d.delayLeave=(m,g,y)=>{const E=nl(r,f);E[String(f.key)]=f,m[Pt]=()=>{g(),m[Pt]=void 0,delete c.delayedLeave},c.delayedLeave=y})}return s}}},_c=bc;function nl(e,t){const{leavingVNodes:n}=e;let r=n.get(t.type);return r||(r=Object.create(null),n.set(t.type,r)),r}function bo(e,t,n,r){const{appear:o,mode:s,persisted:i=!1,onBeforeEnter:l,onEnter:a,onAfterEnter:c,onEnterCancelled:u,onBeforeLeave:f,onLeave:d,onAfterLeave:m,onLeaveCancelled:g,onBeforeAppear:y,onAppear:E,onAfterAppear:T,onAppearCancelled:x}=t,v=String(e.key),k=nl(n,e),D=(b,G)=>{b&&tt(b,r,9,G)},O=(b,G)=>{const L=G[1];D(b,G),ee(b)?b.every(W=>W.length<=1)&&L():b.length<=1&&L()},H={mode:s,persisted:i,beforeEnter(b){let G=l;if(!n.isMounted)if(o)G=y||l;else return;b[Pt]&&b[Pt](!0);const L=k[v];L&&Xt(e,L)&&L.el[Pt]&&L.el[Pt](),D(G,[b])},enter(b){let G=a,L=c,W=u;if(!n.isMounted)if(o)G=E||a,L=T||c,W=x||u;else return;let w=!1;const N=b[fr]=te=>{w||(w=!0,te?D(W,[b]):D(L,[b]),H.delayedLeave&&H.delayedLeave(),b[fr]=void 0)};G?O(G,[b,N]):N()},leave(b,G){const L=String(e.key);if(b[fr]&&b[fr](!0),n.isUnmounting)return G();D(f,[b]);let W=!1;const w=b[Pt]=N=>{W||(W=!0,G(),N?D(g,[b]):D(m,[b]),b[Pt]=void 0,k[L]===e&&delete k[L])};k[L]=e,d?O(d,[b,w]):w()},clone(b){return bo(b,t,n,r)}};return H}function Zr(e){if(Zn(e))return e=Dt(e),e.children=null,e}function ys(e){if(!Zn(e))return e;const{shapeFlag:t,children:n}=e;if(n){if(t&16)return n[0];if(t&32&&se(n.default))return n.default()}}function _o(e,t){e.shapeFlag&6&&e.component?_o(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function rl(e,t=!1,n){let r=[],o=0;for(let s=0;s1)for(let s=0;s!!e.type.__asyncLoader;/*! #__NO_SIDE_EFFECTS__ */function yc(e){se(e)&&(e={loader:e});const{loader:t,loadingComponent:n,errorComponent:r,delay:o=200,timeout:s,suspensible:i=!0,onError:l}=e;let a=null,c,u=0;const f=()=>(u++,a=null,d()),d=()=>{let m;return a||(m=a=t().catch(g=>{if(g=g instanceof Error?g:new Error(String(g)),l)return new Promise((y,E)=>{l(g,()=>y(f()),()=>E(g),u+1)});throw g}).then(g=>m!==a&&a?a:(g&&(g.__esModule||g[Symbol.toStringTag]==="Module")&&(g=g.default),c=g,g)))};return he({name:"AsyncComponentWrapper",__asyncLoader:d,get __asyncResolved(){return c},setup(){const m=Me;if(c)return()=>eo(c,m);const g=x=>{a=null,Qn(x,m,13,!r)};if(i&&m.suspense||tr)return d().then(x=>()=>eo(x,m)).catch(x=>(g(x),()=>r?ie(r,{error:x}):null));const y=ce(!1),E=ce(),T=ce(!!o);return o&&setTimeout(()=>{T.value=!1},o),s!=null&&setTimeout(()=>{if(!y.value&&!E.value){const x=new Error(`Async component timed out after ${s}ms.`);g(x),E.value=x}},s),d().then(()=>{y.value=!0,m.parent&&Zn(m.parent.vnode)&&(m.parent.effect.dirty=!0,Fr(m.parent.update))}).catch(x=>{g(x),E.value=x}),()=>{if(y.value&&c)return eo(c,m);if(E.value&&r)return ie(r,{error:E.value});if(n&&!T.value)return ie(n)}}})}function eo(e,t){const{ref:n,props:r,children:o,ce:s}=t.vnode,i=ie(e,r,o);return i.ref=n,i.ce=s,delete t.vnode.ce,i}const Zn=e=>e.type.__isKeepAlive;function wc(e,t){ol(e,"a",t)}function kc(e,t){ol(e,"da",t)}function ol(e,t,n=Me){const r=e.__wdc||(e.__wdc=()=>{let o=n;for(;o;){if(o.isDeactivated)return;o=o.parent}return e()});if(jr(t,r,n),n){let o=n.parent;for(;o&&o.parent;)Zn(o.parent.vnode)&&Ec(r,t,n,o),o=o.parent}}function Ec(e,t,n,r){const o=jr(t,e,r,!0);zr(()=>{Oo(r[t],o)},n)}function jr(e,t,n=Me,r=!1){if(n){const o=n[e]||(n[e]=[]),s=t.__weh||(t.__weh=(...i)=>{if(n.isUnmounted)return;Bt();const l=er(n),a=tt(t,n,e,i);return l(),zt(),a});return r?o.unshift(s):o.push(s),s}}const kt=e=>(t,n=Me)=>(!tr||e==="sp")&&jr(e,(...r)=>t(...r),n),Cc=kt("bm"),He=kt("m"),Sc=kt("bu"),xc=kt("u"),Br=kt("bum"),zr=kt("um"),Lc=kt("sp"),Ac=kt("rtg"),Tc=kt("rtc");function Pc(e,t=Me){jr("ec",e,t)}function Ft(e,t,n,r){let o;const s=n&&n[r];if(ee(e)||Te(e)){o=new Array(e.length);for(let i=0,l=e.length;it(i,l,void 0,s&&s[l]));else{const i=Object.keys(e);o=new Array(i.length);for(let l=0,a=i.length;lTr(t)?!(t.type===qe||t.type===_e&&!sl(t.children)):!0)?e:null}const yo=e=>e?wl(e)?Wr(e)||e.proxy:yo(e.parent):null,Rn=Ae(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>yo(e.parent),$root:e=>yo(e.root),$emit:e=>e.emit,$options:e=>Vo(e),$forceUpdate:e=>e.f||(e.f=()=>{e.effect.dirty=!0,Fr(e.update)}),$nextTick:e=>e.n||(e.n=yn.bind(e.proxy)),$watch:e=>gc.bind(e)}),to=(e,t)=>e!==ye&&!e.__isScriptSetup&&de(e,t),Rc={get({_:e},t){if(t==="__v_skip")return!0;const{ctx:n,setupState:r,data:o,props:s,accessCache:i,type:l,appContext:a}=e;let c;if(t[0]!=="$"){const m=i[t];if(m!==void 0)switch(m){case 1:return r[t];case 2:return o[t];case 4:return n[t];case 3:return s[t]}else{if(to(r,t))return i[t]=1,r[t];if(o!==ye&&de(o,t))return i[t]=2,o[t];if((c=e.propsOptions[0])&&de(c,t))return i[t]=3,s[t];if(n!==ye&&de(n,t))return i[t]=4,n[t];wo&&(i[t]=0)}}const u=Rn[t];let f,d;if(u)return t==="$attrs"&&Je(e.attrs,"get",""),u(e);if((f=l.__cssModules)&&(f=f[t]))return f;if(n!==ye&&de(n,t))return i[t]=4,n[t];if(d=a.config.globalProperties,de(d,t))return d[t]},set({_:e},t,n){const{data:r,setupState:o,ctx:s}=e;return to(o,t)?(o[t]=n,!0):r!==ye&&de(r,t)?(r[t]=n,!0):de(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(s[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:o,propsOptions:s}},i){let l;return!!n[i]||e!==ye&&de(e,i)||to(t,i)||(l=s[0])&&de(l,i)||de(r,i)||de(Rn,i)||de(o.config.globalProperties,i)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:de(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function ws(e){return ee(e)?e.reduce((t,n)=>(t[n]=null,t),{}):e}let wo=!0;function Ic(e){const t=Vo(e),n=e.proxy,r=e.ctx;wo=!1,t.beforeCreate&&ks(t.beforeCreate,e,"bc");const{data:o,computed:s,methods:i,watch:l,provide:a,inject:c,created:u,beforeMount:f,mounted:d,beforeUpdate:m,updated:g,activated:y,deactivated:E,beforeDestroy:T,beforeUnmount:x,destroyed:v,unmounted:k,render:D,renderTracked:O,renderTriggered:H,errorCaptured:b,serverPrefetch:G,expose:L,inheritAttrs:W,components:w,directives:N,filters:te}=t;if(c&&Oc(c,r,null),i)for(const J in i){const V=i[J];se(V)&&(r[J]=V.bind(n))}if(o){const J=o.call(n,n);we(J)&&(e.data=Xn(J))}if(wo=!0,s)for(const J in s){const V=s[J],Ie=se(V)?V.bind(n,n):se(V.get)?V.get.bind(n,n):et,Fe=!se(V)&&se(V.set)?V.set.bind(n):et,Ve=I({get:Ie,set:Fe});Object.defineProperty(r,J,{enumerable:!0,configurable:!0,get:()=>Ve.value,set:je=>Ve.value=je})}if(l)for(const J in l)il(l[J],r,n,J);if(a){const J=se(a)?a.call(n):a;Reflect.ownKeys(J).forEach(V=>{Nt(V,J[V])})}u&&ks(u,e,"c");function R(J,V){ee(V)?V.forEach(Ie=>J(Ie.bind(n))):V&&J(V.bind(n))}if(R(Cc,f),R(He,d),R(Sc,m),R(xc,g),R(wc,y),R(kc,E),R(Pc,b),R(Tc,O),R(Ac,H),R(Br,x),R(zr,k),R(Lc,G),ee(L))if(L.length){const J=e.exposed||(e.exposed={});L.forEach(V=>{Object.defineProperty(J,V,{get:()=>n[V],set:Ie=>n[V]=Ie})})}else e.exposed||(e.exposed={});D&&e.render===et&&(e.render=D),W!=null&&(e.inheritAttrs=W),w&&(e.components=w),N&&(e.directives=N)}function Oc(e,t,n=et){ee(e)&&(e=ko(e));for(const r in e){const o=e[r];let s;we(o)?"default"in o?s=ze(o.from||r,o.default,!0):s=ze(o.from||r):s=ze(o),Ke(s)?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>s.value,set:i=>s.value=i}):t[r]=s}}function ks(e,t,n){tt(ee(e)?e.map(r=>r.bind(t.proxy)):e.bind(t.proxy),t,n)}function il(e,t,n,r){const o=r.includes(".")?el(n,r):()=>n[r];if(Te(e)){const s=t[e];se(s)&&De(o,s)}else if(se(e))De(o,e.bind(n));else if(we(e))if(ee(e))e.forEach(s=>il(s,t,n,r));else{const s=se(e.handler)?e.handler.bind(n):t[e.handler];se(s)&&De(o,s,e)}}function Vo(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:o,optionsCache:s,config:{optionMergeStrategies:i}}=e.appContext,l=s.get(t);let a;return l?a=l:!o.length&&!n&&!r?a=t:(a={},o.length&&o.forEach(c=>Lr(a,c,i,!0)),Lr(a,t,i)),we(t)&&s.set(t,a),a}function Lr(e,t,n,r=!1){const{mixins:o,extends:s}=t;s&&Lr(e,s,n,!0),o&&o.forEach(i=>Lr(e,i,n,!0));for(const i in t)if(!(r&&i==="expose")){const l=$c[i]||n&&n[i];e[i]=l?l(e[i],t[i]):t[i]}return e}const $c={data:Es,props:Cs,emits:Cs,methods:Ln,computed:Ln,beforeCreate:We,created:We,beforeMount:We,mounted:We,beforeUpdate:We,updated:We,beforeDestroy:We,beforeUnmount:We,destroyed:We,unmounted:We,activated:We,deactivated:We,errorCaptured:We,serverPrefetch:We,components:Ln,directives:Ln,watch:Nc,provide:Es,inject:Mc};function Es(e,t){return t?e?function(){return Ae(se(e)?e.call(this,this):e,se(t)?t.call(this,this):t)}:t:e}function Mc(e,t){return Ln(ko(e),ko(t))}function ko(e){if(ee(e)){const t={};for(let n=0;n1)return n&&se(t)?t.call(r&&r.proxy):t}}const al={},cl=()=>Object.create(al),ul=e=>Object.getPrototypeOf(e)===al;function Dc(e,t,n,r=!1){const o={},s=cl();e.propsDefaults=Object.create(null),fl(e,t,o,s);for(const i in e.propsOptions[0])i in o||(o[i]=void 0);n?e.props=r?o:zi(o):e.type.props?e.props=o:e.props=s,e.attrs=s}function jc(e,t,n,r){const{props:o,attrs:s,vnode:{patchFlag:i}}=e,l=pe(o),[a]=e.propsOptions;let c=!1;if((r||i>0)&&!(i&16)){if(i&8){const u=e.vnode.dynamicProps;for(let f=0;f{a=!0;const[d,m]=dl(f,t,!0);Ae(i,d),m&&l.push(...m)};!n&&t.mixins.length&&t.mixins.forEach(u),e.extends&&u(e.extends),e.mixins&&e.mixins.forEach(u)}if(!s&&!a)return we(e)&&r.set(e,fn),fn;if(ee(s))for(let u=0;u-1,m[1]=y<0||g-1||de(m,"default"))&&l.push(f)}}}const c=[i,l];return we(e)&&r.set(e,c),c}function Ss(e){return e[0]!=="$"&&!pn(e)}function xs(e){return e===null?"null":typeof e=="function"?e.name||"":typeof e=="object"&&e.constructor&&e.constructor.name||""}function Ls(e,t){return xs(e)===xs(t)}function As(e,t){return ee(t)?t.findIndex(n=>Ls(n,e)):se(t)&&Ls(t,e)?0:-1}const pl=e=>e[0]==="_"||e==="$stable",Uo=e=>ee(e)?e.map(ot):[ot(e)],Bc=(e,t,n)=>{if(t._n)return t;const r=xe((...o)=>Uo(t(...o)),n);return r._c=!1,r},hl=(e,t,n)=>{const r=e._ctx;for(const o in e){if(pl(o))continue;const s=e[o];if(se(s))t[o]=Bc(o,s,r);else if(s!=null){const i=Uo(s);t[o]=()=>i}}},ml=(e,t)=>{const n=Uo(t);e.slots.default=()=>n},zc=(e,t)=>{const n=e.slots=cl();if(e.vnode.shapeFlag&32){const r=t._;r?(Ae(n,t),xi(n,"_",r,!0)):hl(t,n)}else t&&ml(e,t)},Kc=(e,t,n)=>{const{vnode:r,slots:o}=e;let s=!0,i=ye;if(r.shapeFlag&32){const l=t._;l?n&&l===1?s=!1:(Ae(o,t),!n&&l===1&&delete o._):(s=!t.$stable,hl(t,o)),i=t}else t&&(ml(e,t),i={default:1});if(s)for(const l in o)!pl(l)&&i[l]==null&&delete o[l]};function Ar(e,t,n,r,o=!1){if(ee(e)){e.forEach((d,m)=>Ar(d,t&&(ee(t)?t[m]:t),n,r,o));return}if(mn(r)&&!o)return;const s=r.shapeFlag&4?Wr(r.component)||r.component.proxy:r.el,i=o?null:s,{i:l,r:a}=e,c=t&&t.r,u=l.refs===ye?l.refs={}:l.refs,f=l.setupState;if(c!=null&&c!==a&&(Te(c)?(u[c]=null,de(f,c)&&(f[c]=null)):Ke(c)&&(c.value=null)),se(a))Mt(a,l,12,[i,u]);else{const d=Te(a),m=Ke(a);if(d||m){const g=()=>{if(e.f){const y=d?de(f,a)?f[a]:u[a]:a.value;o?ee(y)&&Oo(y,s):ee(y)?y.includes(s)||y.push(s):d?(u[a]=[s],de(f,a)&&(f[a]=u[a])):(a.value=[s],e.k&&(u[e.k]=a.value))}else d?(u[a]=i,de(f,a)&&(f[a]=i)):m&&(a.value=i,e.k&&(u[e.k]=i))};i?(g.id=-1,Ue(g,n)):g()}}}let xt=!1;const Wc=e=>e.namespaceURI.includes("svg")&&e.tagName!=="foreignObject",Vc=e=>e.namespaceURI.includes("MathML"),dr=e=>{if(Wc(e))return"svg";if(Vc(e))return"mathml"},pr=e=>e.nodeType===8;function Uc(e){const{mt:t,p:n,o:{patchProp:r,createText:o,nextSibling:s,parentNode:i,remove:l,insert:a,createComment:c}}=e,u=(v,k)=>{if(!k.hasChildNodes()){n(null,v,k),Cr(),k._vnode=v;return}xt=!1,f(k.firstChild,v,null,null,null),Cr(),k._vnode=v,xt&&console.error("Hydration completed but contains mismatches.")},f=(v,k,D,O,H,b=!1)=>{b=b||!!k.dynamicChildren;const G=pr(v)&&v.data==="[",L=()=>y(v,k,D,O,H,G),{type:W,ref:w,shapeFlag:N,patchFlag:te}=k;let le=v.nodeType;k.el=v,te===-2&&(b=!1,k.dynamicChildren=null);let R=null;switch(W){case vn:le!==3?k.children===""?(a(k.el=o(""),i(v),v),R=v):R=L():(v.data!==k.children&&(xt=!0,v.data=k.children),R=s(v));break;case qe:x(v)?(R=s(v),T(k.el=v.content.firstChild,v,D)):le!==8||G?R=L():R=s(v);break;case On:if(G&&(v=s(v),le=v.nodeType),le===1||le===3){R=v;const J=!k.children.length;for(let V=0;V{b=b||!!k.dynamicChildren;const{type:G,props:L,patchFlag:W,shapeFlag:w,dirs:N,transition:te}=k,le=G==="input"||G==="option";if(le||W!==-1){N&&dt(k,null,D,"created");let R=!1;if(x(v)){R=gl(O,te)&&D&&D.vnode.props&&D.vnode.props.appear;const V=v.content.firstChild;R&&te.beforeEnter(V),T(V,v,D),k.el=v=V}if(w&16&&!(L&&(L.innerHTML||L.textContent))){let V=m(v.firstChild,k,v,D,O,H,b);for(;V;){xt=!0;const Ie=V;V=V.nextSibling,l(Ie)}}else w&8&&v.textContent!==k.children&&(xt=!0,v.textContent=k.children);if(L)if(le||!b||W&48)for(const V in L)(le&&(V.endsWith("value")||V==="indeterminate")||Gn(V)&&!pn(V)||V[0]===".")&&r(v,V,null,L[V],void 0,void 0,D);else L.onClick&&r(v,"onClick",null,L.onClick,void 0,void 0,D);let J;(J=L&&L.onVnodeBeforeMount)&&Ze(J,D,k),N&&dt(k,null,D,"beforeMount"),((J=L&&L.onVnodeMounted)||N||R)&&Zi(()=>{J&&Ze(J,D,k),R&&te.enter(v),N&&dt(k,null,D,"mounted")},O)}return v.nextSibling},m=(v,k,D,O,H,b,G)=>{G=G||!!k.dynamicChildren;const L=k.children,W=L.length;for(let w=0;w{const{slotScopeIds:G}=k;G&&(H=H?H.concat(G):G);const L=i(v),W=m(s(v),k,L,D,O,H,b);return W&&pr(W)&&W.data==="]"?s(k.anchor=W):(xt=!0,a(k.anchor=c("]"),L,W),W)},y=(v,k,D,O,H,b)=>{if(xt=!0,k.el=null,b){const W=E(v);for(;;){const w=s(v);if(w&&w!==W)l(w);else break}}const G=s(v),L=i(v);return l(v),n(null,k,L,G,D,O,dr(L),H),G},E=(v,k="[",D="]")=>{let O=0;for(;v;)if(v=s(v),v&&pr(v)&&(v.data===k&&O++,v.data===D)){if(O===0)return s(v);O--}return v},T=(v,k,D)=>{const O=k.parentNode;O&&O.replaceChild(v,k);let H=D;for(;H;)H.vnode.el===k&&(H.vnode.el=H.subTree.el=v),H=H.parent},x=v=>v.nodeType===1&&v.tagName.toLowerCase()==="template";return[u,f]}const Ue=Zi;function qc(e){return Gc(e,Uc)}function Gc(e,t){const n=Li();n.__VUE__=!0;const{insert:r,remove:o,patchProp:s,createElement:i,createText:l,createComment:a,setText:c,setElementText:u,parentNode:f,nextSibling:d,setScopeId:m=et,insertStaticContent:g}=e,y=(p,h,_,A=null,C=null,$=null,B=void 0,M=null,F=!!h.dynamicChildren)=>{if(p===h)return;p&&!Xt(p,h)&&(A=S(p),je(p,C,$,!0),p=null),h.patchFlag===-2&&(F=!1,h.dynamicChildren=null);const{type:P,ref:U,shapeFlag:Q}=h;switch(P){case vn:E(p,h,_,A);break;case qe:T(p,h,_,A);break;case On:p==null&&x(h,_,A,B);break;case _e:w(p,h,_,A,C,$,B,M,F);break;default:Q&1?D(p,h,_,A,C,$,B,M,F):Q&6?N(p,h,_,A,C,$,B,M,F):(Q&64||Q&128)&&P.process(p,h,_,A,C,$,B,M,F,q)}U!=null&&C&&Ar(U,p&&p.ref,$,h||p,!h)},E=(p,h,_,A)=>{if(p==null)r(h.el=l(h.children),_,A);else{const C=h.el=p.el;h.children!==p.children&&c(C,h.children)}},T=(p,h,_,A)=>{p==null?r(h.el=a(h.children||""),_,A):h.el=p.el},x=(p,h,_,A)=>{[p.el,p.anchor]=g(p.children,h,_,A,p.el,p.anchor)},v=({el:p,anchor:h},_,A)=>{let C;for(;p&&p!==h;)C=d(p),r(p,_,A),p=C;r(h,_,A)},k=({el:p,anchor:h})=>{let _;for(;p&&p!==h;)_=d(p),o(p),p=_;o(h)},D=(p,h,_,A,C,$,B,M,F)=>{h.type==="svg"?B="svg":h.type==="math"&&(B="mathml"),p==null?O(h,_,A,C,$,B,M,F):G(p,h,C,$,B,M,F)},O=(p,h,_,A,C,$,B,M)=>{let F,P;const{props:U,shapeFlag:Q,transition:Y,dirs:re}=p;if(F=p.el=i(p.type,$,U&&U.is,U),Q&8?u(F,p.children):Q&16&&b(p.children,F,null,A,C,no(p,$),B,M),re&&dt(p,null,A,"created"),H(F,p,p.scopeId,B,A),U){for(const ve in U)ve!=="value"&&!pn(ve)&&s(F,ve,null,U[ve],$,p.children,A,C,Oe);"value"in U&&s(F,"value",null,U.value,$),(P=U.onVnodeBeforeMount)&&Ze(P,A,p)}re&&dt(p,null,A,"beforeMount");const ae=gl(C,Y);ae&&Y.beforeEnter(F),r(F,h,_),((P=U&&U.onVnodeMounted)||ae||re)&&Ue(()=>{P&&Ze(P,A,p),ae&&Y.enter(F),re&&dt(p,null,A,"mounted")},C)},H=(p,h,_,A,C)=>{if(_&&m(p,_),A)for(let $=0;${for(let P=F;P{const M=h.el=p.el;let{patchFlag:F,dynamicChildren:P,dirs:U}=h;F|=p.patchFlag&16;const Q=p.props||ye,Y=h.props||ye;let re;if(_&&Vt(_,!1),(re=Y.onVnodeBeforeUpdate)&&Ze(re,_,h,p),U&&dt(h,p,_,"beforeUpdate"),_&&Vt(_,!0),P?L(p.dynamicChildren,P,M,_,A,no(h,C),$):B||V(p,h,M,null,_,A,no(h,C),$,!1),F>0){if(F&16)W(M,h,Q,Y,_,A,C);else if(F&2&&Q.class!==Y.class&&s(M,"class",null,Y.class,C),F&4&&s(M,"style",Q.style,Y.style,C),F&8){const ae=h.dynamicProps;for(let ve=0;ve{re&&Ze(re,_,h,p),U&&dt(h,p,_,"updated")},A)},L=(p,h,_,A,C,$,B)=>{for(let M=0;M{if(_!==A){if(_!==ye)for(const M in _)!pn(M)&&!(M in A)&&s(p,M,_[M],null,B,h.children,C,$,Oe);for(const M in A){if(pn(M))continue;const F=A[M],P=_[M];F!==P&&M!=="value"&&s(p,M,P,F,B,h.children,C,$,Oe)}"value"in A&&s(p,"value",_.value,A.value,B)}},w=(p,h,_,A,C,$,B,M,F)=>{const P=h.el=p?p.el:l(""),U=h.anchor=p?p.anchor:l("");let{patchFlag:Q,dynamicChildren:Y,slotScopeIds:re}=h;re&&(M=M?M.concat(re):re),p==null?(r(P,_,A),r(U,_,A),b(h.children||[],_,U,C,$,B,M,F)):Q>0&&Q&64&&Y&&p.dynamicChildren?(L(p.dynamicChildren,Y,_,C,$,B,M),(h.key!=null||C&&h===C.subTree)&&vl(p,h,!0)):V(p,h,_,U,C,$,B,M,F)},N=(p,h,_,A,C,$,B,M,F)=>{h.slotScopeIds=M,p==null?h.shapeFlag&512?C.ctx.activate(h,_,A,B,F):te(h,_,A,C,$,B,F):le(p,h,F)},te=(p,h,_,A,C,$,B)=>{const M=p.component=ru(p,A,C);if(Zn(p)&&(M.ctx.renderer=q),ou(M),M.asyncDep){if(C&&C.registerDep(M,R),!p.el){const F=M.subTree=ie(qe);T(null,F,h,_)}}else R(M,p,h,_,C,$,B)},le=(p,h,_)=>{const A=h.component=p.component;if(ac(p,h,_))if(A.asyncDep&&!A.asyncResolved){J(A,h,_);return}else A.next=h,nc(A.update),A.effect.dirty=!0,A.update();else h.el=p.el,A.vnode=h},R=(p,h,_,A,C,$,B)=>{const M=()=>{if(p.isMounted){let{next:U,bu:Q,u:Y,parent:re,vnode:ae}=p;{const an=bl(p);if(an){U&&(U.el=ae.el,J(p,U,B)),an.asyncDep.then(()=>{p.isUnmounted||M()});return}}let ve=U,ke;Vt(p,!1),U?(U.el=ae.el,J(p,U,B)):U=ae,Q&&Yr(Q),(ke=U.props&&U.props.onVnodeBeforeUpdate)&&Ze(ke,re,U,ae),Vt(p,!0);const $e=Qr(p),rt=p.subTree;p.subTree=$e,y(rt,$e,f(rt.el),S(rt),p,C,$),U.el=$e.el,ve===null&&cc(p,$e.el),Y&&Ue(Y,C),(ke=U.props&&U.props.onVnodeUpdated)&&Ue(()=>Ze(ke,re,U,ae),C)}else{let U;const{el:Q,props:Y}=h,{bm:re,m:ae,parent:ve}=p,ke=mn(h);if(Vt(p,!1),re&&Yr(re),!ke&&(U=Y&&Y.onVnodeBeforeMount)&&Ze(U,ve,h),Vt(p,!0),Q&&ge){const $e=()=>{p.subTree=Qr(p),ge(Q,p.subTree,p,C,null)};ke?h.type.__asyncLoader().then(()=>!p.isUnmounted&&$e()):$e()}else{const $e=p.subTree=Qr(p);y(null,$e,_,A,p,C,$),h.el=$e.el}if(ae&&Ue(ae,C),!ke&&(U=Y&&Y.onVnodeMounted)){const $e=h;Ue(()=>Ze(U,ve,$e),C)}(h.shapeFlag&256||ve&&mn(ve.vnode)&&ve.vnode.shapeFlag&256)&&p.a&&Ue(p.a,C),p.isMounted=!0,h=_=A=null}},F=p.effect=new Mo(M,et,()=>Fr(P),p.scope),P=p.update=()=>{F.dirty&&F.run()};P.id=p.uid,Vt(p,!0),P()},J=(p,h,_)=>{h.component=p;const A=p.vnode.props;p.vnode=h,p.next=null,jc(p,h.props,A,_),Kc(p,h.children,_),Bt(),vs(p),zt()},V=(p,h,_,A,C,$,B,M,F=!1)=>{const P=p&&p.children,U=p?p.shapeFlag:0,Q=h.children,{patchFlag:Y,shapeFlag:re}=h;if(Y>0){if(Y&128){Fe(P,Q,_,A,C,$,B,M,F);return}else if(Y&256){Ie(P,Q,_,A,C,$,B,M,F);return}}re&8?(U&16&&Oe(P,C,$),Q!==P&&u(_,Q)):U&16?re&16?Fe(P,Q,_,A,C,$,B,M,F):Oe(P,C,$,!0):(U&8&&u(_,""),re&16&&b(Q,_,A,C,$,B,M,F))},Ie=(p,h,_,A,C,$,B,M,F)=>{p=p||fn,h=h||fn;const P=p.length,U=h.length,Q=Math.min(P,U);let Y;for(Y=0;YU?Oe(p,C,$,!0,!1,Q):b(h,_,A,C,$,B,M,F,Q)},Fe=(p,h,_,A,C,$,B,M,F)=>{let P=0;const U=h.length;let Q=p.length-1,Y=U-1;for(;P<=Q&&P<=Y;){const re=p[P],ae=h[P]=F?Rt(h[P]):ot(h[P]);if(Xt(re,ae))y(re,ae,_,null,C,$,B,M,F);else break;P++}for(;P<=Q&&P<=Y;){const re=p[Q],ae=h[Y]=F?Rt(h[Y]):ot(h[Y]);if(Xt(re,ae))y(re,ae,_,null,C,$,B,M,F);else break;Q--,Y--}if(P>Q){if(P<=Y){const re=Y+1,ae=reY)for(;P<=Q;)je(p[P],C,$,!0),P++;else{const re=P,ae=P,ve=new Map;for(P=ae;P<=Y;P++){const Ye=h[P]=F?Rt(h[P]):ot(h[P]);Ye.key!=null&&ve.set(Ye.key,P)}let ke,$e=0;const rt=Y-ae+1;let an=!1,is=0;const kn=new Array(rt);for(P=0;P=rt){je(Ye,C,$,!0);continue}let ft;if(Ye.key!=null)ft=ve.get(Ye.key);else for(ke=ae;ke<=Y;ke++)if(kn[ke-ae]===0&&Xt(Ye,h[ke])){ft=ke;break}ft===void 0?je(Ye,C,$,!0):(kn[ft-ae]=P+1,ft>=is?is=ft:an=!0,y(Ye,h[ft],_,null,C,$,B,M,F),$e++)}const ls=an?Jc(kn):fn;for(ke=ls.length-1,P=rt-1;P>=0;P--){const Ye=ae+P,ft=h[Ye],as=Ye+1{const{el:$,type:B,transition:M,children:F,shapeFlag:P}=p;if(P&6){Ve(p.component.subTree,h,_,A);return}if(P&128){p.suspense.move(h,_,A);return}if(P&64){B.move(p,h,_,q);return}if(B===_e){r($,h,_);for(let Q=0;QM.enter($),C);else{const{leave:Q,delayLeave:Y,afterLeave:re}=M,ae=()=>r($,h,_),ve=()=>{Q($,()=>{ae(),re&&re()})};Y?Y($,ae,ve):ve()}else r($,h,_)},je=(p,h,_,A=!1,C=!1)=>{const{type:$,props:B,ref:M,children:F,dynamicChildren:P,shapeFlag:U,patchFlag:Q,dirs:Y}=p;if(M!=null&&Ar(M,null,_,p,!0),U&256){h.ctx.deactivate(p);return}const re=U&1&&Y,ae=!mn(p);let ve;if(ae&&(ve=B&&B.onVnodeBeforeUnmount)&&Ze(ve,h,p),U&6)ut(p.component,_,A);else{if(U&128){p.suspense.unmount(_,A);return}re&&dt(p,null,h,"beforeUnmount"),U&64?p.type.remove(p,h,_,C,q,A):P&&($!==_e||Q>0&&Q&64)?Oe(P,h,_,!1,!0):($===_e&&Q&384||!C&&U&16)&&Oe(F,h,_),A&&Et(p)}(ae&&(ve=B&&B.onVnodeUnmounted)||re)&&Ue(()=>{ve&&Ze(ve,h,p),re&&dt(p,null,h,"unmounted")},_)},Et=p=>{const{type:h,el:_,anchor:A,transition:C}=p;if(h===_e){Ct(_,A);return}if(h===On){k(p);return}const $=()=>{o(_),C&&!C.persisted&&C.afterLeave&&C.afterLeave()};if(p.shapeFlag&1&&C&&!C.persisted){const{leave:B,delayLeave:M}=C,F=()=>B(_,$);M?M(p.el,$,F):F()}else $()},Ct=(p,h)=>{let _;for(;p!==h;)_=d(p),o(p),p=_;o(h)},ut=(p,h,_)=>{const{bum:A,scope:C,update:$,subTree:B,um:M}=p;A&&Yr(A),C.stop(),$&&($.active=!1,je(B,p,h,_)),M&&Ue(M,h),Ue(()=>{p.isUnmounted=!0},h),h&&h.pendingBranch&&!h.isUnmounted&&p.asyncDep&&!p.asyncResolved&&p.suspenseId===h.pendingId&&(h.deps--,h.deps===0&&h.resolve())},Oe=(p,h,_,A=!1,C=!1,$=0)=>{for(let B=$;Bp.shapeFlag&6?S(p.component.subTree):p.shapeFlag&128?p.suspense.next():d(p.anchor||p.el);let K=!1;const j=(p,h,_)=>{p==null?h._vnode&&je(h._vnode,null,null,!0):y(h._vnode||null,p,h,null,null,null,_),K||(K=!0,vs(),Cr(),K=!1),h._vnode=p},q={p:y,um:je,m:Ve,r:Et,mt:te,mc:b,pc:V,pbc:L,n:S,o:e};let ue,ge;return t&&([ue,ge]=t(q)),{render:j,hydrate:ue,createApp:Fc(j,ue)}}function no({type:e,props:t},n){return n==="svg"&&e==="foreignObject"||n==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:n}function Vt({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function gl(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function vl(e,t,n=!1){const r=e.children,o=t.children;if(ee(r)&&ee(o))for(let s=0;s>1,e[n[l]]0&&(t[r]=n[s-1]),n[s]=r)}}for(s=n.length,i=n[s-1];s-- >0;)n[s]=i,i=t[i];return n}function bl(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:bl(t)}const Yc=e=>e.__isTeleport,_e=Symbol.for("v-fgt"),vn=Symbol.for("v-txt"),qe=Symbol.for("v-cmt"),On=Symbol.for("v-stc"),$n=[];let st=null;function z(e=!1){$n.push(st=e?null:[])}function Xc(){$n.pop(),st=$n[$n.length-1]||null}let Bn=1;function Ts(e){Bn+=e}function _l(e){return e.dynamicChildren=Bn>0?st||fn:null,Xc(),Bn>0&&st&&st.push(e),e}function Z(e,t,n,r,o,s){return _l(ne(e,t,n,r,o,s,!0))}function Se(e,t,n,r,o){return _l(ie(e,t,n,r,o,!0))}function Tr(e){return e?e.__v_isVNode===!0:!1}function Xt(e,t){return e.type===t.type&&e.key===t.key}const yl=({key:e})=>e??null,_r=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?Te(e)||Ke(e)||se(e)?{i:Re,r:e,k:t,f:!!n}:e:null);function ne(e,t=null,n=null,r=0,o=null,s=e===_e?0:1,i=!1,l=!1){const a={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&yl(t),ref:t&&_r(t),scopeId:Xi,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:s,patchFlag:r,dynamicProps:o,dynamicChildren:null,appContext:null,ctx:Re};return l?(qo(a,n),s&128&&e.normalize(a)):n&&(a.shapeFlag|=Te(n)?8:16),Bn>0&&!i&&st&&(a.patchFlag>0||s&6)&&a.patchFlag!==32&&st.push(a),a}const ie=Qc;function Qc(e,t=null,n=null,r=0,o=null,s=!1){if((!e||e===uc)&&(e=qe),Tr(e)){const l=Dt(e,t,!0);return n&&qo(l,n),Bn>0&&!s&&st&&(l.shapeFlag&6?st[st.indexOf(e)]=l:st.push(l)),l.patchFlag|=-2,l}if(cu(e)&&(e=e.__vccOpts),t){t=Zc(t);let{class:l,style:a}=t;l&&!Te(l)&&(t.class=Ge(l)),we(a)&&(Ki(a)&&!ee(a)&&(a=Ae({},a)),t.style=Yn(a))}const i=Te(e)?1:dc(e)?128:Yc(e)?64:we(e)?4:se(e)?2:0;return ne(e,t,n,r,o,i,s,!0)}function Zc(e){return e?Ki(e)||ul(e)?Ae({},e):e:null}function Dt(e,t,n=!1,r=!1){const{props:o,ref:s,patchFlag:i,children:l,transition:a}=e,c=t?Co(o||{},t):o,u={__v_isVNode:!0,__v_skip:!0,type:e.type,props:c,key:c&&yl(c),ref:t&&t.ref?n&&s?ee(s)?s.concat(_r(t)):[s,_r(t)]:_r(t):s,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:l,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==_e?i===-1?16:i|16:i,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:a,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Dt(e.ssContent),ssFallback:e.ssFallback&&Dt(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return a&&r&&(u.transition=a.clone(u)),u}function mt(e=" ",t=0){return ie(vn,null,e,t)}function eu(e,t){const n=ie(On,null,e);return n.staticCount=t,n}function Le(e="",t=!1){return t?(z(),Se(qe,null,e)):ie(qe,null,e)}function ot(e){return e==null||typeof e=="boolean"?ie(qe):ee(e)?ie(_e,null,e.slice()):typeof e=="object"?Rt(e):ie(vn,null,String(e))}function Rt(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Dt(e)}function qo(e,t){let n=0;const{shapeFlag:r}=e;if(t==null)t=null;else if(ee(t))n=16;else if(typeof t=="object")if(r&65){const o=t.default;o&&(o._c&&(o._d=!1),qo(e,o()),o._c&&(o._d=!0));return}else{n=32;const o=t._;!o&&!ul(t)?t._ctx=Re:o===3&&Re&&(Re.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else se(t)?(t={default:t,_ctx:Re},n=32):(t=String(t),r&64?(n=16,t=[mt(t)]):n=8);e.children=t,e.shapeFlag|=n}function Co(...e){const t={};for(let n=0;nMe||Re;let Pr,So;{const e=Li(),t=(n,r)=>{let o;return(o=e[n])||(o=e[n]=[]),o.push(r),s=>{o.length>1?o.forEach(i=>i(s)):o[0](s)}};Pr=t("__VUE_INSTANCE_SETTERS__",n=>Me=n),So=t("__VUE_SSR_SETTERS__",n=>tr=n)}const er=e=>{const t=Me;return Pr(e),e.scope.on(),()=>{e.scope.off(),Pr(t)}},Ps=()=>{Me&&Me.scope.off(),Pr(null)};function wl(e){return e.vnode.shapeFlag&4}let tr=!1;function ou(e,t=!1){t&&So(t);const{props:n,children:r}=e.vnode,o=wl(e);Dc(e,n,o,t),zc(e,r);const s=o?su(e,t):void 0;return t&&So(!1),s}function su(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,Rc);const{setup:r}=n;if(r){const o=e.setupContext=r.length>1?lu(e):null,s=er(e);Bt();const i=Mt(r,e,0,[e.props,o]);if(zt(),s(),Ei(i)){if(i.then(Ps,Ps),t)return i.then(l=>{Rs(e,l,t)}).catch(l=>{Qn(l,e,0)});e.asyncDep=i}else Rs(e,i,t)}else kl(e,t)}function Rs(e,t,n){se(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:we(t)&&(e.setupState=Ui(t)),kl(e,n)}let Is;function kl(e,t,n){const r=e.type;if(!e.render){if(!t&&Is&&!r.render){const o=r.template||Vo(e).template;if(o){const{isCustomElement:s,compilerOptions:i}=e.appContext.config,{delimiters:l,compilerOptions:a}=r,c=Ae(Ae({isCustomElement:s,delimiters:l},i),a);r.render=Is(o,c)}}e.render=r.render||et}{const o=er(e);Bt();try{Ic(e)}finally{zt(),o()}}}const iu={get(e,t){return Je(e,"get",""),e[t]}};function lu(e){const t=n=>{e.exposed=n||{}};return{attrs:new Proxy(e.attrs,iu),slots:e.slots,emit:e.emit,expose:t}}function Wr(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(Ui(Ua(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Rn)return Rn[n](e)},has(t,n){return n in t||n in Rn}}))}function au(e,t=!0){return se(e)?e.displayName||e.name:e.name||t&&e.__name}function cu(e){return se(e)&&"__vccOpts"in e}const I=(e,t)=>qa(e,t,tr);function oe(e,t,n){const r=arguments.length;return r===2?we(t)&&!ee(t)?Tr(t)?ie(e,null,[t]):ie(e,t):ie(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&Tr(n)&&(n=[n]),ie(e,t,n))}const uu="3.4.26";/** +* @vue/runtime-dom v3.4.26 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/const fu="http://www.w3.org/2000/svg",du="http://www.w3.org/1998/Math/MathML",It=typeof document<"u"?document:null,Os=It&&It.createElement("template"),pu={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{const o=t==="svg"?It.createElementNS(fu,e):t==="mathml"?It.createElementNS(du,e):It.createElement(e,n?{is:n}:void 0);return e==="select"&&r&&r.multiple!=null&&o.setAttribute("multiple",r.multiple),o},createText:e=>It.createTextNode(e),createComment:e=>It.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>It.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,r,o,s){const i=n?n.previousSibling:t.lastChild;if(o&&(o===s||o.nextSibling))for(;t.insertBefore(o.cloneNode(!0),n),!(o===s||!(o=o.nextSibling)););else{Os.innerHTML=r==="svg"?`${e}`:r==="mathml"?`${e}`:e;const l=Os.content;if(r==="svg"||r==="mathml"){const a=l.firstChild;for(;a.firstChild;)l.appendChild(a.firstChild);l.removeChild(a)}t.insertBefore(l,n)}return[i?i.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},Lt="transition",En="animation",zn=Symbol("_vtc"),wn=(e,{slots:t})=>oe(_c,hu(e),t);wn.displayName="Transition";const El={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String};wn.props=Ae({},tl,El);const Ut=(e,t=[])=>{ee(e)?e.forEach(n=>n(...t)):e&&e(...t)},$s=e=>e?ee(e)?e.some(t=>t.length>1):e.length>1:!1;function hu(e){const t={};for(const w in e)w in El||(t[w]=e[w]);if(e.css===!1)return t;const{name:n="v",type:r,duration:o,enterFromClass:s=`${n}-enter-from`,enterActiveClass:i=`${n}-enter-active`,enterToClass:l=`${n}-enter-to`,appearFromClass:a=s,appearActiveClass:c=i,appearToClass:u=l,leaveFromClass:f=`${n}-leave-from`,leaveActiveClass:d=`${n}-leave-active`,leaveToClass:m=`${n}-leave-to`}=e,g=mu(o),y=g&&g[0],E=g&&g[1],{onBeforeEnter:T,onEnter:x,onEnterCancelled:v,onLeave:k,onLeaveCancelled:D,onBeforeAppear:O=T,onAppear:H=x,onAppearCancelled:b=v}=t,G=(w,N,te)=>{qt(w,N?u:l),qt(w,N?c:i),te&&te()},L=(w,N)=>{w._isLeaving=!1,qt(w,f),qt(w,m),qt(w,d),N&&N()},W=w=>(N,te)=>{const le=w?H:x,R=()=>G(N,w,te);Ut(le,[N,R]),Ms(()=>{qt(N,w?a:s),At(N,w?u:l),$s(le)||Ns(N,r,y,R)})};return Ae(t,{onBeforeEnter(w){Ut(T,[w]),At(w,s),At(w,i)},onBeforeAppear(w){Ut(O,[w]),At(w,a),At(w,c)},onEnter:W(!1),onAppear:W(!0),onLeave(w,N){w._isLeaving=!0;const te=()=>L(w,N);At(w,f),At(w,d),bu(),Ms(()=>{w._isLeaving&&(qt(w,f),At(w,m),$s(k)||Ns(w,r,E,te))}),Ut(k,[w,te])},onEnterCancelled(w){G(w,!1),Ut(v,[w])},onAppearCancelled(w){G(w,!0),Ut(b,[w])},onLeaveCancelled(w){L(w),Ut(D,[w])}})}function mu(e){if(e==null)return null;if(we(e))return[ro(e.enter),ro(e.leave)];{const t=ro(e);return[t,t]}}function ro(e){return ga(e)}function At(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e[zn]||(e[zn]=new Set)).add(t)}function qt(e,t){t.split(/\s+/).forEach(r=>r&&e.classList.remove(r));const n=e[zn];n&&(n.delete(t),n.size||(e[zn]=void 0))}function Ms(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let gu=0;function Ns(e,t,n,r){const o=e._endId=++gu,s=()=>{o===e._endId&&r()};if(n)return setTimeout(s,n);const{type:i,timeout:l,propCount:a}=vu(e,t);if(!i)return r();const c=i+"end";let u=0;const f=()=>{e.removeEventListener(c,d),s()},d=m=>{m.target===e&&++u>=a&&f()};setTimeout(()=>{u(n[g]||"").split(", "),o=r(`${Lt}Delay`),s=r(`${Lt}Duration`),i=Hs(o,s),l=r(`${En}Delay`),a=r(`${En}Duration`),c=Hs(l,a);let u=null,f=0,d=0;t===Lt?i>0&&(u=Lt,f=i,d=s.length):t===En?c>0&&(u=En,f=c,d=a.length):(f=Math.max(i,c),u=f>0?i>c?Lt:En:null,d=u?u===Lt?s.length:a.length:0);const m=u===Lt&&/\b(transform|all)(,|$)/.test(r(`${Lt}Property`).toString());return{type:u,timeout:f,propCount:d,hasTransform:m}}function Hs(e,t){for(;e.lengthFs(n)+Fs(e[r])))}function Fs(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function bu(){return document.body.offsetHeight}function _u(e,t,n){const r=e[zn];r&&(t=(t?[t,...r]:[...r]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}const Rr=Symbol("_vod"),Cl=Symbol("_vsh"),Ir={beforeMount(e,{value:t},{transition:n}){e[Rr]=e.style.display==="none"?"":e.style.display,n&&t?n.beforeEnter(e):Cn(e,t)},mounted(e,{value:t},{transition:n}){n&&t&&n.enter(e)},updated(e,{value:t,oldValue:n},{transition:r}){!t!=!n&&(r?t?(r.beforeEnter(e),Cn(e,!0),r.enter(e)):r.leave(e,()=>{Cn(e,!1)}):Cn(e,t))},beforeUnmount(e,{value:t}){Cn(e,t)}};function Cn(e,t){e.style.display=t?e[Rr]:"none",e[Cl]=!t}const yu=Symbol(""),wu=/(^|;)\s*display\s*:/;function ku(e,t,n){const r=e.style,o=Te(n);let s=!1;if(n&&!o){if(t)if(Te(t))for(const i of t.split(";")){const l=i.slice(0,i.indexOf(":")).trim();n[l]==null&&yr(r,l,"")}else for(const i in t)n[i]==null&&yr(r,i,"");for(const i in n)i==="display"&&(s=!0),yr(r,i,n[i])}else if(o){if(t!==n){const i=r[yu];i&&(n+=";"+i),r.cssText=n,s=wu.test(n)}}else t&&e.removeAttribute("style");Rr in e&&(e[Rr]=s?r.display:"",e[Cl]&&(r.display="none"))}const Ds=/\s*!important$/;function yr(e,t,n){if(ee(n))n.forEach(r=>yr(e,t,r));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const r=Eu(e,t);Ds.test(n)?e.setProperty(on(r),n.replace(Ds,""),"important"):e[r]=n}}const js=["Webkit","Moz","ms"],oo={};function Eu(e,t){const n=oo[t];if(n)return n;let r=nt(t);if(r!=="filter"&&r in e)return oo[t]=r;r=Jn(r);for(let o=0;oso||(Pu.then(()=>so=0),so=Date.now());function Iu(e,t){const n=r=>{if(!r._vts)r._vts=Date.now();else if(r._vts<=n.attached)return;tt(Ou(r,n.value),t,5,[r])};return n.value=e,n.attached=Ru(),n}function Ou(e,t){if(ee(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(r=>o=>!o._stopped&&r&&r(o))}else return t}const Ws=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,$u=(e,t,n,r,o,s,i,l,a)=>{const c=o==="svg";t==="class"?_u(e,r,c):t==="style"?ku(e,n,r):Gn(t)?Io(t)||Au(e,t,n,r,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Mu(e,t,r,c))?Su(e,t,r,s,i,l,a):(t==="true-value"?e._trueValue=r:t==="false-value"&&(e._falseValue=r),Cu(e,t,r,c))};function Mu(e,t,n,r){if(r)return!!(t==="innerHTML"||t==="textContent"||t in e&&Ws(t)&&se(n));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const o=e.tagName;if(o==="IMG"||o==="VIDEO"||o==="CANVAS"||o==="SOURCE")return!1}return Ws(t)&&Te(n)?!1:t in e}const Nu={esc:"escape",space:" ",up:"arrow-up",left:"arrow-left",right:"arrow-right",down:"arrow-down",delete:"backspace"},Hu=(e,t)=>{const n=e._withKeys||(e._withKeys={}),r=t.join(".");return n[r]||(n[r]=o=>{if(!("key"in o))return;const s=on(o.key);if(t.some(i=>i===s||Nu[i]===s))return e(o)})},Fu=Ae({patchProp:$u},pu);let io,Vs=!1;function Du(){return io=Vs?io:qc(Fu),Vs=!0,io}const ju=(...e)=>{const t=Du().createApp(...e),{mount:n}=t;return t.mount=r=>{const o=zu(r);if(o)return n(o,!0,Bu(o))},t};function Bu(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function zu(e){return Te(e)?document.querySelector(e):e}var Ku=["link","meta","script","style","noscript","template"],Wu=["title","base"],Vu=([e,t,n])=>Wu.includes(e)?e:Ku.includes(e)?e==="meta"&&t.name?`${e}.${t.name}`:e==="template"&&t.id?`${e}.${t.id}`:JSON.stringify([e,Object.entries(t).map(([r,o])=>typeof o=="boolean"?o?[r,""]:null:[r,o]).filter(r=>r!=null).sort(([r],[o])=>r.localeCompare(o)),n]):null,Uu=e=>{const t=new Set,n=[];return e.forEach(r=>{const o=Vu(r);o&&!t.has(o)&&(t.add(o),n.push(r))}),n},nr=e=>/^(https?:)?\/\//.test(e),Sl=e=>/^[a-z][a-z0-9+.-]*:/.test(e),Go=e=>Object.prototype.toString.call(e)==="[object Object]",qu=e=>{const[t,...n]=e.split(/(\?|#)/);if(!t||t.endsWith("/"))return e;let r=t.replace(/(^|\/)README.md$/i,"$1index.html");return r.endsWith(".md")?r=r.substring(0,r.length-3)+".html":r.endsWith(".html")||(r=r+".html"),r.endsWith("/index.html")&&(r=r.substring(0,r.length-10)),r+n.join("")},xl=e=>e[e.length-1]==="/"?e.slice(0,-1):e,Ll=e=>e[0]==="/"?e.slice(1):e,Al=(e,t)=>{const n=Object.keys(e).sort((r,o)=>{const s=o.split("/").length-r.split("/").length;return s!==0?s:o.length-r.length});for(const r of n)if(t.startsWith(r))return r;return"/"},it=e=>typeof e=="string";const Gu="modulepreload",Ju=function(e){return"/learning-kotlin/"+e},Us={},Pe=function(t,n,r){let o=Promise.resolve();if(n&&n.length>0){const s=document.getElementsByTagName("link"),i=document.querySelector("meta[property=csp-nonce]"),l=(i==null?void 0:i.nonce)||(i==null?void 0:i.getAttribute("nonce"));o=Promise.all(n.map(a=>{if(a=Ju(a),a in Us)return;Us[a]=!0;const c=a.endsWith(".css"),u=c?'[rel="stylesheet"]':"";if(!!r)for(let m=s.length-1;m>=0;m--){const g=s[m];if(g.href===a&&(!c||g.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${a}"]${u}`))return;const d=document.createElement("link");if(d.rel=c?"stylesheet":Gu,c||(d.as="script",d.crossOrigin=""),d.href=a,l&&d.setAttribute("nonce",l),document.head.appendChild(d),c)return new Promise((m,g)=>{d.addEventListener("load",m),d.addEventListener("error",()=>g(new Error(`Unable to preload CSS for ${a}`)))})}))}return o.then(()=>t()).catch(s=>{const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=s,window.dispatchEvent(i),!i.defaultPrevented)throw s})},Yu=JSON.parse("{}"),Xu=Object.fromEntries([["/",{loader:()=>Pe(()=>import("./index.html-C_I161o0.js"),__vite__mapDeps([0,1])),meta:{title:"Welcome"}}],["/en/",{loader:()=>Pe(()=>import("./index.html-CDMPFPby.js"),__vite__mapDeps([2,1])),meta:{title:"Welcome"}}],["/fr/",{loader:()=>Pe(()=>import("./index.html-B8Y7MGp7.js"),[]),meta:{title:""}}],["/en/backend-development/",{loader:()=>Pe(()=>import("./index.html-DJx1WQoN.js"),[]),meta:{title:"📚 Backend development"}}],["/en/front-development/",{loader:()=>Pe(()=>import("./index.html-DuVz8Jxd.js"),__vite__mapDeps([3,4])),meta:{title:"📚 Frontend development"}}],["/en/kotlin-features/",{loader:()=>Pe(()=>import("./index.html-8yMvNPCQ.js"),[]),meta:{title:"📚 Kotlin language features"}}],["/en/kotlin-features-advanced/",{loader:()=>Pe(()=>import("./index.html-D9WLw91N.js"),[]),meta:{title:"📚 Advanced and other Kotlin features"}}],["/en/other-technologies/",{loader:()=>Pe(()=>import("./index.html-CrQuKRiG.js"),__vite__mapDeps([5,6])),meta:{title:"🛠 Let's make a cross-plaform app !"}}],["/en/presentation/",{loader:()=>Pe(()=>import("./index.html-BOBxFfL8.js"),__vite__mapDeps([7,8])),meta:{title:"🚀 Presentation of Kotlin"}}],["/en/workshops/",{loader:()=>Pe(()=>import("./index.html-B1GaKHtm.js"),__vite__mapDeps([9,10])),meta:{title:"📅 Workshops"}}],["/fr/backend-development/",{loader:()=>Pe(()=>import("./index.html-hW5i8q7d.js"),[]),meta:{title:"📚 Développement du backend"}}],["/fr/front-development/",{loader:()=>Pe(()=>import("./index.html-BGxKf9ec.js"),__vite__mapDeps([11,4])),meta:{title:"📚 Développement frontend"}}],["/fr/kotlin-features/",{loader:()=>Pe(()=>import("./index.html-pJuZpkgy.js"),[]),meta:{title:"📚 Fonctionnalités du langage Kotlin"}}],["/fr/kotlin-features-advanced/",{loader:()=>Pe(()=>import("./index.html-BBhIJwI8.js"),[]),meta:{title:"📚 Fonctionnalités avancées de Kotlin"}}],["/fr/other-technologies/",{loader:()=>Pe(()=>import("./index.html-BEuoD-gh.js"),__vite__mapDeps([12,6])),meta:{title:"🛠 Construisons une app multiplateforme !"}}],["/fr/presentation/",{loader:()=>Pe(()=>import("./index.html-Cd7wtrxg.js"),__vite__mapDeps([13,8])),meta:{title:"🚀 Présentation de Kotlin"}}],["/fr/workshops/",{loader:()=>Pe(()=>import("./index.html-mnsf-FXr.js"),__vite__mapDeps([14,10])),meta:{title:"📅 Workshops"}}],["/404.html",{loader:()=>Pe(()=>import("./404.html-CtZ3hJcg.js"),[]),meta:{title:""}}]]);/*! + * vue-router v4.3.0 + * (c) 2024 Eduardo San Martin Morote + * @license MIT + */const un=typeof document<"u";function Qu(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const me=Object.assign;function lo(e,t){const n={};for(const r in t){const o=t[r];n[r]=lt(o)?o.map(e):e(o)}return n}const Mn=()=>{},lt=Array.isArray,Tl=/#/g,Zu=/&/g,ef=/\//g,tf=/=/g,nf=/\?/g,Pl=/\+/g,rf=/%5B/g,of=/%5D/g,Rl=/%5E/g,sf=/%60/g,Il=/%7B/g,lf=/%7C/g,Ol=/%7D/g,af=/%20/g;function Jo(e){return encodeURI(""+e).replace(lf,"|").replace(rf,"[").replace(of,"]")}function cf(e){return Jo(e).replace(Il,"{").replace(Ol,"}").replace(Rl,"^")}function xo(e){return Jo(e).replace(Pl,"%2B").replace(af,"+").replace(Tl,"%23").replace(Zu,"%26").replace(sf,"`").replace(Il,"{").replace(Ol,"}").replace(Rl,"^")}function uf(e){return xo(e).replace(tf,"%3D")}function ff(e){return Jo(e).replace(Tl,"%23").replace(nf,"%3F")}function df(e){return e==null?"":ff(e).replace(ef,"%2F")}function Kn(e){try{return decodeURIComponent(""+e)}catch{}return""+e}const pf=/\/$/,hf=e=>e.replace(pf,"");function ao(e,t,n="/"){let r,o={},s="",i="";const l=t.indexOf("#");let a=t.indexOf("?");return l=0&&(a=-1),a>-1&&(r=t.slice(0,a),s=t.slice(a+1,l>-1?l:t.length),o=e(s)),l>-1&&(r=r||t.slice(0,l),i=t.slice(l,t.length)),r=bf(r??t,n),{fullPath:r+(s&&"?")+s+i,path:r,query:o,hash:Kn(i)}}function mf(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function qs(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function gf(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;return r>-1&&r===o&&bn(t.matched[r],n.matched[o])&&$l(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function bn(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function $l(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!vf(e[n],t[n]))return!1;return!0}function vf(e,t){return lt(e)?Gs(e,t):lt(t)?Gs(t,e):e===t}function Gs(e,t){return lt(t)?e.length===t.length&&e.every((n,r)=>n===t[r]):e.length===1&&e[0]===t}function bf(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/"),o=r[r.length-1];(o===".."||o===".")&&r.push("");let s=n.length-1,i,l;for(i=0;i1&&s--;else break;return n.slice(0,s).join("/")+"/"+r.slice(i).join("/")}var Wn;(function(e){e.pop="pop",e.push="push"})(Wn||(Wn={}));var Nn;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Nn||(Nn={}));function _f(e){if(!e)if(un){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),hf(e)}const yf=/^[^#]+#/;function wf(e,t){return e.replace(yf,"#")+t}function kf(e,t){const n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}const Vr=()=>({left:window.scrollX,top:window.scrollY});function Ef(e){let t;if("el"in e){const n=e.el,r=typeof n=="string"&&n.startsWith("#"),o=typeof n=="string"?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!o)return;t=kf(o,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.scrollX,t.top!=null?t.top:window.scrollY)}function Js(e,t){return(history.state?history.state.position-t:-1)+e}const Lo=new Map;function Cf(e,t){Lo.set(e,t)}function Sf(e){const t=Lo.get(e);return Lo.delete(e),t}let xf=()=>location.protocol+"//"+location.host;function Ml(e,t){const{pathname:n,search:r,hash:o}=t,s=e.indexOf("#");if(s>-1){let l=o.includes(e.slice(s))?e.slice(s).length:1,a=o.slice(l);return a[0]!=="/"&&(a="/"+a),qs(a,"")}return qs(n,e)+r+o}function Lf(e,t,n,r){let o=[],s=[],i=null;const l=({state:d})=>{const m=Ml(e,location),g=n.value,y=t.value;let E=0;if(d){if(n.value=m,t.value=d,i&&i===g){i=null;return}E=y?d.position-y.position:0}else r(m);o.forEach(T=>{T(n.value,g,{delta:E,type:Wn.pop,direction:E?E>0?Nn.forward:Nn.back:Nn.unknown})})};function a(){i=n.value}function c(d){o.push(d);const m=()=>{const g=o.indexOf(d);g>-1&&o.splice(g,1)};return s.push(m),m}function u(){const{history:d}=window;d.state&&d.replaceState(me({},d.state,{scroll:Vr()}),"")}function f(){for(const d of s)d();s=[],window.removeEventListener("popstate",l),window.removeEventListener("beforeunload",u)}return window.addEventListener("popstate",l),window.addEventListener("beforeunload",u,{passive:!0}),{pauseListeners:a,listen:c,destroy:f}}function Ys(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:o?Vr():null}}function Af(e){const{history:t,location:n}=window,r={value:Ml(e,n)},o={value:t.state};o.value||s(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function s(a,c,u){const f=e.indexOf("#"),d=f>-1?(n.host&&document.querySelector("base")?e:e.slice(f))+a:xf()+e+a;try{t[u?"replaceState":"pushState"](c,"",d),o.value=c}catch(m){console.error(m),n[u?"replace":"assign"](d)}}function i(a,c){const u=me({},t.state,Ys(o.value.back,a,o.value.forward,!0),c,{position:o.value.position});s(a,u,!0),r.value=a}function l(a,c){const u=me({},o.value,t.state,{forward:a,scroll:Vr()});s(u.current,u,!0);const f=me({},Ys(r.value,a,null),{position:u.position+1},c);s(a,f,!1),r.value=a}return{location:r,state:o,push:l,replace:i}}function Tf(e){e=_f(e);const t=Af(e),n=Lf(e,t.state,t.location,t.replace);function r(s,i=!0){i||n.pauseListeners(),history.go(s)}const o=me({location:"",base:e,go:r,createHref:wf.bind(null,e)},t,n);return Object.defineProperty(o,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(o,"state",{enumerable:!0,get:()=>t.state.value}),o}function Pf(e){return typeof e=="string"||e&&typeof e=="object"}function Nl(e){return typeof e=="string"||typeof e=="symbol"}const _t={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},Hl=Symbol("");var Xs;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(Xs||(Xs={}));function _n(e,t){return me(new Error,{type:e,[Hl]:!0},t)}function bt(e,t){return e instanceof Error&&Hl in e&&(t==null||!!(e.type&t))}const Qs="[^/]+?",Rf={sensitive:!1,strict:!1,start:!0,end:!0},If=/[.+*?^${}()[\]/\\]/g;function Of(e,t){const n=me({},Rf,t),r=[];let o=n.start?"^":"";const s=[];for(const c of e){const u=c.length?[]:[90];n.strict&&!c.length&&(o+="/");for(let f=0;ft.length?t.length===1&&t[0]===80?1:-1:0}function Mf(e,t){let n=0;const r=e.score,o=t.score;for(;n0&&t[t.length-1]<0}const Nf={type:0,value:""},Hf=/[a-zA-Z0-9_]/;function Ff(e){if(!e)return[[]];if(e==="/")return[[Nf]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(m){throw new Error(`ERR (${n})/"${c}": ${m}`)}let n=0,r=n;const o=[];let s;function i(){s&&o.push(s),s=[]}let l=0,a,c="",u="";function f(){c&&(n===0?s.push({type:0,value:c}):n===1||n===2||n===3?(s.length>1&&(a==="*"||a==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),s.push({type:1,value:c,regexp:u,repeatable:a==="*"||a==="+",optional:a==="*"||a==="?"})):t("Invalid state to consume buffer"),c="")}function d(){c+=a}for(;l{i(x)}:Mn}function i(u){if(Nl(u)){const f=r.get(u);f&&(r.delete(u),n.splice(n.indexOf(f),1),f.children.forEach(i),f.alias.forEach(i))}else{const f=n.indexOf(u);f>-1&&(n.splice(f,1),u.record.name&&r.delete(u.record.name),u.children.forEach(i),u.alias.forEach(i))}}function l(){return n}function a(u){let f=0;for(;f=0&&(u.record.path!==n[f].record.path||!Fl(u,n[f]));)f++;n.splice(f,0,u),u.record.name&&!ti(u)&&r.set(u.record.name,u)}function c(u,f){let d,m={},g,y;if("name"in u&&u.name){if(d=r.get(u.name),!d)throw _n(1,{location:u});y=d.record.name,m=me(ei(f.params,d.keys.filter(x=>!x.optional).concat(d.parent?d.parent.keys.filter(x=>x.optional):[]).map(x=>x.name)),u.params&&ei(u.params,d.keys.map(x=>x.name))),g=d.stringify(m)}else if(u.path!=null)g=u.path,d=n.find(x=>x.re.test(g)),d&&(m=d.parse(g),y=d.record.name);else{if(d=f.name?r.get(f.name):n.find(x=>x.re.test(f.path)),!d)throw _n(1,{location:u,currentLocation:f});y=d.record.name,m=me({},f.params,u.params),g=d.stringify(m)}const E=[];let T=d;for(;T;)E.unshift(T.record),T=T.parent;return{name:y,path:g,params:m,matched:E,meta:Kf(E)}}return e.forEach(u=>s(u)),{addRoute:s,resolve:c,removeRoute:i,getRoutes:l,getRecordMatcher:o}}function ei(e,t){const n={};for(const r of t)r in e&&(n[r]=e[r]);return n}function Bf(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:zf(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function zf(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const r in e.components)t[r]=typeof n=="object"?n[r]:n;return t}function ti(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function Kf(e){return e.reduce((t,n)=>me(t,n.meta),{})}function ni(e,t){const n={};for(const r in e)n[r]=r in t?t[r]:e[r];return n}function Fl(e,t){return t.children.some(n=>n===e||Fl(e,n))}function Wf(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?"?e.slice(1):e).split("&");for(let o=0;os&&xo(s)):[r&&xo(r)]).forEach(s=>{s!==void 0&&(t+=(t.length?"&":"")+n,s!=null&&(t+="="+s))})}return t}function Vf(e){const t={};for(const n in e){const r=e[n];r!==void 0&&(t[n]=lt(r)?r.map(o=>o==null?null:""+o):r==null?r:""+r)}return t}const Uf=Symbol(""),oi=Symbol(""),Ur=Symbol(""),Yo=Symbol(""),Ao=Symbol("");function Sn(){let e=[];function t(r){return e.push(r),()=>{const o=e.indexOf(r);o>-1&&e.splice(o,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function Ot(e,t,n,r,o,s=i=>i()){const i=r&&(r.enterCallbacks[o]=r.enterCallbacks[o]||[]);return()=>new Promise((l,a)=>{const c=d=>{d===!1?a(_n(4,{from:n,to:t})):d instanceof Error?a(d):Pf(d)?a(_n(2,{from:t,to:d})):(i&&r.enterCallbacks[o]===i&&typeof d=="function"&&i.push(d),l())},u=s(()=>e.call(r&&r.instances[o],t,n,c));let f=Promise.resolve(u);e.length<3&&(f=f.then(c)),f.catch(d=>a(d))})}function co(e,t,n,r,o=s=>s()){const s=[];for(const i of e)for(const l in i.components){let a=i.components[l];if(!(t!=="beforeRouteEnter"&&!i.instances[l]))if(qf(a)){const u=(a.__vccOpts||a)[t];u&&s.push(Ot(u,n,r,i,l,o))}else{let c=a();s.push(()=>c.then(u=>{if(!u)return Promise.reject(new Error(`Couldn't resolve component "${l}" at "${i.path}"`));const f=Qu(u)?u.default:u;i.components[l]=f;const m=(f.__vccOpts||f)[t];return m&&Ot(m,n,r,i,l,o)()}))}}return s}function qf(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function si(e){const t=ze(Ur),n=ze(Yo),r=I(()=>t.resolve(X(e.to))),o=I(()=>{const{matched:a}=r.value,{length:c}=a,u=a[c-1],f=n.matched;if(!u||!f.length)return-1;const d=f.findIndex(bn.bind(null,u));if(d>-1)return d;const m=ii(a[c-2]);return c>1&&ii(u)===m&&f[f.length-1].path!==m?f.findIndex(bn.bind(null,a[c-2])):d}),s=I(()=>o.value>-1&&Xf(n.params,r.value.params)),i=I(()=>o.value>-1&&o.value===n.matched.length-1&&$l(n.params,r.value.params));function l(a={}){return Yf(a)?t[X(e.replace)?"replace":"push"](X(e.to)).catch(Mn):Promise.resolve()}return{route:r,href:I(()=>r.value.href),isActive:s,isExactActive:i,navigate:l}}const Gf=he({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:si,setup(e,{slots:t}){const n=Xn(si(e)),{options:r}=ze(Ur),o=I(()=>({[li(e.activeClass,r.linkActiveClass,"router-link-active")]:n.isActive,[li(e.exactActiveClass,r.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const s=t.default&&t.default(n);return e.custom?s:oe("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:o.value},s)}}}),Jf=Gf;function Yf(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Xf(e,t){for(const n in t){const r=t[n],o=e[n];if(typeof r=="string"){if(r!==o)return!1}else if(!lt(o)||o.length!==r.length||r.some((s,i)=>s!==o[i]))return!1}return!0}function ii(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const li=(e,t,n)=>e??t??n,Qf=he({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const r=ze(Ao),o=I(()=>e.route||r.value),s=ze(oi,0),i=I(()=>{let c=X(s);const{matched:u}=o.value;let f;for(;(f=u[c])&&!f.components;)c++;return c}),l=I(()=>o.value.matched[i.value]);Nt(oi,I(()=>i.value+1)),Nt(Uf,l),Nt(Ao,o);const a=ce();return De(()=>[a.value,l.value,e.name],([c,u,f],[d,m,g])=>{u&&(u.instances[f]=c,m&&m!==u&&c&&c===d&&(u.leaveGuards.size||(u.leaveGuards=m.leaveGuards),u.updateGuards.size||(u.updateGuards=m.updateGuards))),c&&u&&(!m||!bn(u,m)||!d)&&(u.enterCallbacks[f]||[]).forEach(y=>y(c))},{flush:"post"}),()=>{const c=o.value,u=e.name,f=l.value,d=f&&f.components[u];if(!d)return ai(n.default,{Component:d,route:c});const m=f.props[u],g=m?m===!0?c.params:typeof m=="function"?m(c):m:null,E=oe(d,me({},g,t,{onVnodeUnmounted:T=>{T.component.isUnmounted&&(f.instances[u]=null)},ref:a}));return ai(n.default,{Component:E,route:c})||E}}});function ai(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const Zf=Qf;function ed(e){const t=jf(e.routes,e),n=e.parseQuery||Wf,r=e.stringifyQuery||ri,o=e.history,s=Sn(),i=Sn(),l=Sn(),a=sn(_t);let c=_t;un&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=lo.bind(null,S=>""+S),f=lo.bind(null,df),d=lo.bind(null,Kn);function m(S,K){let j,q;return Nl(S)?(j=t.getRecordMatcher(S),q=K):q=S,t.addRoute(q,j)}function g(S){const K=t.getRecordMatcher(S);K&&t.removeRoute(K)}function y(){return t.getRoutes().map(S=>S.record)}function E(S){return!!t.getRecordMatcher(S)}function T(S,K){if(K=me({},K||a.value),typeof S=="string"){const h=ao(n,S,K.path),_=t.resolve({path:h.path},K),A=o.createHref(h.fullPath);return me(h,_,{params:d(_.params),hash:Kn(h.hash),redirectedFrom:void 0,href:A})}let j;if(S.path!=null)j=me({},S,{path:ao(n,S.path,K.path).path});else{const h=me({},S.params);for(const _ in h)h[_]==null&&delete h[_];j=me({},S,{params:f(h)}),K.params=f(K.params)}const q=t.resolve(j,K),ue=S.hash||"";q.params=u(d(q.params));const ge=mf(r,me({},S,{hash:cf(ue),path:q.path})),p=o.createHref(ge);return me({fullPath:ge,hash:ue,query:r===ri?Vf(S.query):S.query||{}},q,{redirectedFrom:void 0,href:p})}function x(S){return typeof S=="string"?ao(n,S,a.value.path):me({},S)}function v(S,K){if(c!==S)return _n(8,{from:K,to:S})}function k(S){return H(S)}function D(S){return k(me(x(S),{replace:!0}))}function O(S){const K=S.matched[S.matched.length-1];if(K&&K.redirect){const{redirect:j}=K;let q=typeof j=="function"?j(S):j;return typeof q=="string"&&(q=q.includes("?")||q.includes("#")?q=x(q):{path:q},q.params={}),me({query:S.query,hash:S.hash,params:q.path!=null?{}:S.params},q)}}function H(S,K){const j=c=T(S),q=a.value,ue=S.state,ge=S.force,p=S.replace===!0,h=O(j);if(h)return H(me(x(h),{state:typeof h=="object"?me({},ue,h.state):ue,force:ge,replace:p}),K||j);const _=j;_.redirectedFrom=K;let A;return!ge&&gf(r,q,j)&&(A=_n(16,{to:_,from:q}),Ve(q,q,!0,!1)),(A?Promise.resolve(A):L(_,q)).catch(C=>bt(C)?bt(C,2)?C:Fe(C):V(C,_,q)).then(C=>{if(C){if(bt(C,2))return H(me({replace:p},x(C.to),{state:typeof C.to=="object"?me({},ue,C.to.state):ue,force:ge}),K||_)}else C=w(_,q,!0,p,ue);return W(_,q,C),C})}function b(S,K){const j=v(S,K);return j?Promise.reject(j):Promise.resolve()}function G(S){const K=Ct.values().next().value;return K&&typeof K.runWithContext=="function"?K.runWithContext(S):S()}function L(S,K){let j;const[q,ue,ge]=td(S,K);j=co(q.reverse(),"beforeRouteLeave",S,K);for(const h of q)h.leaveGuards.forEach(_=>{j.push(Ot(_,S,K))});const p=b.bind(null,S,K);return j.push(p),Oe(j).then(()=>{j=[];for(const h of s.list())j.push(Ot(h,S,K));return j.push(p),Oe(j)}).then(()=>{j=co(ue,"beforeRouteUpdate",S,K);for(const h of ue)h.updateGuards.forEach(_=>{j.push(Ot(_,S,K))});return j.push(p),Oe(j)}).then(()=>{j=[];for(const h of ge)if(h.beforeEnter)if(lt(h.beforeEnter))for(const _ of h.beforeEnter)j.push(Ot(_,S,K));else j.push(Ot(h.beforeEnter,S,K));return j.push(p),Oe(j)}).then(()=>(S.matched.forEach(h=>h.enterCallbacks={}),j=co(ge,"beforeRouteEnter",S,K,G),j.push(p),Oe(j))).then(()=>{j=[];for(const h of i.list())j.push(Ot(h,S,K));return j.push(p),Oe(j)}).catch(h=>bt(h,8)?h:Promise.reject(h))}function W(S,K,j){l.list().forEach(q=>G(()=>q(S,K,j)))}function w(S,K,j,q,ue){const ge=v(S,K);if(ge)return ge;const p=K===_t,h=un?history.state:{};j&&(q||p?o.replace(S.fullPath,me({scroll:p&&h&&h.scroll},ue)):o.push(S.fullPath,ue)),a.value=S,Ve(S,K,j,p),Fe()}let N;function te(){N||(N=o.listen((S,K,j)=>{if(!ut.listening)return;const q=T(S),ue=O(q);if(ue){H(me(ue,{replace:!0}),q).catch(Mn);return}c=q;const ge=a.value;un&&Cf(Js(ge.fullPath,j.delta),Vr()),L(q,ge).catch(p=>bt(p,12)?p:bt(p,2)?(H(p.to,q).then(h=>{bt(h,20)&&!j.delta&&j.type===Wn.pop&&o.go(-1,!1)}).catch(Mn),Promise.reject()):(j.delta&&o.go(-j.delta,!1),V(p,q,ge))).then(p=>{p=p||w(q,ge,!1),p&&(j.delta&&!bt(p,8)?o.go(-j.delta,!1):j.type===Wn.pop&&bt(p,20)&&o.go(-1,!1)),W(q,ge,p)}).catch(Mn)}))}let le=Sn(),R=Sn(),J;function V(S,K,j){Fe(S);const q=R.list();return q.length?q.forEach(ue=>ue(S,K,j)):console.error(S),Promise.reject(S)}function Ie(){return J&&a.value!==_t?Promise.resolve():new Promise((S,K)=>{le.add([S,K])})}function Fe(S){return J||(J=!S,te(),le.list().forEach(([K,j])=>S?j(S):K()),le.reset()),S}function Ve(S,K,j,q){const{scrollBehavior:ue}=e;if(!un||!ue)return Promise.resolve();const ge=!j&&Sf(Js(S.fullPath,0))||(q||!j)&&history.state&&history.state.scroll||null;return yn().then(()=>ue(S,K,ge)).then(p=>p&&Ef(p)).catch(p=>V(p,S,K))}const je=S=>o.go(S);let Et;const Ct=new Set,ut={currentRoute:a,listening:!0,addRoute:m,removeRoute:g,hasRoute:E,getRoutes:y,resolve:T,options:e,push:k,replace:D,go:je,back:()=>je(-1),forward:()=>je(1),beforeEach:s.add,beforeResolve:i.add,afterEach:l.add,onError:R.add,isReady:Ie,install(S){const K=this;S.component("RouterLink",Jf),S.component("RouterView",Zf),S.config.globalProperties.$router=K,Object.defineProperty(S.config.globalProperties,"$route",{enumerable:!0,get:()=>X(a)}),un&&!Et&&a.value===_t&&(Et=!0,k(o.location).catch(ue=>{}));const j={};for(const ue in _t)Object.defineProperty(j,ue,{get:()=>a.value[ue],enumerable:!0});S.provide(Ur,K),S.provide(Yo,zi(j)),S.provide(Ao,a);const q=S.unmount;Ct.add(S),S.unmount=function(){Ct.delete(S),Ct.size<1&&(c=_t,N&&N(),N=null,a.value=_t,Et=!1,J=!1),q()}}};function Oe(S){return S.reduce((K,j)=>K.then(()=>G(j)),Promise.resolve())}return ut}function td(e,t){const n=[],r=[],o=[],s=Math.max(t.matched.length,e.matched.length);for(let i=0;ibn(c,l))?r.push(l):n.push(l));const a=e.matched[i];a&&(t.matched.find(c=>bn(c,a))||o.push(a))}return[n,r,o]}function ln(){return ze(Ur)}function Kt(){return ze(Yo)}var Xo=Symbol(""),vt=()=>{const e=ze(Xo);if(!e)throw new Error("useClientData() is called without provider.");return e},nd=()=>vt().pageComponent,gn=()=>vt().pageData,ht=()=>vt().pageFrontmatter,rd=()=>vt().pageHead,od=()=>vt().pageLang,sd=()=>vt().pageLayout,Wt=()=>vt().routeLocale,id=()=>vt().routes,Dl=()=>vt().siteData,Qo=()=>vt().siteLocaleData,ld=Symbol(""),To=sn(Yu),Vn=sn(Xu),jl=e=>{const t=qu(e);if(Vn.value[t])return t;const n=encodeURI(t);return Vn.value[n]?n:To.value[t]||To.value[n]||t},Un=e=>{const t=jl(e),n=Vn.value[t]??{...Vn.value["/404.html"],notFound:!0};return{path:t,notFound:!1,...n}},Zo=he({name:"ClientOnly",setup(e,t){const n=ce(!1);return He(()=>{n.value=!0}),()=>{var r,o;return n.value?(o=(r=t.slots).default)==null?void 0:o.call(r):null}}}),ad=he({name:"Content",props:{path:{type:String,required:!1,default:""}},setup(e){const t=nd(),n=I(()=>{if(!e.path)return t.value;const r=Un(e.path);return yc(()=>r.loader().then(({comp:o})=>o))});return()=>oe(n.value)}}),ct=(e={})=>e,rr=e=>nr(e)?e:`/learning-kotlin/${Ll(e)}`,cd=e=>{if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget){const t=e.currentTarget.getAttribute("target");if(t!=null&&t.match(/\b_blank\b/i))return}return e.preventDefault(),!0}},or=({active:e=!1,activeClass:t="route-link-active",to:n,...r},{slots:o})=>{var a;const s=ln(),i=jl(n),l=i.startsWith("#")||i.startsWith("?")?i:rr(i);return oe("a",{...r,class:["route-link",{[t]:e}],href:l,onClick:(c={})=>{cd(c)?s.push(n).catch():Promise.resolve()}},(a=o.default)==null?void 0:a.call(o))};or.displayName="RouteLink";or.props={active:Boolean,activeClass:String,to:String};var ud="Layout",fd="en-US",Gt=Xn({resolveLayouts:e=>e.reduce((t,n)=>({...t,...n.layouts}),{}),resolvePageHead:(e,t,n)=>{const r=it(t.description)?t.description:n.description,o=[...Array.isArray(t.head)?t.head:[],...n.head,["title",{},e],["meta",{name:"description",content:r}]];return Uu(o)},resolvePageHeadTitle:(e,t)=>[e.title,t.title].filter(n=>!!n).join(" | "),resolvePageLang:(e,t)=>e.lang||t.lang||fd,resolvePageLayout:(e,t)=>{const n=it(e.frontmatter.layout)?e.frontmatter.layout:ud;if(!t[n])throw new Error(`[vuepress] Cannot resolve layout: ${n}`);return t[n]},resolveRouteLocale:(e,t)=>Al(e,t),resolveSiteLocaleData:(e,t)=>{var n;return{...e,...e.locales[t],head:[...((n=e.locales[t])==null?void 0:n.head)??[],...e.head??[]]}}});function qr(e){return Pi()?(Sa(e),!0):!1}function gt(e){return typeof e=="function"?e():X(e)}const es=typeof window<"u"&&typeof document<"u";typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope;const dd=Object.prototype.toString,pd=e=>dd.call(e)==="[object Object]",Po=()=>{};function Bl(e,t){function n(...r){return new Promise((o,s)=>{Promise.resolve(e(()=>t.apply(this,r),{fn:t,thisArg:this,args:r})).then(o).catch(s)})}return n}const zl=e=>e();function hd(e,t={}){let n,r,o=Po;const s=l=>{clearTimeout(l),o(),o=Po};return l=>{const a=gt(e),c=gt(t.maxWait);return n&&s(n),a<=0||c!==void 0&&c<=0?(r&&(s(r),r=null),Promise.resolve(l())):new Promise((u,f)=>{o=t.rejectOnCancel?f:u,c&&!r&&(r=setTimeout(()=>{n&&s(n),r=null,u(l())},c)),n=setTimeout(()=>{r&&s(r),r=null,u(l())},a)})}}function md(e=zl){const t=ce(!0);function n(){t.value=!1}function r(){t.value=!0}const o=(...s)=>{t.value&&e(...s)};return{isActive:Nr(t),pause:n,resume:r,eventFilter:o}}function gd(e){let t;function n(){return t||(t=e()),t}return n.reset=async()=>{const r=t;t=void 0,r&&await r},n}function vd(e){return e||Kr()}function bd(e,t=200,n={}){return Bl(hd(t,n),e)}function _d(e,t,n={}){const{eventFilter:r=zl,...o}=n;return De(e,Bl(r,t),o)}function yd(e,t,n={}){const{eventFilter:r,...o}=n,{eventFilter:s,pause:i,resume:l,isActive:a}=md(r);return{stop:_d(e,t,{...o,eventFilter:s}),pause:i,resume:l,isActive:a}}function ts(e,t=!0,n){vd()?He(e,n):t?e():yn(e)}function wd(e,t,n={}){const{immediate:r=!0}=n,o=ce(!1);let s=null;function i(){s&&(clearTimeout(s),s=null)}function l(){o.value=!1,i()}function a(...c){i(),o.value=!0,s=setTimeout(()=>{o.value=!1,s=null,e(...c)},gt(t))}return r&&(o.value=!0,es&&a()),qr(l),{isPending:Nr(o),start:a,stop:l}}function kd(e=!1,t={}){const{truthyValue:n=!0,falsyValue:r=!1}=t,o=Ke(e),s=ce(e);function i(l){if(arguments.length)return s.value=l,s.value;{const a=gt(n);return s.value=s.value===a?gt(r):a,s.value}}return o?i:[s,i]}function Zt(e){var t;const n=gt(e);return(t=n==null?void 0:n.$el)!=null?t:n}const jt=es?window:void 0,Kl=es?window.navigator:void 0;function at(...e){let t,n,r,o;if(typeof e[0]=="string"||Array.isArray(e[0])?([n,r,o]=e,t=jt):[t,n,r,o]=e,!t)return Po;Array.isArray(n)||(n=[n]),Array.isArray(r)||(r=[r]);const s=[],i=()=>{s.forEach(u=>u()),s.length=0},l=(u,f,d,m)=>(u.addEventListener(f,d,m),()=>u.removeEventListener(f,d,m)),a=De(()=>[Zt(t),gt(o)],([u,f])=>{if(i(),!u)return;const d=pd(f)?{...f}:f;s.push(...n.flatMap(m=>r.map(g=>l(u,m,g,d))))},{immediate:!0,flush:"post"}),c=()=>{a(),i()};return qr(c),c}function Ed(){const e=ce(!1),t=Kr();return t&&He(()=>{e.value=!0},t),e}function Gr(e){const t=Ed();return I(()=>(t.value,!!e()))}function Wl(e,t={}){const{window:n=jt}=t,r=Gr(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function");let o;const s=ce(!1),i=c=>{s.value=c.matches},l=()=>{o&&("removeEventListener"in o?o.removeEventListener("change",i):o.removeListener(i))},a=mc(()=>{r.value&&(l(),o=n.matchMedia(gt(e)),"addEventListener"in o?o.addEventListener("change",i):o.addListener(i),s.value=o.matches)});return qr(()=>{a(),l(),o=void 0}),s}function ci(e,t={}){const{controls:n=!1,navigator:r=Kl}=t,o=Gr(()=>r&&"permissions"in r);let s;const i=typeof e=="string"?{name:e}:e,l=ce(),a=()=>{s&&(l.value=s.state)},c=gd(async()=>{if(o.value){if(!s)try{s=await r.permissions.query(i),at(s,"change",a),a()}catch{l.value="prompt"}return s}});return c(),n?{state:l,isSupported:o,query:c}:l}function Cd(e={}){const{navigator:t=Kl,read:n=!1,source:r,copiedDuring:o=1500,legacy:s=!1}=e,i=Gr(()=>t&&"clipboard"in t),l=ci("clipboard-read"),a=ci("clipboard-write"),c=I(()=>i.value||s),u=ce(""),f=ce(!1),d=wd(()=>f.value=!1,o);function m(){i.value&&T(l.value)?t.clipboard.readText().then(x=>{u.value=x}):u.value=E()}c.value&&n&&at(["copy","cut"],m);async function g(x=gt(r)){c.value&&x!=null&&(i.value&&T(a.value)?await t.clipboard.writeText(x):y(x),u.value=x,f.value=!0,d.start())}function y(x){const v=document.createElement("textarea");v.value=x??"",v.style.position="absolute",v.style.opacity="0",document.body.appendChild(v),v.select(),document.execCommand("copy"),v.remove()}function E(){var x,v,k;return(k=(v=(x=document==null?void 0:document.getSelection)==null?void 0:x.call(document))==null?void 0:v.toString())!=null?k:""}function T(x){return x==="granted"||x==="prompt"}return{isSupported:c,text:u,copied:f,copy:g}}const hr=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},mr="__vueuse_ssr_handlers__",Sd=xd();function xd(){return mr in hr||(hr[mr]=hr[mr]||{}),hr[mr]}function Ld(e,t){return Sd[e]||t}function Ad(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}const Td={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},ui="vueuse-storage";function Vl(e,t,n,r={}){var o;const{flush:s="pre",deep:i=!0,listenToStorageChanges:l=!0,writeDefaults:a=!0,mergeDefaults:c=!1,shallow:u,window:f=jt,eventFilter:d,onError:m=L=>{console.error(L)},initOnMounted:g}=r,y=(u?sn:ce)(typeof t=="function"?t():t);if(!n)try{n=Ld("getDefaultStorage",()=>{var L;return(L=jt)==null?void 0:L.localStorage})()}catch(L){m(L)}if(!n)return y;const E=gt(t),T=Ad(E),x=(o=r.serializer)!=null?o:Td[T],{pause:v,resume:k}=yd(y,()=>O(y.value),{flush:s,deep:i,eventFilter:d});f&&l&&ts(()=>{at(f,"storage",b),at(f,ui,G),g&&b()}),g||b();function D(L,W){f&&f.dispatchEvent(new CustomEvent(ui,{detail:{key:e,oldValue:L,newValue:W,storageArea:n}}))}function O(L){try{const W=n.getItem(e);if(L==null)D(W,null),n.removeItem(e);else{const w=x.write(L);W!==w&&(n.setItem(e,w),D(W,w))}}catch(W){m(W)}}function H(L){const W=L?L.newValue:n.getItem(e);if(W==null)return a&&E!=null&&n.setItem(e,x.write(E)),E;if(!L&&c){const w=x.read(W);return typeof c=="function"?c(w,E):T==="object"&&!Array.isArray(w)?{...E,...w}:w}else return typeof W!="string"?W:x.read(W)}function b(L){if(!(L&&L.storageArea!==n)){if(L&&L.key==null){y.value=E;return}if(!(L&&L.key!==e)){v();try{(L==null?void 0:L.newValue)!==x.write(y.value)&&(y.value=H(L))}catch(W){m(W)}finally{L?yn(k):k()}}}}function G(L){b(L.detail)}return y}function Pd(e){return Wl("(prefers-color-scheme: dark)",e)}function Rd(e,t,n={}){const{window:r=jt,...o}=n;let s;const i=Gr(()=>r&&"ResizeObserver"in r),l=()=>{s&&(s.disconnect(),s=void 0)},a=I(()=>Array.isArray(e)?e.map(f=>Zt(f)):[Zt(e)]),c=De(a,f=>{if(l(),i.value&&r){s=new ResizeObserver(t);for(const d of f)d&&s.observe(d,o)}},{immediate:!0,flush:"post"}),u=()=>{l(),c()};return qr(u),{isSupported:i,stop:u}}function Id(e,t={width:0,height:0},n={}){const{window:r=jt,box:o="content-box"}=n,s=I(()=>{var f,d;return(d=(f=Zt(e))==null?void 0:f.namespaceURI)==null?void 0:d.includes("svg")}),i=ce(t.width),l=ce(t.height),{stop:a}=Rd(e,([f])=>{const d=o==="border-box"?f.borderBoxSize:o==="content-box"?f.contentBoxSize:f.devicePixelContentBoxSize;if(r&&s.value){const m=Zt(e);if(m){const g=r.getComputedStyle(m);i.value=Number.parseFloat(g.width),l.value=Number.parseFloat(g.height)}}else if(d){const m=Array.isArray(d)?d:[d];i.value=m.reduce((g,{inlineSize:y})=>g+y,0),l.value=m.reduce((g,{blockSize:y})=>g+y,0)}else i.value=f.contentRect.width,l.value=f.contentRect.height},n);ts(()=>{const f=Zt(e);f&&(i.value="offsetWidth"in f?f.offsetWidth:t.width,l.value="offsetHeight"in f?f.offsetHeight:t.height)});const c=De(()=>Zt(e),f=>{i.value=f?t.width:0,l.value=f?t.height:0});function u(){a(),c()}return{width:i,height:l,stop:u}}function Od(e={}){const{window:t=jt,behavior:n="auto"}=e;if(!t)return{x:ce(0),y:ce(0)};const r=ce(t.scrollX),o=ce(t.scrollY),s=I({get(){return r.value},set(l){scrollTo({left:l,behavior:n})}}),i=I({get(){return o.value},set(l){scrollTo({top:l,behavior:n})}});return at(t,"scroll",()=>{r.value=t.scrollX,o.value=t.scrollY},{capture:!1,passive:!0}),{x:s,y:i}}function $d(e={}){const{window:t=jt,initialWidth:n=Number.POSITIVE_INFINITY,initialHeight:r=Number.POSITIVE_INFINITY,listenOrientation:o=!0,includeScrollbar:s=!0}=e,i=ce(n),l=ce(r),a=()=>{t&&(s?(i.value=t.innerWidth,l.value=t.innerHeight):(i.value=t.document.documentElement.clientWidth,l.value=t.document.documentElement.clientHeight))};if(a(),ts(a),at("resize",a,{passive:!0}),o){const c=Wl("(orientation: portrait)");De(c,()=>a())}return{width:i,height:l}}const fi=async(e,t)=>{const{path:n,query:r}=e.currentRoute.value,{scrollBehavior:o}=e.options;e.options.scrollBehavior=void 0,await e.replace({path:n,query:r,hash:t}),e.options.scrollBehavior=o},Md=({headerLinkSelector:e,headerAnchorSelector:t,delay:n,offset:r=5})=>{const o=ln();at("scroll",bd(()=>{var g,y;const i=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(i-0)f.some(T=>T.hash===E.hash));for(let E=0;E=(((g=T.parentElement)==null?void 0:g.offsetTop)??0)-r,k=!x||i<(((y=x.parentElement)==null?void 0:y.offsetTop)??0)-r;if(!(v&&k))continue;const O=decodeURIComponent(o.currentRoute.value.hash),H=decodeURIComponent(T.hash);if(O===H)return;if(u){for(let b=E+1;b{const t=Wt();return I(()=>e[t.value]??{})};var zd={"/en/":{backToTop:"Back to top"},"/fr/":{backToTop:"Retour en haut"},"/":{backToTop:"Back to top"}};const Kd=he({name:"BackToTop",setup(){const e=ht(),t=Bd(zd),n=sn(),{height:r}=Id(n),{height:o}=$d(),{y:s}=Od(),i=I(()=>e.value.backToTop!==!1&&s.value>100),l=I(()=>s.value/(r.value-o.value)*100);return He(()=>{n.value=document.body}),()=>oe(wn,{name:"back-to-top"},()=>i.value?oe("button",{type:"button",class:"vp-back-to-top-button","aria-label":t.value.backToTop,onClick:()=>{window.scrollTo({top:0,behavior:"smooth"})}},[oe("span",{class:"vp-scroll-progress",role:"progressbar","aria-labelledby":"loadinglabel","aria-valuenow":l.value},oe("svg",oe("circle",{cx:"26",cy:"26",r:"24",fill:"none",stroke:"currentColor","stroke-width":"4","stroke-dasharray":`${Math.PI*l.value*.48} ${Math.PI*(100-l.value)*.48}`}))),oe("div",{class:"back-to-top-icon"})]):null)}}),Wd=ct({rootComponents:[Kd]}),Vd=e=>{const t=Wt();return I(()=>e[t.value]??{})},Ud=e=>new Promise(t=>setTimeout(t,e)),qd=/\b(?:Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini)/i,Gd=()=>typeof window<"u"&&window.navigator&&"userAgent"in window.navigator&&qd.test(navigator.userAgent),Jd=({delay:e=500,duration:t=2e3,locales:n,selector:r,showInMobile:o})=>{const{copy:s,copied:i}=Cd({legacy:!0,copiedDuring:t}),l=Vd(n),a=gn(),c=d=>{if(!d.hasAttribute("copy-code-registered")){const m=document.createElement("button");m.type="button",m.classList.add("vp-copy-code-button"),m.innerHTML='
',m.setAttribute("aria-label",l.value.copy),m.setAttribute("data-copied",l.value.copied),d.parentElement&&d.parentElement.insertBefore(m,d),d.setAttribute("copy-code-registered","")}},u=()=>{yn().then(()=>Ud(e)).then(()=>{r.forEach(d=>{document.querySelectorAll(d).forEach(c)})})},f=(d,m,g)=>{let{innerText:y=""}=m;/language-(shellscript|shell|bash|sh|zsh)/.test(d.classList.toString())&&(y=y.replace(/^ *(\$|>) /gm,"")),s(y).then(()=>{g.classList.add("copied"),De(i,()=>{g.classList.remove("copied"),g.blur()},{once:!0})})};He(()=>{const d=!Gd()||o;d&&u(),at("click",m=>{const g=m.target;if(g.matches('div[class*="language-"] > button.copy')){const y=g.parentElement,E=g.nextElementSibling;E&&f(y,E,g)}else if(g.matches('div[class*="language-"] div.vp-copy-icon')){const y=g.parentElement,E=y.parentElement,T=y.nextElementSibling;T&&f(E,T,y)}}),De(()=>a.value.path,()=>{d&&u()})})};var Yd={"/en/":{copy:"Copy code",copied:"Copied"},"/fr/":{copy:"Copier le code",copied:"Copié"},"/":{copy:"Copy code",copied:"Copied"}},Xd=['.theme-default-content div[class*="language-"] pre'];const Qd=500,Zd=2e3,ep=Yd,tp=Xd,np=!1,rp=ct({setup:()=>{Jd({selector:tp,locales:ep,duration:Zd,delay:Qd,showInMobile:np})}}),op=oe("svg",{class:"external-link-icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},[oe("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}),oe("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"})]),sp=he({name:"ExternalLinkIcon",props:{locales:{type:Object,default:()=>({})}},setup(e){const t=Wt(),n=I(()=>e.locales[t.value]??{openInNewWindow:"open in new window"});return()=>oe("span",[op,oe("span",{class:"external-link-icon-sr-only"},n.value.openInNewWindow)])}});var ip={"/en/":{openInNewWindow:"open in new window"},"/fr/":{openInNewWindow:"open in new window"},"/":{openInNewWindow:"open in new window"}};const lp=ip,ap=ct({enhance({app:e}){e.component("ExternalLinkIcon",oe(sp,{locales:lp}))}});/** + * NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT + */const fe={settings:{minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:'
'},status:null,set:e=>{const t=fe.isStarted();e=uo(e,fe.settings.minimum,1),fe.status=e===1?null:e;const n=fe.render(!t),r=n.querySelector(fe.settings.barSelector),o=fe.settings.speed,s=fe.settings.easing;return n.offsetWidth,cp(i=>{gr(r,{transform:"translate3d("+di(e)+"%,0,0)",transition:"all "+o+"ms "+s}),e===1?(gr(n,{transition:"none",opacity:"1"}),n.offsetWidth,setTimeout(function(){gr(n,{transition:"all "+o+"ms linear",opacity:"0"}),setTimeout(function(){fe.remove(),i()},o)},o)):setTimeout(()=>i(),o)}),fe},isStarted:()=>typeof fe.status=="number",start:()=>{fe.status||fe.set(0);const e=()=>{setTimeout(()=>{fe.status&&(fe.trickle(),e())},fe.settings.trickleSpeed)};return fe.settings.trickle&&e(),fe},done:e=>!e&&!fe.status?fe:fe.inc(.3+.5*Math.random()).set(1),inc:e=>{let t=fe.status;return t?(typeof e!="number"&&(e=(1-t)*uo(Math.random()*t,.1,.95)),t=uo(t+e,0,.994),fe.set(t)):fe.start()},trickle:()=>fe.inc(Math.random()*fe.settings.trickleRate),render:e=>{if(fe.isRendered())return document.getElementById("nprogress");pi(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=fe.settings.template;const n=t.querySelector(fe.settings.barSelector),r=e?"-100":di(fe.status||0),o=document.querySelector(fe.settings.parent);return gr(n,{transition:"all 0 linear",transform:"translate3d("+r+"%,0,0)"}),o!==document.body&&pi(o,"nprogress-custom-parent"),o==null||o.appendChild(t),t},remove:()=>{hi(document.documentElement,"nprogress-busy"),hi(document.querySelector(fe.settings.parent),"nprogress-custom-parent");const e=document.getElementById("nprogress");e&&up(e)},isRendered:()=>!!document.getElementById("nprogress")},uo=(e,t,n)=>en?n:e,di=e=>(-1+e)*100,cp=function(){const e=[];function t(){const n=e.shift();n&&n(t)}return function(n){e.push(n),e.length===1&&t()}}(),gr=function(){const e=["Webkit","O","Moz","ms"],t={};function n(i){return i.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(l,a){return a.toUpperCase()})}function r(i){const l=document.body.style;if(i in l)return i;let a=e.length;const c=i.charAt(0).toUpperCase()+i.slice(1);let u;for(;a--;)if(u=e[a]+c,u in l)return u;return i}function o(i){return i=n(i),t[i]??(t[i]=r(i))}function s(i,l,a){l=o(l),i.style[l]=a}return function(i,l){for(const a in l){const c=l[a];c!==void 0&&Object.prototype.hasOwnProperty.call(l,a)&&s(i,a,c)}}}(),Ul=(e,t)=>(typeof e=="string"?e:ns(e)).indexOf(" "+t+" ")>=0,pi=(e,t)=>{const n=ns(e),r=n+t;Ul(n,t)||(e.className=r.substring(1))},hi=(e,t)=>{const n=ns(e);if(!Ul(e,t))return;const r=n.replace(" "+t+" "," ");e.className=r.substring(1,r.length-1)},ns=e=>(" "+(e.className||"")+" ").replace(/\s+/gi," "),up=e=>{e&&e.parentNode&&e.parentNode.removeChild(e)},fp=()=>{He(()=>{const e=ln(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(n=>{t.has(n.path)||fe.start()}),e.afterEach(n=>{t.add(n.path),fe.done()})})},dp=ct({setup(){fp()}}),pp=JSON.parse(`{"logo":"logo_worldline.png","repo":"worldline/learning-kotlin","locales":{"/en/":{"selectLanguageName":"English","sidebar":["/en/presentation/","/en/kotlin-features/","/en/backend-development/","/en/front-development/","/en/other-technologies/","/en/kotlin-features-advanced/","/en/workshops/"]},"/fr/":{"selectLanguageName":"Français","sidebar":["/fr/presentation/","/fr/kotlin-features/","/fr/backend-development/","/fr/front-development/","/fr/other-technologies/","/fr/kotlin-features-advanced/","/fr/workshops/"]},"/":{"selectLanguageName":"English"}},"colorMode":"auto","colorModeSwitch":true,"navbar":[],"selectLanguageText":"Languages","selectLanguageAriaLabel":"Select language","sidebar":"auto","sidebarDepth":2,"editLink":true,"editLinkText":"Edit this page","lastUpdated":true,"lastUpdatedText":"Last Updated","contributors":true,"contributorsText":"Contributors","notFound":["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."],"backToHome":"Take me home","openInNewWindow":"open in new window","toggleColorMode":"toggle color mode","toggleSidebar":"toggle sidebar"}`),hp=ce(pp),ql=()=>hp,Gl=Symbol(""),mp=()=>{const e=ze(Gl);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},gp=(e,t)=>{const{locales:n,...r}=e;return{...r,...n==null?void 0:n[t]}},vp=ct({enhance({app:e}){const t=ql(),n=e._context.provides[Xo],r=I(()=>gp(t.value,n.routeLocale.value));e.provide(Gl,r),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return r.value}}})}}),bp=()=>{const e=id();return I(()=>Object.keys(e.value))},fo=(e,t)=>{var r;const n=(r=(t==null?void 0:t._instance)||Kr())==null?void 0:r.appContext.components;return n?e in n||nt(e)in n||Jn(nt(e))in n:!1},_p=(e,t)=>it(e)&&e.startsWith(t),Jl=e=>_p(e,"/"),yp="http://.",mi=(e,t)=>{if(Jl(e)||typeof t!="string")return Un(e);const n=t.slice(0,t.lastIndexOf("/"));return Un(new URL(`${n}/${encodeURI(e)}`,yp).pathname)},wp=he({__name:"Badge",props:{type:{type:String,required:!1,default:"tip"},text:{type:String,required:!1,default:""},vertical:{type:String,required:!1,default:void 0}},setup(e){return(t,n)=>(z(),Z("span",{class:Ge(["badge",e.type]),style:Yn({verticalAlign:e.vertical})},[be(t.$slots,"default",{},()=>[mt(Ee(e.text),1)])],6))}}),Ce=(e,t)=>{const n=e.__vccOpts||e;for(const[r,o]of t)n[r]=o;return n},kp=Ce(wp,[["__file","Badge.vue"]]),Ep=he({name:"CodeGroup",slots:Object,setup(e,{slots:t}){const n=ce([]),r=ce(-1),o=Vl("vuepress-code-group",{}),s=I(()=>n.value.map(c=>c.innerText).join(","));He(()=>{De(()=>o.value[s.value],(c=-1)=>{r.value!==c&&(r.value=c)},{immediate:!0}),De(r,c=>{o.value[s.value]!==c&&(o.value[s.value]=c)})});const i=(c=r.value)=>{c{c>0?r.value=c-1:r.value=n.value.length-1,n.value[r.value].focus()},a=(c,u)=>{c.key===" "||c.key==="Enter"?(c.preventDefault(),r.value=u):c.key==="ArrowRight"?(c.preventDefault(),i(u)):c.key==="ArrowLeft"&&(c.preventDefault(),l(u))};return()=>{var u;const c=(((u=t.default)==null?void 0:u.call(t))||[]).filter(f=>f.type.name==="CodeGroupItem").map(f=>(f.props===null&&(f.props={}),f));return c.length===0?null:(r.value<0||r.value>c.length-1?(r.value=c.findIndex(f=>f.props.active===""||f.props.active===!0),r.value===-1&&(r.value=0)):c.forEach((f,d)=>{f.props.active=d===r.value}),oe("div",{class:"code-group"},[oe("div",{class:"code-group__nav",role:"tablist"},c.map((f,d)=>{const m=d===r.value;return oe("button",{ref:g=>{g&&(n.value[d]=g)},class:{"code-group__nav-tab":!0,"code-group__nav-tab-active":m},role:"tab",ariaSelected:m,onClick:()=>r.value=d,onKeydown:g=>a(g,d)},f.props.title)})),c]))}}}),Cp=he({name:"CodeGroupItem",__name:"CodeGroupItem",props:{title:{type:String,required:!0},active:{type:Boolean,required:!1,default:!1}},setup(e){return(t,n)=>(z(),Z("div",{class:Ge(["code-group-item",{"code-group-item__active":e.active}]),role:"tabpanel"},[be(t.$slots,"default")],2))}}),Sp=Ce(Cp,[["__file","CodeGroupItem.vue"]]),xp=()=>ql(),Ne=()=>mp(),Yl=Symbol(""),rs=()=>{const e=ze(Yl);if(!e)throw new Error("useDarkMode() is called without provider.");return e},Lp=()=>{const e=Ne(),t=Pd(),n=Vl("vuepress-color-scheme",e.value.colorMode),r=I({get(){return e.value.colorModeSwitch?n.value==="auto"?t.value:n.value==="dark":e.value.colorMode==="dark"},set(o){o===t.value?n.value="auto":n.value=o?"dark":"light"}});Nt(Yl,r),Ap(r)},Ap=e=>{const t=(n=e.value)=>{const r=window==null?void 0:window.document.querySelector("html");r==null||r.classList.toggle("dark",n)};He(()=>{De(e,t,{immediate:!0})}),zr(()=>t())},Tp="http://.",Pp=()=>{const e=ln(),t=Kt();return n=>{if(n)if(Jl(n))t.path!==n&&e.push(n);else if(Sl(n))window&&window.open(n);else{const r=t.path.slice(0,t.path.lastIndexOf("/"));e.push(new URL(`${r}/${encodeURI(n)}`,Tp).pathname)}}};let po=null,xn=null;const Rp={wait:()=>po,pending:()=>{po=new Promise(e=>xn=e)},resolve:()=>{xn==null||xn(),po=null,xn=null}},Xl=()=>Rp,Ql=e=>{const{notFound:t,meta:n,path:r}=Un(e);return t?{text:r,link:r}:{text:n.title||r,link:r}},gi=e=>decodeURI(e).replace(/#.*$/,"").replace(/(index)?\.(md|html)$/,""),Ip=(e,t)=>{if(t.hash===e)return!0;const n=gi(t.path),r=gi(e);return n===r},Zl=(e,t)=>e.link&&Ip(e.link,t)?!0:e.children?e.children.some(n=>Zl(n,t)):!1,ea=e=>!nr(e)||/github\.com/.test(e)?"GitHub":/bitbucket\.org/.test(e)?"Bitbucket":/gitlab\.com/.test(e)?"GitLab":/gitee\.com/.test(e)?"Gitee":null,Op={GitHub:":repo/edit/:branch/:path",GitLab:":repo/-/edit/:branch/:path",Gitee:":repo/edit/:branch/:path",Bitbucket:":repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default"},$p=({docsRepo:e,editLinkPattern:t})=>{if(t)return t;const n=ea(e);return n!==null?Op[n]:null},Mp=({docsRepo:e,docsBranch:t,docsDir:n,filePathRelative:r,editLinkPattern:o})=>{if(!r)return null;const s=$p({docsRepo:e,editLinkPattern:o});return s?s.replace(/:repo/,nr(e)?e:`https://github.com/${e}`).replace(/:branch/,t).replace(/:path/,Ll(`${xl(n)}/${r}`)):null},ta=Symbol("sidebarItems"),os=()=>{const e=ze(ta);if(!e)throw new Error("useSidebarItems() is called without provider.");return e},Np=()=>{const e=Ne(),t=ht(),n=gn(),r=Kt(),o=I(()=>Hp(t.value,e.value,n.value,r.path));Nt(ta,o)},Hp=(e,t,n,r)=>{const o=e.sidebar??t.sidebar??"auto",s=e.sidebarDepth??t.sidebarDepth??2;return e.home||o===!1?[]:o==="auto"?na(n,s):Array.isArray(o)?ra(n,r,o,s):Go(o)?Dp(n,r,o,s):[]},Fp=(e,t)=>({text:e.title,link:e.link,children:ss(e.children,t)}),ss=(e,t)=>t>0?e.map(n=>Fp(n,t-1)):[],na=(e,t)=>[{text:e.title,children:ss(e.headers,t)}],ra=(e,t,n,r)=>{const o=s=>{var l;let i;if(it(s)?i=Ql(s):i=s,i.children)return{...i,children:i.children.map(a=>o(a))};if(i.link===t){const a=((l=e.headers[0])==null?void 0:l.level)===1?e.headers[0].children:e.headers;return{...i,children:ss(a,r)}}return i};return n.map(s=>o(s))},Dp=(e,t,n,r)=>{const o=Al(n,t),s=n[o]??[];return s==="heading"?na(e,r):ra(e,t,s,r)},jp="719px",Bp={mobile:jp};var qn;(function(e){e.MOBILE="mobile"})(qn||(qn={}));var wi;const zp={[qn.MOBILE]:Number.parseInt((wi=Bp.mobile)==null?void 0:wi.replace("px",""),10)},oa=(e,t)=>{const n=zp[e];Number.isInteger(n)&&(at("orientationchange",()=>t(n),!1),at("resize",()=>t(n),!1),He(()=>{t(n)}))},Kp={},Wp={class:"theme-default-content"};function Vp(e,t){const n=nn("Content");return z(),Z("div",Wp,[ie(n)])}const Up=Ce(Kp,[["render",Vp],["__file","HomeContent.vue"]]),qp={key:0,class:"features"},Gp=he({__name:"HomeFeatures",setup(e){const t=ht(),n=I(()=>Array.isArray(t.value.features)?t.value.features:[]);return(r,o)=>n.value.length?(z(),Z("div",qp,[(z(!0),Z(_e,null,Ft(n.value,s=>(z(),Z("div",{key:s.title,class:"feature"},[ne("h2",null,Ee(s.title),1),ne("p",null,Ee(s.details),1)]))),128))])):Le("",!0)}}),Jp=Ce(Gp,[["__file","HomeFeatures.vue"]]),Yp=["innerHTML"],Xp=["textContent"],Qp=he({__name:"HomeFooter",setup(e){const t=ht(),n=I(()=>t.value.footer),r=I(()=>t.value.footerHtml);return(o,s)=>n.value?(z(),Z(_e,{key:0},[r.value?(z(),Z("div",{key:0,class:"footer",innerHTML:n.value},null,8,Yp)):(z(),Z("div",{key:1,class:"footer",textContent:Ee(n.value)},null,8,Xp))],64)):Le("",!0)}}),Zp=Ce(Qp,[["__file","HomeFooter.vue"]]),eh=["href","rel","target","aria-label"],th=he({inheritAttrs:!1,__name:"AutoLink",props:{item:{type:Object,required:!0}},setup(e){const t=e,n=Kt(),r=Dl(),{item:o}=Hr(t),s=I(()=>nr(o.value.link)),i=I(()=>!s.value&&Sl(o.value.link)),l=I(()=>{if(!i.value){if(o.value.target)return o.value.target;if(s.value)return"_blank"}}),a=I(()=>l.value==="_blank"),c=I(()=>!s.value&&!i.value&&!a.value),u=I(()=>{if(!i.value){if(o.value.rel)return o.value.rel;if(a.value)return"noopener noreferrer"}}),f=I(()=>o.value.ariaLabel||o.value.text),d=I(()=>{const g=Object.keys(r.value.locales);return g.length?!g.some(y=>y===o.value.link):o.value.link!=="/"}),m=I(()=>c.value?o.value.activeMatch?new RegExp(o.value.activeMatch).test(n.path):d.value?n.path.startsWith(o.value.link):!1:!1);return(g,y)=>{const E=nn("RouteLink"),T=nn("AutoLinkExternalIcon");return c.value?(z(),Se(E,Co({key:0,active:m.value,to:X(o).link,"aria-label":f.value},g.$attrs),{default:xe(()=>[be(g.$slots,"default",{},()=>[be(g.$slots,"before"),mt(" "+Ee(X(o).text)+" ",1),be(g.$slots,"after")])]),_:3},16,["active","to","aria-label"])):(z(),Z("a",Co({key:1,class:"external-link",href:X(o).link,rel:u.value,target:l.value,"aria-label":f.value},g.$attrs),[be(g.$slots,"default",{},()=>[be(g.$slots,"before"),mt(" "+Ee(X(o).text)+" ",1),a.value?(z(),Se(T,{key:0})):Le("",!0),be(g.$slots,"after")])],16,eh))}}}),wt=Ce(th,[["__file","AutoLink.vue"]]),nh={class:"hero"},rh={key:0,id:"main-title"},oh={key:1,class:"description"},sh={key:2,class:"actions"},ih=he({__name:"HomeHero",setup(e){const t=ht(),n=Qo(),r=rs(),o=I(()=>r.value&&t.value.heroImageDark!==void 0?t.value.heroImageDark:t.value.heroImage),s=I(()=>t.value.heroAlt||l.value||"hero"),i=I(()=>t.value.heroHeight||280),l=I(()=>t.value.heroText===null?null:t.value.heroText||n.value.title||"Hello"),a=I(()=>t.value.tagline===null?null:t.value.tagline||n.value.description||"Welcome to your VuePress site"),c=I(()=>Array.isArray(t.value.actions)?t.value.actions.map(({text:f,link:d,type:m="primary"})=>({text:f,link:d,type:m})):[]),u=()=>{if(!o.value)return null;const f=oe("img",{src:rr(o.value),alt:s.value,height:i.value});return t.value.heroImageDark===void 0?f:oe(Zo,()=>f)};return(f,d)=>(z(),Z("header",nh,[ie(u),l.value?(z(),Z("h1",rh,Ee(l.value),1)):Le("",!0),a.value?(z(),Z("p",oh,Ee(a.value),1)):Le("",!0),c.value.length?(z(),Z("p",sh,[(z(!0),Z(_e,null,Ft(c.value,m=>(z(),Se(wt,{key:m.text,class:Ge(["action-button",[m.type]]),item:m},null,8,["class","item"]))),128))])):Le("",!0)]))}}),lh=Ce(ih,[["__file","HomeHero.vue"]]),ah={class:"home"},ch=he({__name:"Home",setup(e){return(t,n)=>(z(),Z("main",ah,[ie(lh),ie(Jp),ie(Up),ie(Zp)]))}}),uh=Ce(ch,[["__file","Home.vue"]]),fh=["aria-hidden"],dh=he({__name:"NavbarBrand",setup(e){const t=Wt(),n=Qo(),r=Ne(),o=rs(),s=I(()=>r.value.home||t.value),i=I(()=>n.value.title),l=I(()=>o.value&&r.value.logoDark!==void 0?r.value.logoDark:r.value.logo),a=I(()=>r.value.logoAlt??i.value),c=I(()=>i.value.toLocaleUpperCase().trim()===a.value.toLocaleUpperCase().trim()),u=()=>{if(!l.value)return null;const f=oe("img",{class:"logo",src:rr(l.value),alt:a.value});return r.value.logoDark===void 0?f:oe(Zo,()=>f)};return(f,d)=>(z(),Se(X(or),{to:s.value},{default:xe(()=>[ie(u),i.value?(z(),Z("span",{key:0,class:Ge(["site-name",{"can-hide":l.value}]),"aria-hidden":c.value},Ee(i.value),11,fh)):Le("",!0)]),_:1},8,["to"]))}}),ph=Ce(dh,[["__file","NavbarBrand.vue"]]),hh=he({__name:"DropdownTransition",setup(e){const t=r=>{r.style.height=r.scrollHeight+"px"},n=r=>{r.style.height=""};return(r,o)=>(z(),Se(wn,{name:"dropdown",onEnter:t,onAfterEnter:n,onBeforeLeave:t},{default:xe(()=>[be(r.$slots,"default")]),_:3}))}}),sa=Ce(hh,[["__file","DropdownTransition.vue"]]),mh=["aria-label"],gh={class:"title"},vh=ne("span",{class:"arrow down"},null,-1),bh=["aria-label"],_h={class:"title"},yh={class:"navbar-dropdown"},wh={class:"navbar-dropdown-subtitle"},kh={key:1},Eh={class:"navbar-dropdown-subitem-wrapper"},Ch=he({__name:"NavbarDropdown",props:{item:{type:Object,required:!0}},setup(e){const t=e,{item:n}=Hr(t),r=I(()=>n.value.ariaLabel||n.value.text),o=ce(!1),s=Kt();De(()=>s.path,()=>{o.value=!1});const i=a=>{a.detail===0?o.value=!o.value:o.value=!1},l=(a,c)=>c[c.length-1]===a;return(a,c)=>(z(),Z("div",{class:Ge(["navbar-dropdown-wrapper",{open:o.value}])},[ne("button",{class:"navbar-dropdown-title",type:"button","aria-label":r.value,onClick:i},[ne("span",gh,Ee(X(n).text),1),vh],8,mh),ne("button",{class:"navbar-dropdown-title-mobile",type:"button","aria-label":r.value,onClick:c[0]||(c[0]=u=>o.value=!o.value)},[ne("span",_h,Ee(X(n).text),1),ne("span",{class:Ge(["arrow",o.value?"down":"right"])},null,2)],8,bh),ie(sa,null,{default:xe(()=>[xr(ne("ul",yh,[(z(!0),Z(_e,null,Ft(X(n).children,u=>(z(),Z("li",{key:u.text,class:"navbar-dropdown-item"},[u.children?(z(),Z(_e,{key:0},[ne("h4",wh,[u.link?(z(),Se(wt,{key:0,item:u,onFocusout:f=>l(u,X(n).children)&&u.children.length===0&&(o.value=!1)},null,8,["item","onFocusout"])):(z(),Z("span",kh,Ee(u.text),1))]),ne("ul",Eh,[(z(!0),Z(_e,null,Ft(u.children,f=>(z(),Z("li",{key:f.link,class:"navbar-dropdown-subitem"},[ie(wt,{item:f,onFocusout:d=>l(f,u.children)&&l(u,X(n).children)&&(o.value=!1)},null,8,["item","onFocusout"])]))),128))])],64)):(z(),Se(wt,{key:1,item:u,onFocusout:f=>l(u,X(n).children)&&(o.value=!1)},null,8,["item","onFocusout"]))]))),128))],512),[[Ir,o.value]])]),_:1})],2))}}),Sh=Ce(Ch,[["__file","NavbarDropdown.vue"]]),xh=["aria-label"],Lh=he({__name:"NavbarItems",setup(e){const t=()=>{const f=Kt(),d=bp(),m=Wt(),g=Dl(),y=Qo(),E=xp(),T=Ne();return I(()=>{const x=Object.keys(g.value.locales);if(x.length<2)return[];const v=f.path,k=f.fullPath;return[{text:`${T.value.selectLanguageText}`,ariaLabel:`${T.value.selectLanguageAriaLabel??T.value.selectLanguageText}`,children:x.map(O=>{var w,N;const H=((w=g.value.locales)==null?void 0:w[O])??{},b=((N=E.value.locales)==null?void 0:N[O])??{},G=`${H.lang}`,L=b.selectLanguageName??G;if(G===y.value.lang)return{text:L,activeMatch:/./,link:f.hash??"#"};const W=v.replace(m.value,O);return{text:L,link:d.value.some(te=>te===W)?k.replace(v,W):b.home??O}})}]})},n=()=>{const f=Ne(),d=I(()=>f.value.repo),m=I(()=>d.value?ea(d.value):null),g=I(()=>d.value&&!nr(d.value)?`https://github.com/${d.value}`:d.value),y=I(()=>g.value?f.value.repoLabel?f.value.repoLabel:m.value===null?"Source":m.value:null);return I(()=>!g.value||!y.value?[]:[{text:y.value,link:g.value}])},r=f=>it(f)?Ql(f):f.children?{...f,children:f.children.map(d=>r(d))}:f,o=()=>{const f=Ne();return I(()=>(f.value.navbar||[]).map(d=>r(d)))},s=ce(!1),i=o(),l=t(),a=n(),c=I(()=>[...i.value,...l.value,...a.value]);oa(qn.MOBILE,f=>{window.innerWidthNe().value.navbarLabel??"site navigation");return(f,d)=>c.value.length?(z(),Z("nav",{key:0,class:"navbar-items","aria-label":u.value},[(z(!0),Z(_e,null,Ft(c.value,m=>(z(),Z("div",{key:m.text,class:"navbar-item"},["children"in m?(z(),Se(Sh,{key:0,item:m,class:Ge(s.value?"mobile":"")},null,8,["item","class"])):(z(),Se(wt,{key:1,item:m},null,8,["item"]))]))),128))],8,xh)):Le("",!0)}}),ia=Ce(Lh,[["__file","NavbarItems.vue"]]),Ah=["title"],Th={class:"icon",focusable:"false",viewBox:"0 0 32 32"},Ph=eu('',9),Rh=[Ph],Ih={class:"icon",focusable:"false",viewBox:"0 0 32 32"},Oh=ne("path",{d:"M13.502 5.414a15.075 15.075 0 0 0 11.594 18.194a11.113 11.113 0 0 1-7.975 3.39c-.138 0-.278.005-.418 0a11.094 11.094 0 0 1-3.2-21.584M14.98 3a1.002 1.002 0 0 0-.175.016a13.096 13.096 0 0 0 1.825 25.981c.164.006.328 0 .49 0a13.072 13.072 0 0 0 10.703-5.555a1.01 1.01 0 0 0-.783-1.565A13.08 13.08 0 0 1 15.89 4.38A1.015 1.015 0 0 0 14.98 3z",fill:"currentColor"},null,-1),$h=[Oh],Mh=he({__name:"ToggleColorModeButton",setup(e){const t=Ne(),n=rs(),r=()=>{n.value=!n.value};return(o,s)=>(z(),Z("button",{class:"toggle-color-mode-button",title:X(t).toggleColorMode,onClick:r},[xr((z(),Z("svg",Th,Rh,512)),[[Ir,!X(n)]]),xr((z(),Z("svg",Ih,$h,512)),[[Ir,X(n)]])],8,Ah))}}),Nh=Ce(Mh,[["__file","ToggleColorModeButton.vue"]]),Hh=["title"],Fh=ne("div",{class:"icon","aria-hidden":"true"},[ne("span"),ne("span"),ne("span")],-1),Dh=[Fh],jh=he({__name:"ToggleSidebarButton",emits:["toggle"],setup(e){const t=Ne();return(n,r)=>(z(),Z("div",{class:"toggle-sidebar-button",title:X(t).toggleSidebar,"aria-expanded":"false",role:"button",tabindex:"0",onClick:r[0]||(r[0]=o=>n.$emit("toggle"))},Dh,8,Hh))}}),Bh=Ce(jh,[["__file","ToggleSidebarButton.vue"]]),zh=he({__name:"Navbar",emits:["toggle-sidebar"],setup(e){const t=Ne(),n=ce(null),r=ce(null),o=ce(0),s=I(()=>o.value?{maxWidth:o.value+"px"}:{}),i=(l,a)=>{var f,d,m;const c=(m=(d=(f=l==null?void 0:l.ownerDocument)==null?void 0:f.defaultView)==null?void 0:d.getComputedStyle(l,null))==null?void 0:m[a],u=Number.parseInt(c,10);return Number.isNaN(u)?0:u};return oa(qn.MOBILE,l=>{var c;const a=i(n.value,"paddingLeft")+i(n.value,"paddingRight");window.innerWidth{const c=nn("NavbarSearch");return z(),Z("header",{ref_key:"navbar",ref:n,class:"navbar"},[ie(Bh,{onToggle:a[0]||(a[0]=u=>l.$emit("toggle-sidebar"))}),ne("span",{ref_key:"navbarBrand",ref:r},[ie(ph)],512),ne("div",{class:"navbar-items-wrapper",style:Yn(s.value)},[be(l.$slots,"before"),ie(ia,{class:"can-hide"}),be(l.$slots,"after"),X(t).colorModeSwitch?(z(),Se(Nh,{key:0})):Le("",!0),ie(c)],4)],512)}}}),Kh=Ce(zh,[["__file","Navbar.vue"]]),Wh={class:"vp-page-meta"},Vh={key:0,class:"vp-meta-item edit-link"},Uh=ne("svg",{class:"icon",viewBox:"0 0 1024 1024"},[ne("g",{fill:"currentColor"},[ne("path",{d:"M430.818 653.65a60.46 60.46 0 0 1-50.96-93.281l71.69-114.012 7.773-10.365L816.038 80.138A60.46 60.46 0 0 1 859.225 62a60.46 60.46 0 0 1 43.186 18.138l43.186 43.186a60.46 60.46 0 0 1 0 86.373L588.879 565.55l-8.637 8.637-117.466 68.234a60.46 60.46 0 0 1-31.958 11.229z"}),ne("path",{d:"M728.802 962H252.891A190.883 190.883 0 0 1 62.008 771.98V296.934a190.883 190.883 0 0 1 190.883-192.61h267.754a60.46 60.46 0 0 1 0 120.92H252.891a69.962 69.962 0 0 0-69.098 69.099V771.98a69.962 69.962 0 0 0 69.098 69.098h475.911A69.962 69.962 0 0 0 797.9 771.98V503.363a60.46 60.46 0 1 1 120.922 0V771.98A190.883 190.883 0 0 1 728.802 962z"})])],-1),qh={class:"vp-meta-item git-info"},Gh={key:0,class:"vp-meta-item last-updated"},Jh={class:"meta-item-label"},Yh={class:"meta-item-info"},Xh={key:1,class:"vp-meta-item contributors"},Qh={class:"meta-item-label"},Zh={class:"meta-item-info"},em=["title"],tm=he({__name:"PageMeta",setup(e){const t=()=>{const a=Ne(),c=gn(),u=ht();return I(()=>{if(!(u.value.editLink??a.value.editLink??!0))return null;const{repo:d,docsRepo:m=d,docsBranch:g="main",docsDir:y="",editLinkText:E}=a.value;if(!m)return null;const T=Mp({docsRepo:m,docsBranch:g,docsDir:y,filePathRelative:c.value.filePathRelative,editLinkPattern:u.value.editLinkPattern??a.value.editLinkPattern});return T?{text:E??"Edit this page",link:T}:null})},n=()=>{const a=Ne(),c=gn(),u=ht();return I(()=>{var m,g;return!(u.value.lastUpdated??a.value.lastUpdated??!0)||!((m=c.value.git)!=null&&m.updatedTime)?null:new Date((g=c.value.git)==null?void 0:g.updatedTime).toLocaleString()})},r=()=>{const a=Ne(),c=gn(),u=ht();return I(()=>{var d;return u.value.contributors??a.value.contributors??!0?((d=c.value.git)==null?void 0:d.contributors)??null:null})},o=Ne(),s=t(),i=n(),l=r();return(a,c)=>{const u=nn("ClientOnly");return z(),Z("footer",Wh,[X(s)?(z(),Z("div",Vh,[ie(wt,{class:"label",item:X(s)},{before:xe(()=>[Uh]),_:1},8,["item"])])):Le("",!0),ne("div",qh,[X(i)?(z(),Z("div",Gh,[ne("span",Jh,Ee(X(o).lastUpdatedText)+": ",1),ie(u,null,{default:xe(()=>[ne("span",Yh,Ee(X(i)),1)]),_:1})])):Le("",!0),X(l)&&X(l).length?(z(),Z("div",Xh,[ne("span",Qh,Ee(X(o).contributorsText)+": ",1),ne("span",Zh,[(z(!0),Z(_e,null,Ft(X(l),(f,d)=>(z(),Z(_e,{key:d},[ne("span",{class:"contributor",title:`email: ${f.email}`},Ee(f.name),9,em),d!==X(l).length-1?(z(),Z(_e,{key:0},[mt(", ")],64)):Le("",!0)],64))),128))])])):Le("",!0)])])}}}),nm=Ce(tm,[["__file","PageMeta.vue"]]),rm=["aria-label"],om={class:"hint"},sm=ne("span",{class:"arrow left"},null,-1),im={class:"link"},lm={class:"hint"},am=ne("span",{class:"arrow right"},null,-1),cm={class:"link"},um=he({__name:"PageNav",setup(e){const t=(f,d)=>{if(f===!1)return null;if(it(f)){const{notFound:m,meta:g,path:y}=mi(f,d);return m?{text:y,link:y}:{text:g.title||y,link:y}}return Go(f)?{...f,link:mi(f.link,d).path}:!1},n=(f,d,m)=>{const g=f.findIndex(y=>y.link===d);if(g!==-1){const y=f[g+m];return y!=null&&y.link?y:null}for(const y of f)if(y.children){const E=n(y.children,d,m);if(E)return E}return null},r=ht(),o=os(),s=Ne(),i=Kt(),l=Pp(),a=I(()=>{const f=t(r.value.prev,i.path);return f!==!1?f:n(o.value,i.path,-1)}),c=I(()=>{const f=t(r.value.next,i.path);return f!==!1?f:n(o.value,i.path,1)}),u=I(()=>Ne().value.pageNavbarLabel??"page navigation");return at("keydown",f=>{f.altKey&&(f.key==="ArrowRight"?c.value&&(l(c.value.link),f.preventDefault()):f.key==="ArrowLeft"&&a.value&&(l(a.value.link),f.preventDefault()))}),(f,d)=>a.value||c.value?(z(),Z("nav",{key:0,class:"vp-page-nav","aria-label":u.value},[a.value?(z(),Se(wt,{key:0,class:"prev",item:a.value},{default:xe(()=>[ne("div",om,[sm,mt(" "+Ee(X(s).prev??"Prev"),1)]),ne("div",im,[ne("span",null,Ee(a.value.text),1)])]),_:1},8,["item"])):Le("",!0),c.value?(z(),Se(wt,{key:1,class:"next",item:c.value},{default:xe(()=>[ne("div",lm,[mt(Ee(X(s).next??"Next")+" ",1),am]),ne("div",cm,[ne("span",null,Ee(c.value.text),1)])]),_:1},8,["item"])):Le("",!0)],8,rm)):Le("",!0)}}),fm=Ce(um,[["__file","PageNav.vue"]]),dm={class:"page"},pm={class:"theme-default-content"},hm=he({__name:"Page",setup(e){return(t,n)=>{const r=nn("Content");return z(),Z("main",dm,[be(t.$slots,"top"),ne("div",pm,[be(t.$slots,"content-top"),ie(r),be(t.$slots,"content-bottom")]),ie(nm),ie(fm),be(t.$slots,"bottom")])}}}),mm=Ce(hm,[["__file","Page.vue"]]),gm={class:"sidebar-item-children"},vm=he({__name:"SidebarItem",props:{item:{type:Object,required:!0},depth:{type:Number,required:!1,default:0}},setup(e){const t=e,{item:n,depth:r}=Hr(t),o=Kt(),s=ln(),i=I(()=>Zl(n.value,o)),l=I(()=>({"sidebar-item":!0,"sidebar-heading":r.value===0,active:i.value,collapsible:n.value.collapsible})),a=I(()=>n.value.collapsible?i.value:!0),[c,u]=kd(a.value),f=m=>{n.value.collapsible&&(m.preventDefault(),u())},d=s.afterEach(m=>{yn(()=>{c.value=a.value})});return Br(()=>{d()}),(m,g)=>{var E;const y=nn("SidebarItem",!0);return z(),Z("li",null,[X(n).link?(z(),Se(wt,{key:0,class:Ge(l.value),item:X(n)},null,8,["class","item"])):(z(),Z("p",{key:1,tabindex:"0",class:Ge(l.value),onClick:f,onKeydown:Hu(f,["enter"])},[mt(Ee(X(n).text)+" ",1),X(n).collapsible?(z(),Z("span",{key:0,class:Ge(["arrow",X(c)?"down":"right"])},null,2)):Le("",!0)],34)),(E=X(n).children)!=null&&E.length?(z(),Se(sa,{key:2},{default:xe(()=>[xr(ne("ul",gm,[(z(!0),Z(_e,null,Ft(X(n).children,T=>(z(),Se(y,{key:`${X(r)}${T.text}${T.link}`,item:T,depth:X(r)+1},null,8,["item","depth"]))),128))],512),[[Ir,X(c)]])]),_:1})):Le("",!0)])}}}),bm=Ce(vm,[["__file","SidebarItem.vue"]]),_m={key:0,class:"sidebar-items"},ym=he({__name:"SidebarItems",setup(e){const t=Kt(),n=os();return He(()=>{De(()=>t.hash,r=>{const o=document.querySelector(".sidebar");if(!o)return;const s=document.querySelector(`.sidebar a.sidebar-item[href="${t.path}${r}"]`);if(!s)return;const{top:i,height:l}=o.getBoundingClientRect(),{top:a,height:c}=s.getBoundingClientRect();ai+l&&s.scrollIntoView(!1)})}),(r,o)=>X(n).length?(z(),Z("ul",_m,[(z(!0),Z(_e,null,Ft(X(n),s=>(z(),Se(bm,{key:`${s.text}${s.link}`,item:s},null,8,["item"]))),128))])):Le("",!0)}}),wm=Ce(ym,[["__file","SidebarItems.vue"]]),km={class:"sidebar"},Em=he({__name:"Sidebar",setup(e){return(t,n)=>(z(),Z("aside",km,[ie(ia),be(t.$slots,"top"),ie(wm),be(t.$slots,"bottom")]))}}),Cm=Ce(Em,[["__file","Sidebar.vue"]]),Sm=he({__name:"Layout",setup(e){const t=gn(),n=ht(),r=Ne(),o=I(()=>n.value.navbar!==!1&&r.value.navbar!==!1),s=os(),i=ce(!1),l=E=>{i.value=typeof E=="boolean"?E:!i.value},a={x:0,y:0},c=E=>{a.x=E.changedTouches[0].clientX,a.y=E.changedTouches[0].clientY},u=E=>{const T=E.changedTouches[0].clientX-a.x,x=E.changedTouches[0].clientY-a.y;Math.abs(T)>Math.abs(x)&&Math.abs(T)>40&&(T>0&&a.x<=80?l(!0):l(!1))},f=I(()=>[{"no-navbar":!o.value,"no-sidebar":!s.value.length,"sidebar-open":i.value},n.value.pageClass]);let d;He(()=>{d=ln().afterEach(()=>{l(!1)})}),zr(()=>{d()});const m=Xl(),g=m.resolve,y=m.pending;return(E,T)=>(z(),Z("div",{class:Ge(["theme-container",f.value]),onTouchstart:c,onTouchend:u},[be(E.$slots,"navbar",{},()=>[o.value?(z(),Se(Kh,{key:0,onToggleSidebar:l},{before:xe(()=>[be(E.$slots,"navbar-before")]),after:xe(()=>[be(E.$slots,"navbar-after")]),_:3})):Le("",!0)]),ne("div",{class:"sidebar-mask",onClick:T[0]||(T[0]=x=>l(!1))}),be(E.$slots,"sidebar",{},()=>[ie(Cm,null,{top:xe(()=>[be(E.$slots,"sidebar-top")]),bottom:xe(()=>[be(E.$slots,"sidebar-bottom")]),_:3})]),be(E.$slots,"page",{},()=>[X(n).home?(z(),Se(uh,{key:0})):(z(),Se(wn,{key:1,name:"fade-slide-y",mode:"out-in",onBeforeEnter:X(g),onBeforeLeave:X(y)},{default:xe(()=>[(z(),Se(mm,{key:X(t).path},{top:xe(()=>[be(E.$slots,"page-top")]),"content-top":xe(()=>[be(E.$slots,"page-content-top")]),"content-bottom":xe(()=>[be(E.$slots,"page-content-bottom")]),bottom:xe(()=>[be(E.$slots,"page-bottom")]),_:3}))]),_:3},8,["onBeforeEnter","onBeforeLeave"]))])],34))}}),xm=Ce(Sm,[["__file","Layout.vue"]]),Lm={class:"theme-container"},Am={class:"page"},Tm={class:"theme-default-content"},Pm=ne("h1",null,"404",-1),Rm=he({__name:"NotFound",setup(e){const t=Wt(),n=Ne(),r=n.value.notFound??["Not Found"],o=()=>r[Math.floor(Math.random()*r.length)],s=n.value.home??t.value,i=n.value.backToHome??"Back to home";return(l,a)=>(z(),Z("div",Lm,[ne("main",Am,[ne("div",Tm,[Pm,ne("blockquote",null,Ee(o()),1),ie(X(or),{to:X(s)},{default:xe(()=>[mt(Ee(X(i)),1)]),_:1},8,["to"])])])]))}}),Im=Ce(Rm,[["__file","NotFound.vue"]]),Om=ct({enhance({app:e,router:t}){fo("Badge")||e.component("Badge",kp),fo("CodeGroup")||e.component("CodeGroup",Ep),fo("CodeGroupItem")||e.component("CodeGroupItem",Sp),e.component("AutoLinkExternalIcon",()=>{const r=e.component("ExternalLinkIcon");return r?oe(r):null}),e.component("NavbarSearch",()=>{const r=e.component("Docsearch")||e.component("SearchBox");return r?oe(r):null});const n=t.options.scrollBehavior;t.options.scrollBehavior=async(...r)=>(await Xl().wait(),n(...r))},setup(){Lp(),Np()},layouts:{Layout:xm,NotFound:Im}}),$m=e=>e instanceof Element?document.activeElement===e&&(["TEXTAREA","SELECT","INPUT"].includes(e.tagName)||e.hasAttribute("contenteditable")):!1,Mm=(e,t)=>t.some(n=>{if(it(n))return n===e.key;const{key:r,ctrl:o=!1,shift:s=!1,alt:i=!1}=n;return r===e.key&&o===e.ctrlKey&&s===e.shiftKey&&i===e.altKey}),Nm=/[^\x00-\x7F]/,Hm=e=>e.split(/\s+/g).map(t=>t.trim()).filter(t=>!!t),vi=e=>e.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),bi=(e,t)=>{const n=t.join(" "),r=Hm(e);if(Nm.test(e))return r.some(i=>n.toLowerCase().indexOf(i)>-1);const o=e.endsWith(" ");return new RegExp(r.map((i,l)=>r.length===l+1&&!o?`(?=.*\\b${vi(i)})`:`(?=.*\\b${vi(i)}\\b)`).join("")+".+","gi").test(n)},Fm=({input:e,hotKeys:t})=>{if(t.value.length===0)return;const n=r=>{e.value&&Mm(r,t.value)&&!$m(r.target)&&(r.preventDefault(),e.value.focus())};He(()=>{document.addEventListener("keydown",n)}),Br(()=>{document.removeEventListener("keydown",n)})},Dm=[{title:"Welcome",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:[]}],path:"/",pathLocale:"/",extraFields:[]},{title:"Welcome",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:[]}],path:"/en/",pathLocale:"/en/",extraFields:[]},{title:"",headers:[{level:2,title:"Prerequisites",slug:"prerequisites",link:"#prerequisites",children:[]},{level:2,title:"Liens utiles",slug:"liens-utiles",link:"#liens-utiles",children:[]}],path:"/fr/",pathLocale:"/fr/",extraFields:[]},{title:"📚 Backend development",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:[]}],path:"/en/backend-development/",pathLocale:"/en/",extraFields:[]},{title:"📚 Frontend development",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:[]}],path:"/en/front-development/",pathLocale:"/en/",extraFields:[]},{title:"📚 Kotlin language features",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:[]}],path:"/en/kotlin-features/",pathLocale:"/en/",extraFields:[]},{title:"📚 Advanced and other Kotlin features",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:[]}],path:"/en/kotlin-features-advanced/",pathLocale:"/en/",extraFields:[]},{title:"🛠 Let's make a cross-plaform app !",headers:[],path:"/en/other-technologies/",pathLocale:"/en/",extraFields:[]},{title:"🚀 Presentation of Kotlin",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:[]}],path:"/en/presentation/",pathLocale:"/en/",extraFields:[]},{title:"📅 Workshops",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:[]}]}],path:"/en/workshops/",pathLocale:"/en/",extraFields:[]},{title:"📚 Développement du backend",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:[]}],path:"/fr/backend-development/",pathLocale:"/fr/",extraFields:[]},{title:"📚 Développement frontend",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:[]}],path:"/fr/front-development/",pathLocale:"/fr/",extraFields:[]},{title:"📚 Fonctionnalités du langage Kotlin",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:[]}],path:"/fr/kotlin-features/",pathLocale:"/fr/",extraFields:[]},{title:"📚 Fonctionnalités avancées de Kotlin",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:[]}]}],path:"/fr/kotlin-features-advanced/",pathLocale:"/fr/",extraFields:[]},{title:"🛠 Construisons une app multiplateforme !",headers:[],path:"/fr/other-technologies/",pathLocale:"/fr/",extraFields:[]},{title:"🚀 Présentation de Kotlin",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:[]}],path:"/fr/presentation/",pathLocale:"/fr/",extraFields:[]},{title:"📅 Workshops",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:[]}]}],path:"/fr/workshops/",pathLocale:"/fr/",extraFields:[]},{title:"",headers:[],path:"/404.html",pathLocale:"/",extraFields:[]}],jm=ce(Dm),Bm=()=>jm,zm=({searchIndex:e,routeLocale:t,query:n,maxSuggestions:r})=>{const o=I(()=>e.value.filter(s=>s.pathLocale===t.value));return I(()=>{const s=n.value.trim().toLowerCase();if(!s)return[];const i=[],l=(a,c)=>{bi(s,[c.title])&&i.push({link:`${a.path}#${c.slug}`,title:a.title,header:c.title});for(const u of c.children){if(i.length>=r.value)return;l(a,u)}};for(const a of o.value){if(i.length>=r.value)break;if(bi(s,[a.title,...a.extraFields])){i.push({link:a.path,title:a.title});continue}for(const c of a.headers){if(i.length>=r.value)break;l(a,c)}}return i})},Km=e=>{const t=ce(0);return{focusIndex:t,focusNext:()=>{t.value{t.value>0?t.value-=1:t.value=e.value.length-1}}},Wm=he({name:"SearchBox",props:{locales:{type:Object,required:!1,default:()=>({})},hotKeys:{type:Array,required:!1,default:()=>[]},maxSuggestions:{type:Number,required:!1,default:5}},setup(e){const{locales:t,hotKeys:n,maxSuggestions:r}=Hr(e),o=ln(),s=Wt(),i=Bm(),l=ce(null),a=ce(!1),c=ce(""),u=I(()=>t.value[s.value]??{}),f=zm({searchIndex:i,routeLocale:s,query:c,maxSuggestions:r}),{focusIndex:d,focusNext:m,focusPrev:g}=Km(f);Fm({input:l,hotKeys:n});const y=I(()=>a.value&&!!f.value.length),E=()=>{y.value&&g()},T=()=>{y.value&&m()},x=v=>{if(!y.value)return;const k=f.value[v];k&&o.push(k.link).then(()=>{c.value="",d.value=0})};return()=>oe("form",{class:"search-box",role:"search"},[oe("input",{ref:l,type:"search",placeholder:u.value.placeholder,autocomplete:"off",spellcheck:!1,value:c.value,onFocus:()=>a.value=!0,onBlur:()=>a.value=!1,onInput:v=>c.value=v.target.value,onKeydown:v=>{switch(v.key){case"ArrowUp":{E();break}case"ArrowDown":{T();break}case"Enter":{v.preventDefault(),x(d.value);break}}}}),y.value&&oe("ul",{class:"suggestions",onMouseleave:()=>d.value=-1},f.value.map(({link:v,title:k,header:D},O)=>oe("li",{class:["suggestion",{focus:d.value===O}],onMouseenter:()=>d.value=O,onMousedown:()=>x(O)},oe("a",{href:v,onClick:H=>H.preventDefault()},[oe("span",{class:"page-title"},k),D&&oe("span",{class:"page-header"},`> ${D}`)]))))])}});var Vm=["s","/"],Um={"/en/":{placeholder:"Search"},"/fr/":{placeholder:"Rechercher"}};const qm=Um,Gm=Vm,Jm=5,Ym=ct({enhance({app:e}){e.component("SearchBox",t=>oe(Wm,{locales:qm,hotKeys:Gm,maxSuggestions:Jm,...t}))}});function Xm(e){return{all:e=e||new Map,on:function(t,n){var r=e.get(t);r?r.push(n):e.set(t,[n])},off:function(t,n){var r=e.get(t);r&&(n?r.splice(r.indexOf(n)>>>0,1):e.set(t,[]))},emit:function(t,n){var r=e.get(t);r&&r.slice().map(function(o){o(n)}),(r=e.get("*"))&&r.slice().map(function(o){o(t,n)})}}}const Qm=()=>{navigator.serviceWorker.getRegistration().then(e=>{e&&e.active&&(e==null||e.addEventListener("updatefound",()=>{window.location.reload(!0)}))})},Zm=async(e,t={},n=!0)=>{const{register:r}=await Pe(()=>import("./index-DTEEl-sV.js"),[]);r(e,{ready(o){var s;n&&console.info("[Service Worker]: active"),(s=t.ready)==null||s.call(t,o)},registered(o){var s;n&&console.log("[Service Worker]: registered"),(s=t.registered)==null||s.call(t,o)},cached(o){var s;n&&console.log("[Service Worker]: cached"),(s=t.cached)==null||s.call(t,o)},async updatefound(o){var s;await navigator.serviceWorker.getRegistration()&&(n&&console.log("[Service Worker]: update found"),(s=t.updatefound)==null||s.call(t,o))},updated(o){var s;n&&console.log("[Service Worker]: updated"),(s=t.updated)==null||s.call(t,o)},offline(){var o;n&&console.log("[Service Worker]: offline"),(o=t.offline)==null||o.call(t)},error(o){var s;n&&console.error("[Service Worker]: ",o),(s=t.error)==null||s.call(t,o)}})},eg=e=>{const t=e.waiting;if(!t)return;const n=new MessageChannel;t.postMessage({type:"SKIP_WAITING"},[n.port2])},la=Symbol(""),tg=()=>{const e=ze(la);if(!e)throw new Error("usePwaEvent() is called without provider.");return e},ng=async(e,t)=>Zm(rr(e),{ready(n){t.emit("ready",n)},registered(n){t.emit("registered",n)},cached(n){t.emit("cached",n)},updatefound(n){t.emit("updatefound",n)},updated(n){const r="service-worker-version",o=Number(localStorage.getItem(r)||0);localStorage.setItem(r,(o+1).toString()),localStorage.removeItem("manifest"),t.emit("updated",n)},offline(){t.emit("offline")},error(n){t.emit("error",n)}}),rg=(e,t=!1)=>{const n=Xm();Nt(la,n),He(async()=>{var o;let r=!1;(o=navigator.serviceWorker)!=null&&o.controller&&navigator.serviceWorker.addEventListener("controllerchange",()=>{r||(r=!0,window.location.reload())}),t&&Qm(),await ng(e,n)})},og=()=>{He(()=>{if(window.matchMedia("(display-mode: standalone)").matches){const t=document.head.querySelector('meta[name="viewport"]');if(t){t.setAttribute("content","width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover");return}const n=document.createElement("meta");n.name="viewport",n.content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover",document.head.appendChild(n)}})},sg=e=>{const t=Wt();return I(()=>e[t.value]??{})},aa=({name:e="",color:t="currentColor"},{slots:n})=>{var r;return oe("svg",{xmlns:"http://www.w3.org/2000/svg",class:["icon",`${e}-icon`],viewBox:"0 0 1024 1024",fill:t,"aria-label":`${e} icon`},(r=n.default)==null?void 0:r.call(n))};aa.displayName="SVGWrapper";const ca=()=>oe(aa,{name:"update"},()=>oe("path",{d:"M949.949 146.25v255.826c0 21.981-13.989 35.97-35.97 35.97H658.154c-13.988 0-25.983-7.992-33.973-21.981-5.997-13.989-4-27.977 7.991-39.97l79.942-77.946c-55.954-51.973-121.918-77.955-199.863-77.955-37.975 0-75.95 8.002-113.924 21.99-37.975 15.985-67.948 37.976-91.934 63.957-25.982 23.987-47.973 53.96-63.957 91.934-29.983 73.955-29.983 153.895 0 227.85 15.984 37.976 37.975 67.947 63.957 91.934 23.986 25.982 53.959 47.973 91.934 63.956 37.974 13.989 75.95 21.991 113.924 21.991 45.967 0 87.942-9.998 127.913-29.982 41.976-17.99 75.951-45.967 101.931-83.943 7.993-4 11.994-5.995 13.989-5.995 5.997 0 9.998 1.994 13.988 5.995l77.958 77.946c3.989 4 5.986 7.993 5.986 11.994 0 1.994-1.996 5.995-3.99 11.994-43.973 51.962-93.941 91.934-151.9 117.914-53.958 25.983-115.92 39.972-185.874 39.972-61.961 0-119.921-11.984-169.89-33.973-57.96-25.985-105.923-57.963-139.896-93.943-35.98-33.972-67.958-81.936-93.94-139.897-45.967-101.93-45.967-237.846 0-339.777 25.982-57.96 57.96-105.923 93.94-139.896 33.973-35.98 81.936-67.958 139.896-93.94 49.968-21.99 107.928-33.974 169.89-33.974 55.963 0 109.923 9.988 161.885 29.973 53.97 21.99 101.933 51.963 139.908 89.938l73.954-73.944c9.987-9.998 23.987-13.988 39.971-8.002 13.988 8.002 21.98 19.995 21.98 33.984z"}));ca.displayName="UpdateIcon";const ig=he({name:"PwaReadyPopup",props:{locales:{type:Object,required:!0}},slots:Object,setup(e,{slots:t}){const n=sg(e.locales),r=sn(),o=I(()=>!!r.value),s=()=>{r.value&&(eg(r.value),r.value=void 0)};return He(()=>{tg().on("updated",l=>{l&&(r.value=l)})}),()=>oe(wn,{name:"popup"},()=>{var i;return((i=t.default)==null?void 0:i.call(t,{isReady:o.value,reload:s}))||(o.value?oe("button",{type:"button",class:"sw-update-popup",tabindex:0,onClick:()=>s()},[n.value.update,oe("span",{class:"icon-wrapper"},oe(ca))]):null)})}});var lg={"/en/":{install:"Install",iOSInstall:"Tap the share button and then 'Add to Home Screen'",cancel:"Cancel",close:"Close",prevImage:"Previous Image",nextImage:"Next Image",desc:"Description",feature:"Key Features",explain:"This app can be installed on your PC or mobile device. This will allow this web app to look and behave like any other installed app. You will find it in your app lists and be able to pin it to your home screen, start menus or task bars. This installed web app will also be able to safely interact with other apps and your operating system. ",hint:"New content found.",update:"New content is available."},"/fr/":{install:"Installer",iOSInstall:"Appuyez sur le bouton partager puis 'Ajouter à l'écran d'accueil'",cancel:"Annuler",close:"Fermer",prevImage:"Image précédente",nextImage:"Image suivante",desc:"Description",feature:"Composants clés",explain:"Cette app peut être installée sur PC ou smartphone. Cela permettra de rendre cette page web comme n'importe quelle autre application. Vous la trouverez dans votre liste d'application et serez capable de la pin sur votre écran principal et divers menus. L'application web installée sera capable d'interagir avec les autres applications et le système d'exploitation.",hint:"New content found.",update:"New content is available."},"/":{install:"Install",iOSInstall:"Tap the share button and then 'Add to Home Screen'",cancel:"Cancel",close:"Close",prevImage:"Previous Image",nextImage:"Next Image",desc:"Description",feature:"Key Features",explain:"This app can be installed on your PC or mobile device. This will allow this web app to look and behave like any other installed app. You will find it in your app lists and be able to pin it to your home screen, start menus or task bars. This installed web app will also be able to safely interact with other apps and your operating system. ",hint:"New content found.",update:"New content is available."}};const ag=lg,cg=()=>oe(ig,{locales:ag}),ug=ct({setup:()=>{rg("service-worker.js",!1),og()},rootComponents:[cg]}),fg=e=>new Promise(t=>setTimeout(t,e));/*! medium-zoom 1.1.0 | MIT License | https://github.com/francoischalifour/medium-zoom */var Jt=Object.assign||function(e){for(var t=1;t1&&arguments[1]!==void 0?arguments[1]:{},r=window.Promise||function(w){function N(){}w(N,N)},o=function(w){var N=w.target;if(N===G){g();return}v.indexOf(N)!==-1&&y({target:N})},s=function(){if(!(D||!b.original)){var w=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;Math.abs(O-w)>H.scrollOffset&&setTimeout(g,150)}},i=function(w){var N=w.key||w.keyCode;(N==="Escape"||N==="Esc"||N===27)&&g()},l=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},N=w;if(w.background&&(G.style.background=w.background),w.container&&w.container instanceof Object&&(N.container=Jt({},H.container,w.container)),w.template){var te=wr(w.template)?w.template:document.querySelector(w.template);N.template=te}return H=Jt({},H,N),v.forEach(function(le){le.dispatchEvent(cn("medium-zoom:update",{detail:{zoom:L}}))}),L},a=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};return e(Jt({},H,w))},c=function(){for(var w=arguments.length,N=Array(w),te=0;te0?N.reduce(function(R,J){return[].concat(R,yi(J))},[]):v;return le.forEach(function(R){R.classList.remove("medium-zoom-image"),R.dispatchEvent(cn("medium-zoom:detach",{detail:{zoom:L}}))}),v=v.filter(function(R){return le.indexOf(R)===-1}),L},f=function(w,N){var te=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return v.forEach(function(le){le.addEventListener("medium-zoom:"+w,N,te)}),k.push({type:"medium-zoom:"+w,listener:N,options:te}),L},d=function(w,N){var te=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return v.forEach(function(le){le.removeEventListener("medium-zoom:"+w,N,te)}),k=k.filter(function(le){return!(le.type==="medium-zoom:"+w&&le.listener.toString()===N.toString())}),L},m=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},N=w.target,te=function(){var R={width:document.documentElement.clientWidth,height:document.documentElement.clientHeight,left:0,top:0,right:0,bottom:0},J=void 0,V=void 0;if(H.container)if(H.container instanceof Object)R=Jt({},R,H.container),J=R.width-R.left-R.right-H.margin*2,V=R.height-R.top-R.bottom-H.margin*2;else{var Ie=wr(H.container)?H.container:document.querySelector(H.container),Fe=Ie.getBoundingClientRect(),Ve=Fe.width,je=Fe.height,Et=Fe.left,Ct=Fe.top;R=Jt({},R,{width:Ve,height:je,left:Et,top:Ct})}J=J||R.width-H.margin*2,V=V||R.height-H.margin*2;var ut=b.zoomedHd||b.original,Oe=_i(ut)?J:ut.naturalWidth||J,S=_i(ut)?V:ut.naturalHeight||V,K=ut.getBoundingClientRect(),j=K.top,q=K.left,ue=K.width,ge=K.height,p=Math.min(Math.max(ue,Oe),J)/ue,h=Math.min(Math.max(ge,S),V)/ge,_=Math.min(p,h),A=(-q+(J-ue)/2+H.margin+R.left)/_,C=(-j+(V-ge)/2+H.margin+R.top)/_,$="scale("+_+") translate3d("+A+"px, "+C+"px, 0)";b.zoomed.style.transform=$,b.zoomedHd&&(b.zoomedHd.style.transform=$)};return new r(function(le){if(N&&v.indexOf(N)===-1){le(L);return}var R=function Ve(){D=!1,b.zoomed.removeEventListener("transitionend",Ve),b.original.dispatchEvent(cn("medium-zoom:opened",{detail:{zoom:L}})),le(L)};if(b.zoomed){le(L);return}if(N)b.original=N;else if(v.length>0){var J=v;b.original=J[0]}else{le(L);return}if(b.original.dispatchEvent(cn("medium-zoom:open",{detail:{zoom:L}})),O=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,D=!0,b.zoomed=hg(b.original),document.body.appendChild(G),H.template){var V=wr(H.template)?H.template:document.querySelector(H.template);b.template=document.createElement("div"),b.template.appendChild(V.content.cloneNode(!0)),document.body.appendChild(b.template)}if(b.original.parentElement&&b.original.parentElement.tagName==="PICTURE"&&b.original.currentSrc&&(b.zoomed.src=b.original.currentSrc),document.body.appendChild(b.zoomed),window.requestAnimationFrame(function(){document.body.classList.add("medium-zoom--opened")}),b.original.classList.add("medium-zoom-image--hidden"),b.zoomed.classList.add("medium-zoom-image--opened"),b.zoomed.addEventListener("click",g),b.zoomed.addEventListener("transitionend",R),b.original.getAttribute("data-zoom-src")){b.zoomedHd=b.zoomed.cloneNode(),b.zoomedHd.removeAttribute("srcset"),b.zoomedHd.removeAttribute("sizes"),b.zoomedHd.removeAttribute("loading"),b.zoomedHd.src=b.zoomed.getAttribute("data-zoom-src"),b.zoomedHd.onerror=function(){clearInterval(Ie),console.warn("Unable to reach the zoom image target "+b.zoomedHd.src),b.zoomedHd=null,te()};var Ie=setInterval(function(){b.zoomedHd.complete&&(clearInterval(Ie),b.zoomedHd.classList.add("medium-zoom-image--opened"),b.zoomedHd.addEventListener("click",g),document.body.appendChild(b.zoomedHd),te())},10)}else if(b.original.hasAttribute("srcset")){b.zoomedHd=b.zoomed.cloneNode(),b.zoomedHd.removeAttribute("sizes"),b.zoomedHd.removeAttribute("loading");var Fe=b.zoomedHd.addEventListener("load",function(){b.zoomedHd.removeEventListener("load",Fe),b.zoomedHd.classList.add("medium-zoom-image--opened"),b.zoomedHd.addEventListener("click",g),document.body.appendChild(b.zoomedHd),te()})}else te()})},g=function(){return new r(function(w){if(D||!b.original){w(L);return}var N=function te(){b.original.classList.remove("medium-zoom-image--hidden"),document.body.removeChild(b.zoomed),b.zoomedHd&&document.body.removeChild(b.zoomedHd),document.body.removeChild(G),b.zoomed.classList.remove("medium-zoom-image--opened"),b.template&&document.body.removeChild(b.template),D=!1,b.zoomed.removeEventListener("transitionend",te),b.original.dispatchEvent(cn("medium-zoom:closed",{detail:{zoom:L}})),b.original=null,b.zoomed=null,b.zoomedHd=null,b.template=null,w(L)};D=!0,document.body.classList.remove("medium-zoom--opened"),b.zoomed.style.transform="",b.zoomedHd&&(b.zoomedHd.style.transform=""),b.template&&(b.template.style.transition="opacity 150ms",b.template.style.opacity=0),b.original.dispatchEvent(cn("medium-zoom:close",{detail:{zoom:L}})),b.zoomed.addEventListener("transitionend",N)})},y=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},N=w.target;return b.original?g():m({target:N})},E=function(){return H},T=function(){return v},x=function(){return b.original},v=[],k=[],D=!1,O=0,H=n,b={original:null,zoomed:null,zoomedHd:null,template:null};Object.prototype.toString.call(t)==="[object Object]"?H=t:(t||typeof t=="string")&&c(t),H=Jt({margin:0,background:"#fff",scrollOffset:40,container:null,template:null},H);var G=pg(H.background);document.addEventListener("click",o),document.addEventListener("keyup",i),document.addEventListener("scroll",s),window.addEventListener("resize",g);var L={open:m,close:g,toggle:y,update:l,clone:a,attach:c,detach:u,on:f,off:d,getOptions:E,getImages:T,getZoomedImage:x};return L};function gg(e,t){t===void 0&&(t={});var n=t.insertAt;if(!(!e||typeof document>"u")){var r=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css",n==="top"&&r.firstChild?r.insertBefore(o,r.firstChild):r.appendChild(o),o.styleSheet?o.styleSheet.cssText=e:o.appendChild(document.createTextNode(e))}}var vg=".medium-zoom-overlay{position:fixed;top:0;right:0;bottom:0;left:0;opacity:0;transition:opacity .3s;will-change:opacity}.medium-zoom--opened .medium-zoom-overlay{cursor:pointer;cursor:zoom-out;opacity:1}.medium-zoom-image{cursor:pointer;cursor:zoom-in;transition:transform .3s cubic-bezier(.2,0,.2,1)!important}.medium-zoom-image--hidden{visibility:hidden}.medium-zoom-image--opened{position:relative;cursor:pointer;cursor:zoom-out;will-change:transform}";gg(vg);const bg=Symbol("mediumZoom");var _g={};const yg=":not(a) > img",wg=_g,kg=500,Eg=ct({enhance({app:e,router:t}){const n=mg(wg);n.refresh=(r=yg)=>{n.detach(),n.attach(r)},e.provide(bg,n),t.afterEach(()=>{fg(kg).then(()=>n.refresh())})}}),Cg={},Sg=ct({enhance:({app:e})=>{},setup:()=>{}}),br=[jd,Wd,rp,ap,dp,vp,Om,Ym,ug,Eg,Cg,Sg],xg=JSON.parse(`{"base":"/learning-kotlin/","lang":"en-US","title":"","description":"","head":[["link",{"rel":"icon","href":"/learning-kotlin/favicon.ico"}],["link",{"rel":"manifest","href":"/learning-kotlin/manifest.webmanifest"}],["meta",{"name":"theme-color","content":"#2176d6"}]],"locales":{"/en/":{"lang":"en-US","title":"Learning Kotlin","description":"Learning Kotlin"},"/fr/":{"lang":"fr-FR","title":"Apprendre Kotlin","description":"Une formation d'introduction à Kotlin"}}}`);var An=sn(xg),Lg=Tf,Ag=()=>{const e=ed({history:Lg(xl("/learning-kotlin/")),routes:[{name:"vuepress-route",path:"/:catchAll(.*)",components:{}}],scrollBehavior:(t,n,r)=>r||(t.hash?{el:t.hash}:{top:0})});return e.beforeResolve(async(t,n)=>{if(t.path!==n.path||n===_t){const r=Un(t.path);if(r.path!==t.path)return r.path;const o=await r.loader();t.meta={...r.meta,_pageChunk:o}}else t.path===n.path&&(t.meta=n.meta)}),e},Tg=e=>{e.component("ClientOnly",Zo),e.component("Content",ad),e.component("RouteLink",or)},Pg=(e,t,n)=>{const r=I(()=>t.currentRoute.value.path),o=Xa((E,T)=>({get(){return E(),t.currentRoute.value.meta._pageChunk},set(x){t.currentRoute.value.meta._pageChunk=x,T()}})),s=I(()=>Gt.resolveLayouts(n)),i=I(()=>Gt.resolveRouteLocale(An.value.locales,r.value)),l=I(()=>Gt.resolveSiteLocaleData(An.value,i.value)),a=I(()=>o.value.comp),c=I(()=>o.value.data),u=I(()=>c.value.frontmatter),f=I(()=>Gt.resolvePageHeadTitle(c.value,l.value)),d=I(()=>Gt.resolvePageHead(f.value,u.value,l.value)),m=I(()=>Gt.resolvePageLang(c.value,l.value)),g=I(()=>Gt.resolvePageLayout(c.value,s.value)),y={layouts:s,pageData:c,pageComponent:a,pageFrontmatter:u,pageHead:d,pageHeadTitle:f,pageLang:m,pageLayout:g,redirects:To,routeLocale:i,routePath:r,routes:Vn,siteData:An,siteLocaleData:l};return e.provide(Xo,y),Object.defineProperties(e.config.globalProperties,{$frontmatter:{get:()=>u.value},$head:{get:()=>d.value},$headTitle:{get:()=>f.value},$lang:{get:()=>m.value},$page:{get:()=>c.value},$routeLocale:{get:()=>i.value},$site:{get:()=>An.value},$siteLocale:{get:()=>l.value},$withBase:{get:()=>rr}}),y},Rg=()=>{const e=rd(),t=od();let n=[];const r=()=>{e.value.forEach(i=>{const l=Ig(i);l&&n.push(l)})},o=()=>{const i=[];return e.value.forEach(l=>{const a=Og(l);a&&i.push(a)}),i},s=()=>{document.documentElement.lang=t.value;const i=o();n.forEach((l,a)=>{const c=i.findIndex(u=>l.isEqualNode(u));c===-1?(l.remove(),delete n[a]):i.splice(c,1)}),i.forEach(l=>document.head.appendChild(l)),n=[...n.filter(l=>!!l),...i]};Nt(ld,s),He(()=>{r(),De(e,s,{immediate:!1})})},Ig=([e,t,n=""])=>{const r=Object.entries(t).map(([l,a])=>it(a)?`[${l}=${JSON.stringify(a)}]`:a===!0?`[${l}]`:"").join(""),o=`head > ${e}${r}`;return Array.from(document.querySelectorAll(o)).find(l=>l.innerText===n)||null},Og=([e,t,n])=>{if(!it(e))return null;const r=document.createElement(e);return Go(t)&&Object.entries(t).forEach(([o,s])=>{it(s)?r.setAttribute(o,s):s===!0&&r.setAttribute(o,"")}),it(n)&&r.appendChild(document.createTextNode(n)),r},$g=ju,Mg=async()=>{var n;const e=$g({name:"Vuepress",setup(){var s;Rg();for(const i of br)(s=i.setup)==null||s.call(i);const r=br.flatMap(({rootComponents:i=[]})=>i.map(l=>oe(l))),o=sd();return()=>[oe(o.value),r]}}),t=Ag();Tg(e),Pg(e,t,br);for(const r of br)await((n=r.enhance)==null?void 0:n.call(r,{app:e,router:t,siteData:An}));return e.use(t),{app:e,router:t}};Mg().then(({app:e,router:t})=>{t.isReady().then(()=>{e.mount("#app")})});export{Ce as _,ne as a,mt as b,Z as c,Mg as createVueApp,ie as d,eu as e,z as o,nn as r,xe as w}; diff --git a/assets/compose-multiplaform-Dfyu_rxB.gif b/assets/compose-multiplaform-Dfyu_rxB.gif new file mode 100644 index 00000000..ffa4079f Binary files /dev/null and b/assets/compose-multiplaform-Dfyu_rxB.gif differ diff --git a/assets/compose-multiplaform-web-wH6XfCHb.gif b/assets/compose-multiplaform-web-wH6XfCHb.gif new file mode 100644 index 00000000..b85ee793 Binary files /dev/null and b/assets/compose-multiplaform-web-wH6XfCHb.gif differ diff --git a/assets/hello-compose-demo-B4DIIuDy.gif b/assets/hello-compose-demo-B4DIIuDy.gif new file mode 100644 index 00000000..d4fe51b5 Binary files /dev/null and b/assets/hello-compose-demo-B4DIIuDy.gif differ diff --git a/assets/index-DTEEl-sV.js b/assets/index-DTEEl-sV.js new file mode 100644 index 00000000..69326545 --- /dev/null +++ b/assets/index-DTEEl-sV.js @@ -0,0 +1 @@ +var v=function(){return!!(window.location.hostname==="localhost"||window.location.hostname==="[::1]"||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/))},c;typeof window<"u"&&(typeof Promise<"u"?c=new Promise(function(t){return window.addEventListener("load",t)}):c={then:function(t){return window.addEventListener("load",t)}});function s(t,n){n===void 0&&(n={});var i=n.registrationOptions;i===void 0&&(i={}),delete n.registrationOptions;var e=function(r){for(var f=[],a=arguments.length-1;a-- >0;)f[a]=arguments[a+1];n&&n[r]&&n[r].apply(n,f)};"serviceWorker"in navigator&&c.then(function(){v()?(l(t,e,i),navigator.serviceWorker.ready.then(function(r){e("ready",r)}).catch(function(r){return o(e,r)})):(u(t,e,i),navigator.serviceWorker.ready.then(function(r){e("ready",r)}).catch(function(r){return o(e,r)}))})}function o(t,n){navigator.onLine||t("offline"),t("error",n)}function u(t,n,i){navigator.serviceWorker.register(t,i).then(function(e){if(n("registered",e),e.waiting){n("updated",e);return}e.onupdatefound=function(){n("updatefound",e);var r=e.installing;r.onstatechange=function(){r.state==="installed"&&(navigator.serviceWorker.controller?n("updated",e):n("cached",e))}}}).catch(function(e){return o(n,e)})}function l(t,n,i){fetch(t).then(function(e){e.status===404?(n("error",new Error("Service worker not found at "+t)),d()):e.headers.get("content-type").indexOf("javascript")===-1?(n("error",new Error("Expected "+t+" to have javascript content-type, but received "+e.headers.get("content-type"))),d()):u(t,n,i)}).catch(function(e){return o(n,e)})}function d(){"serviceWorker"in navigator&&navigator.serviceWorker.ready.then(function(t){t.unregister()}).catch(function(t){return o(emit,t)})}export{s as register,d as unregister}; diff --git a/assets/index.html-8yMvNPCQ.js b/assets/index.html-8yMvNPCQ.js new file mode 100644 index 00000000..f596b3df --- /dev/null +++ b/assets/index.html-8yMvNPCQ.js @@ -0,0 +1,7 @@ +import{_ as e,r as a,o as n,c as b,a as l,b as I,d as c,e as i}from"./app-jR2rC7Ae.js";const s={},t=i('

📚 Kotlin language features

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.

Basic features

Basic constructs (variables, control flow)

  • 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.

',6),o={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gRnVuY3Rpb25zIGNhbiBiZSBkZWNsYXJlZCBvdXRzaWRlIG9mIGNsYXNzZXNcbmZ1biBzdW0oeDpJbnQsIHk6SW50KSA6IEludCB7XG4gICAgcmV0dXJuIHggKyB5XG59IC8vIGZ1bmN0aW9uIGRlZmluaXRpb25cblxuXG52YWwgZ2xvYmFsVmFsdWUgPSBcIkkgYW0gYSBnbG9iYWwgdmFsdWVcIlxuXG5mdW4gbWFpbigpIHtcbiAgICB2YXIgYTogSW50ID0gMCAvLyB2YXJpYWJsZVxuXHRhID0gMjNcblxuXHR2YWwgYjogU3RyaW5nID0gXCJIZWxsb1wiIC8vIGltbXV0YWJsZSB2YXJpYWJsZVxuXHQvL2IgPSBcIldvcmxkXCIgLy9lcnJvclxuXG5cdHZhciBtZXNzYWdlID0gXCJIZWxsb1wiIC8vIFR5cGUgaXMgaW5mZXJyZWRcbiAgICBcbiAgICB2YWwgdG90YWwgPSBzdW0oMiwgMSkgLy8gY2FsbCBzdW0gZnVuY3Rpb24gICAgXG4gICAgcHJpbnRsbihcIlRoZSBzdW0gaXMgJHt0b3RhbH1cIikgLy8gaW50ZXJwb2xhdGlvblxuICAgIHByaW50bG4oXCJUaGUgZ2xvYmFsVmFsdWUgaXMgLT4gJGdsb2JhbFZhbHVlXCIpXG4gICAgXG4gICAgdmFsIHRlc3RSZXN1bHQxID0gaWYgKHRvdGFsID4gMykgeyBcbiAgICAgICAgXCJCaWdcIiBcbiAgICB9IGVsc2Uge1xuICAgICAgICBcInNtYWxsXCJcbiAgICB9ICBcbiAgICBwcmludGxuKHRlc3RSZXN1bHQxKVxuICBcbiAgICB2YWwgdGVzdFJlc3VsdDIgPSBpZiAodG90YWwgPiAzKSBcIkJpZ1wiIGVsc2UgXCJzbWFsbFwiXG4gICAgcHJpbnRsbih0ZXN0UmVzdWx0MilcbiAgICBcbiAgICB2YWwgZ3JlZXRpbmcgPSBcIkJvbmpvdXJcIlxuICAgIHZhbCBsYW5ndWFnZSA9IHdoZW4oZ3JlZXRpbmcpe1xuICAgICAgIFwiQm9uam91clwiLFwic2FsdXRcIiAtPiBcIkZyYW7Dp2Fpc1wiXG4gICAgICAgIFwiR29vZCBtb3JuaW5nXCIgLT4gXCJFbmdsaXNoXCJcbiAgICAgICAgZWxzZSAtPiBcIlVua25vd25cIlxuICAgIH1cbiAgICBwcmludGxuKGxhbmd1YWdlKVxuICAgIFxuICAgIGZvciAoaSBpbiAxLi4zKSBwcmludGxuKGkpIC8vIHNpbmdsZSBsaW5lXG5cdGZvciAoaSBpbiA2IGRvd25UbyAwIHN0ZXAgMikgcHJpbnRsbihpKVxuICAgIGZvciAobXNnIGluIGFycmF5T2YoXCJIZWxsb1wiLCBcIndvcmxkXCIpKSB7XG4gICAgXHRwcmludGxuKG1zZylcblx0fVxuICAgIHZhciB4ID0gMlxuICAgIHdoaWxlICh4ID4gMCkge1xuICAgIFx0eC0tXG5cdH1cbn0ifQ==",target:"_blank",rel:"noopener noreferrer"},d=i('

Functions

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 }
',4),G={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLy8gRnVuY3Rpb25zIGNhbiBiZSBkZWNsYXJlZCBvdXRzaWRlIG9mIGNsYXNzZXNcbmZ1biBzdW0oeDpJbnQsIHk6SW50KSA6IEludCB7XG4gICAgcmV0dXJuIHggKyB5XG59IC8vIGZ1bmN0aW9uIGRlZmluaXRpb25cblxuLy9PbmUgbGluZSBmdW5jdGlvblxuZnVuIG11bHQoeDpJbnQsIHk6IEludCkgPSB4ICogeVxuXG5mdW4gZ3JlZXQoeWVhcjogSW50ID0gMjAxOCwgbW9udGg6IEludCA9IDEsXG4gICAgICAgICAgbWVzc2FnZTogU3RyaW5nID0gXCJIZWxsb1wiKSA6IFN0cmluZyB7XG4gICAgcmV0dXJuIFwiJHttZXNzYWdlfS4gV2UgYXJlIGluICR7bW9udGh9LyR7eWVhcn1cIlxufVxuXG5mdW4gY2FsY3VsYXRlKHg6IEludCwgeTogSW50LCBmOiAoSW50LCBJbnQpIC0+IEludCkgOiBJbnR7XG4gICAgcmV0dXJuIGYoeCwgeSlcbn0gLy8gY2FsY3VsYXRlIHRha2VzIGEgZnVuY3Rpb24gYXMgYSBwYXJhbXRlclxuXG5mdW4gbWFpbihhcmd2OkFycmF5PFN0cmluZz4pe1xuICAgIHZhbCBhID0gc3VtKDIsIDEpIC8vIGNhbGwgc3VtIGZ1bmN0aW9uXG4gICAgcHJpbnRsbihtdWx0KGEsIHkgPSA4KSkgLy8gd2UgY2FuIG5hbWUgcGFyYW1ldGVyc1xuICAgIFxuICAgIHByaW50bG4oZ3JlZXQoMjAxNywgMTAsIFwiQm9uam91clwiKSkgLy9Cb25qb3VyLiBXZSBhcmUgaW4gMTAvMjAxN1xuICAgIHByaW50bG4oZ3JlZXQoKSkgLy9IZWxsby4gV2UgYXJlIGluIDEvMjAxOFxuICAgIHByaW50bG4oZ3JlZXQoMjAxOCwgbWVzc2FnZSA9IFwiS29ubmljaGl3YVwiKSlcbiAgICBcbiAgICBwcmludGxuKGNhbGN1bGF0ZSg5LCA1LCA6OnN1bSkpIC8vIDE0XG4gICAgLy8gZnVuY3Rpb25zIGNhbiBiZSBkZWNsYXJlZCBmcm9tIHdpdGhpbiBvdGhlciBmdW5jdGlvbnNcbiAgICB2YWwgZiA9IHt4OiBJbnQsIHk6IEludCAtPiB4IC0geX1cbiAgICBwcmludGxuKGNhbGN1bGF0ZSg5LCA1LCBmKSlcbiAgICBcbiAgICBmdW4gZGl2aWRlKHg6SW50LCB5OiBJbnQpID0geCAvIHlcbiAgICBwcmludGxuKGNhbGN1bGF0ZSg5LCA1LCA6OmRpdmlkZSkpXG4gICAgXG4gICAgcHJpbnRsbihjYWxjdWxhdGUoOSwgNSwgeyB4LCB5IC0+IHggKiB5IH0gKSkgLy8gNDVcbiAgICAvLyBJZiB0aGUgbGFzdCBwYXJhbWV0ZXIgaXMgZnVuY3Rpb24sIHdlIGNhbiBjbG9zZSAoKSBlcmxpZXJcbiAgICBwcmludGxuKGNhbGN1bGF0ZSg5LCA1KSB7IHgsIHkgLT4geCAqIHkgfSApIC8vIDQ1XG59In0=",target:"_blank",rel:"noopener noreferrer"},u=i('

The next section talk about null safety.

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.

',5),m={href:"https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJndjpBcnJheTxTdHJpbmc+KXtcbiAgICB2YXIgczogU3RyaW5nIC8vIHZhcnMgbXVzdCBiZSBpbml0aWFsaXplZCBiZWZvcmUgdXNpbmdcbiAgICAvLyBwcmludGxuKHMpIC0+IGNvbXBpbGUgZmFpbHNcbiAgICBzID0gXCJoZWxsb1wiXG4gICAgcHJpbnRsbihzKSAvLyBva1xuICAgIC8vIHMgPSBudWxsIC0+IGNvbXBpbGUgZmFpbHMuIFR5cGVzIGFyZSBub3QgbnVsbGFibGVcbiAgICB2YWwgbXNnIDogU3RyaW5nPyA9IG51bGwgLy8gbnVsbGFibGUgdHlwZXNcbiAgICBwcmludGxuKG1zZylcbiAgICAvLyBtc2cucmV2ZXJzZWQoKSAvLyBtc2cgbWF5IGJlIG51bGwgLT4gY29tcGlsZSBrb1xuICAgIHByaW50bG4obXNnPy5yZXZlcnNlZCgpPy5jYXBpdGFsaXplKCkpIC8vIHJldHVybiBudWxsIGlmIGFueSBwYXJ0IGlzIG51bGxcbiAgICAvL3ByaW50bG4obXNnISEucmV2ZXJzZWQoKSkgLy8gdGVsbCBjb21waWxlciB0byBpZ25vcmUgbnVsbCBjaGVja3MgLT4gTlBFIGlmIHRoZSB2YXIgaXMgbnVsbFxuICAgIFxuICAgIHZhciBuYjogSW50PyA9IDJcbiAgICAvLyBrb3RsaW4gY29tcGlsZXIga25vd24gdGhhdCBuYiBpcyBuZXZlciBudWxsIGluIGVsc2UgLT4gc21hcnQgY2FzdGluZ1xuICAgIHZhbCBkb3VibGUgPSBpZiggbmIgPT0gbnVsbCApIDAgZWxzZSBuYiAqIDJcbiAgICBwcmludGxuKGRvdWJsZSlcbiAgICBcbiAgICAvLyBzaG9ydGN1dCBvZiBwcmV2IGlmIHVzaW5nID86IGVsdmlzIG9wZXJhdG9yXG4gICAgdmFsIHRyaXBsZSA9IG5iPy50aW1lcygzKSA/OiAwIDsgcHJpbnRsbih0cmlwbGUpXG4gICAgLy9PdGhlciBzbWFydCBjYXN0aW5nIHVzZXNcbiAgICBpZiAobmIgaXMgSW50KSB7XG4gICAgICAgIHByaW50bG4obmIudGltZXMoMykpXG4gICAgfVxuICAgIFxuICAgIGlmIChuYiAhPSBudWxsKSB7XG4gICAgICAgIHByaW50bG4obmIudGltZXMoMykpXG4gICAgfVxuICAgIFxuICAgIC8vIHRoZSBhcmd1bWVudCBpcyB1bndyYXBwZWQgaW4gdGhlIGxhbWRhIG9mIGxldFxuICAgIG5iPy5sZXQgeyBpdC50aW1lcygzKSB9XG4gICAgXG4gICAgLy9zYWZlIGNhc3QgcmV0dXJuIG51bGxhYmxlIGFuZCBkb2VzIG5vdCB0aHJvdyBleGNlcHRpb25cbiAgICB2YWwgbmV3TmIgPSBuYiBhcz8gTG9uZ1xuICAgIHByaW50bG4obmV3TmIpXG4gICAgXG4gICAgLy8gQ29udmVydCBudWxsYWJsZSBsaXN0IHRvIG5vbiBudWxsYWJsZSBvbmVcbiAgICB2YWwgbnVsbGFibGVMaXN0OiBMaXN0PEludD8+ID0gbGlzdE9mKDEsIDIsIG51bGwsIDQpXG5cdHZhbCBpbnRMaXN0OiBMaXN0PEludD4gPSBudWxsYWJsZUxpc3QuZmlsdGVyTm90TnVsbCgpXG4gICAgcHJpbnRsbihpbnRMaXN0KVxufSJ9",target:"_blank",rel:"noopener noreferrer"},h=l("div",{class:"custom-container warning"},[l("p",{class:"custom-container-title"},"Java `Optional` does not provide compile time null checks"),l("p",null,[l("code",null,"Optional"),I(" 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: "),l("code",null,"Optional 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('

Intermediate features

Object oriented programming

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('

📅 Workshops

(2023) Android makers : Kotlin Beyond Android

am2023 logo
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('

(2024) MiXit

  • Titre : Développement front et back en Kotlin. Une visite guidée de KMP
qr code
qr code

Agenda

',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('

Prerequisites

  • 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)

Liens utiles

',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('

Concurrence et coroutines

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(`

Littéral de fonction avec récepteur et constructeurs de type sécurisé

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 // or this.length
+println("hello".countCharacters()) // prints 5
+

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 } // extFn is a function literal
+println("hello".extFn()) // prints 5
+println(extFn("hello")) // prints 5
+

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(`
// StirngBuilder uses the builder pattern
+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('

Exercises

Exercise 1

  1. open the java-integration-exercise projects in the materials folder.
  2. 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)
  3. 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)
  4. 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 et Kotlin/WASM

  • 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 😄.

🧪 Application web Kotlin/WASM

',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.

    🧪 Compose Web

    • 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
    compose multiplatform demo

    🧪 Compose desktop + Android app

    • 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
    compose multiplatform demo

    Pour aller plus loin

    ',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('

    Why switch from Java to Kotlin

    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('

    A decision tree to help you decide if you should use Kotlin

    decision tree
    decision tree

    Prerequisites

    ',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('

    Welcome

    Who we are

    avatar
    avatar

    We 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('

    Prerequisites

    • 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('

    Welcome

    Who we are

    avatar
    avatar

    We 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('

    Prerequisites

    • 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('

    Pourquoi passer de Java à Kotlin

    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('

    Concurrency and Coroutines

    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(`

    Function literal with receiver and Type-safe builders

    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 // or this.length
    +println("hello".countCharacters()) // prints 5
    +

    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 } // extFn is a function literal
    +println("hello".extFn()) // prints 5
    +println(extFn("hello")) // prints 5
    +

    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(`
    // StirngBuilder uses the builder pattern
    +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('

    🧪 Exercises

    Exercise 1

    1. open the java-integration-exercise projects in the materials folder.
    2. 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)
    3. 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)
    4. 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
    +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
    +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) { /* TODO: implement */ }
    +
    +    @PostMapping
    +    fun addOne(@RequestBody customer: Customer) { /* TODO: implement */ }
    +
    +    @DeleteMapping("{id}")
    +    fun deleteOne(@PathVariable id: String) { /* TODO: implement */ }
    +}
    +

    Models or model package ? plural or not ?

    Both are ok as long as you follow the same convention in the project.

    🧪 Spring boot part 2 - adding a database

    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()
    +
    +    // use findByIdOrNull instad of findById because the latter returns an optional<Product> instead of Product?
    +    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 boot part 2 - adding tests

    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).

    🧪 Getting started with Kotlin/JS and Express

    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()
    +
    +    // REST API that provides a **GET /hello** endpoint
    +    app.get("/hello") { _, res ->
    +        res.send(messages)
    +    }
    +
    +    // Create a server that listens to port 3000
    +    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

    🧪 Adding a post endpoint and an external Kotlin/JS definition

    ',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 means that this class is defined in JS
    +external class BodyParser {
    +    // we tell Kotlin that we want to use the json() function.
    +    fun json(): Any
    +    // It is not required to define all the functions of the module
    +}
    +
    +// @JsModule is used to import the module from the NPM registry
    +@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 ->
    +    // Kotlin does not keep the original field name when parsing JSON from JS (you can see it the in get response)
    +    if (req.body as? Message == null) {
    +        println("failed to get the body from Kotlin")
    +    }
    +    // Thus, we need to use js() to get the the field by its expected name
    +    // js() calls JS 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)
    +    }
    +}
    +

    🧪 Adding more endpoints

    • Add PUT and DELETE endpoints

    🎯 Solutions

    `,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(`

    Kotlin/JS and Kotlin/WASM common points

    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

    "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."

    img
    img
    Button(
    +  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 text
    Alt text

    🧪 Playing with the Compose multiplatform API

    Compose 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(){
    +    // Generate a list of random numbers
    +    val myRandomValues = List(5) { Random.nextInt(0, 30) }
    +    // LazyColumn is a vertically scrolling list that renders items on demand
    +    LazyColumn {
    +        items(myRandomValues.size){
    +            Text(text = "$it")
    +        }
    +    }
    +}
    +
    • Place this composable below AnimatedVisibility and Button and run the app.
    /*
    +Button(onClick ...
    +AnimatedVisibility(showImage) { ...
    +*/
    +RandomNumberList()
    +
    • Exercise: Make the "Hello, .." button switch between showing the list and and the image.
    Hello compose demo
    Hello compose demo

    🎯 Solutions

    ',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?}") {
    +      // TODO
    +    }
    +  }
    +}
    +
    +
    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) { /* TODO : implement */ }
    +
    +    @PostMapping
    +    fun addOne(@RequestBody customer : Customer) { /* TODO : implement */ }
    +
    +    @DeleteMapping("{id}")
    +    fun deleteOne(@PathVariable id : String) { /* TODO : implement */ }
    +}
    +

    TP : Spring boot partie 2 - ajouter une base de données

    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()
    +
    +    // use findByIdOrNull instad of findById because the latter returns an optional<Product> instead of Product?
    +    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

    TP : Spring boot partie 3 - ajouter des tests

    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.

    Projets terminé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('

    📅 Workshops

    Android makers 2023: Kotlin Beyond Android

    am2023 logo
    am2023 logo

    Liens

    ',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('

    (2024) MiXit

    • Titre : Développement front et back en Kotlin. Une visite guidée de KMP
    qr code
    qr code

    Agenda

    ',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('

    📚 Fonctionnalités du langage 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.

    Caractéristiques de base

    Constructions de base (variables, flux de contrôle)

    • 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('

    Les fonctions

    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

    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('

    Fonctionnalités intermédiaires

    Programmation orientée objet

    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 @@ +
    Shall I use Kotlin ?
    Shall I use Kotlin...
    Android
    Android
    Mobile (Android + iOS)
    Mobile (Android + iOS)
    Web
    Web
    Web + Mobile
    Web + Mobile
    What type
    of front project
    What type...
    Frontend
    Frontend
    Kotlin is the default Android Language since 2019
    Kotlin is the default...
    No
    No
    Yes
    Yes
    Do you want
    to generate native code
    Do you want...
    Java / JVM
    Java / JVM
    Other
    Other
    What type
    of project ?
    What type...
    Yes
    Yes
    Are you intereted in
    using a conise language
    with null safety and full
    interop with Java
    Are you intereted in...
    Yes
    Yes
    No
    No
    Is it a script
    or WASM or WASI
    project ?
    Is it a script...
    Use Kotlin. In addition to that, Quarkus and Spring officially support Kotlin
    Use Kotlin. In addition...
    Use Java or Groovy or Scala
    Use Java or Groo...
    Use Flutter or React native
    Use Flutter or R...
    Yes
    Yes
    No
    No
    Do you want to
    develop the UI using native frameworks (Jetpack compose
    and SwiftUI)
    Do you want to...
    Kotlin Multiplatform (KMP) allows to share no UI code
    Kotlin Multiplatfor...
    Yes
    Yes
    No
    No
    Do you to use
    native System
    views 
    Do you to use...
    Use MAUI
    Use MAUI
    Use KMP with Compose multiplatform
    Use KMP with Compose...
    Yes
    Yes
    No
    No
    Do you want to
    use a conise lnguage
    with null safety
    Do you want to...
    Use Kotlin. Howeber these features are still experimental
    Use Kotlin. Howe...
    Yes
    Yes
    No
    No
    Is it ok to use experimental technologies ?
    Is it ok to use expe...
    Use other languages
    Use other langua...
    JavaSctipt / TypeScript
    JavaSctipt / TypeScript
    Kotlin / Java
    Kotlin / Java
    C#
    C#
    Dart
    Dart
    Rust
    Rust
    Which
    language are
    you already
    confortable with
    Which...
    I want 
    I want 
    No
    No
    Do you want
    to share code
    across mobile
    and web
    Do you want...
    Use JS ot TS to develop the webapp
    Use JS ot TS to...
    Use Kotlin/JS
    Use Kotlin/JS
    Use Blazor
    Use Blazor
    User Flutter
    User Flutter
    Use Yew (or an alternative in Rust)
    Use Yew (or an a...
    Text is not SVG - cannot display
    \ 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 + + + + + +

    📚 Backend development

    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

    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.

    🧪 develop an API with Ktor

    • 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
    +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
    +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 framework

    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.

    🧪 Spring boot part 1 - develop the same API with Spring Boot

    • 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) { /* TODO: implement */ }
    +
    +    @PostMapping
    +    fun addOne(@RequestBody customer: Customer) { /* TODO: implement */ }
    +
    +    @DeleteMapping("{id}")
    +    fun deleteOne(@PathVariable id: String) { /* TODO: implement */ }
    +}
    +

    Models or model package ? plural or not ?

    Both are ok as long as you follow the same convention in the project.

    🧪 Spring boot part 2 - adding a database

    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()
    +
    +    // use findByIdOrNull instad of findById because the latter returns an optional<Product> instead of Product?
    +    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 boot part 2 - adding tests

    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.

    NodeJS

    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).

    🧪 Getting started with Kotlin/JS and Express

    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()
    +
    +    // REST API that provides a **GET /hello** endpoint
    +    app.get("/hello") { _, res ->
    +        res.send(messages)
    +    }
    +
    +    // Create a server that listens to port 3000
    +    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

    🧪 Adding a post endpoint and an external Kotlin/JS definition

    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 means that this class is defined in JS
    +external class BodyParser {
    +    // we tell Kotlin that we want to use the json() function.
    +    fun json(): Any
    +    // It is not required to define all the functions of the module
    +}
    +
    +// @JsModule is used to import the module from the NPM registry
    +@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 ->
    +    // Kotlin does not keep the original field name when parsing JSON from JS (you can see it the in get response)
    +    if (req.body as? Message == null) {
    +        println("failed to get the body from Kotlin")
    +    }
    +    // Thus, we need to use js() to get the the field by its expected name
    +    // js() calls JS 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)
    +    }
    +}
    +

    🧪 Adding more endpoints

    • Add PUT and DELETE endpoints

    🎯 Solutions

    📖 Further readings

    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 + + + + + +

    📚 Frontend development

    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:

    Kotlin supports cross platform frontend development thanks to Kotlin MultiPlatform (KMP)

    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.

    KMP
    KMP

    Many combinations of targets and use cases are possible:

    Kotlin/JS and Kotlin/WASM

    • 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 😄.

    🧪 Kotlin/WASM web app

    • 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
    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

    Kotlin/JS and Kotlin/WASM common points

    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

    "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."

    img
    img
    Button(
    +  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.

    🧪 Create a Compose multiplatform app

    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 Alt text. 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 text
    Alt text

    🧪 Playing with the Compose multiplatform API

    Compose 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(){
    +    // Generate a list of random numbers
    +    val myRandomValues = List(5) { Random.nextInt(0, 30) }
    +    // LazyColumn is a vertically scrolling list that renders items on demand
    +    LazyColumn {
    +        items(myRandomValues.size){
    +            Text(text = "$it")
    +        }
    +    }
    +}
    +
    • Place this composable below AnimatedVisibility and Button and run the app.
    /*
    +Button(onClick ...
    +AnimatedVisibility(showImage) { ...
    +*/
    +RandomNumberList()
    +
    • Exercise: Make the "Hello, .." button switch between showing the list and and the image.
    Hello compose demo
    Hello compose demo

    🎯 Solutions

    📖 Further reading

    + + + 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 + + + + + +
    Learning Kotlin

    Learning Kotlin

    A beginner's guide to a modern programming language

    Get started →

    Language features

    null safety, extensions, lambdas, Java interoperability and more

    Backend development

    With Ktor, spring and NodeJS

    Frontend development

    Compose multiplatform, Kotlin/JS, Kotlin/WASM and JVM frameworks

    Cross-platform development

    With KMP and Compose multiplatform

    Advanced Kotlin

    Coroutines, delegates, Function literal with receiver, DSLs and more

    Practical exercises and solutions

    All chapters have a set of exercises

    Welcome

    Who we are

    avatar
    avatar

    We 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

    TIP

    This training is also available in Frenchopen in new window / Cette formation est aussi disponible en Françaisopen in new window

    Prerequisites

    • Basic knowledge of object-oriented language like Java
    • Prepare your development environment and install stuff before the session (see Tooling section)
    + + + 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 + + + + + +

    📚 Advanced and other Kotlin features

    Delegated properties

    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.

    Concurrency and Coroutines

    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.

    Function literal with receiver and Type-safe builders

    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 // or this.length
    +println("hello".countCharacters()) // prints 5
    +

    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 } // extFn is a function literal
    +println("hello".extFn()) // prints 5
    +println(extFn("hello")) // prints 5
    +

    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.

    // StirngBuilder uses the builder pattern
    +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.

    🧪 Exercises

    Exercise 1

    1. open the java-integration-exercise projects in the materials folder.
    2. 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)
    3. 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)
    4. 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 language features

    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.

    Basic features

    Basic constructs (variables, control flow)

    • 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.

    Functions

    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.

    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

    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.

    🧪 Exercises

    Exercise 1

    Please click on this link to view the exerciseopen in new window

    Please open to see the solution(s)

    Solutionopen in new window

    Exercise 2

    Please click on this link to view the exerciseopen in new window

    Please open to see the solution(s)

    Solutionopen in new window

    Intermediate features

    Object oriented programming

    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 class

    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

    General concepts

    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 and functional programming

    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

    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 and Java interoperability

    • 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

    🧪 Exercises

    Exercise 3

    Please click on this link to view the exerciseopen in new window

    Please open to see the solution(s)

    Solutionopen in new window

    Exercise 4

    Please click on this link to view the exerciseopen in new window

    Please open to see the solution(s)

    Solutionopen in new window

    Exercise 5

    Please click on this link to view the exerciseopen in new window

    Please open to see the solution(s)

    Solutionopen in new window

    Exercise 6

    Please click on this link to view the exerciseopen in new window

    Please open to see the solution(s)

    Solutionopen in new window

    Exercise 7

    Please click on this link to view the exerciseopen in new window

    Please open to see the solution(s)

    Solutionopen in new window

    📖 Further reading

    + + + 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 + + + + + +

    🛠 Let's make a cross-plaform app !

    By combining KMP and Compose, it is possible to fully develop cross-platform mobile, web and desktop apps using only Kotlin.

    Prerequisites

    • Basic knowledge of kotlin development (nullability,inline & lambda functions mainly)
    • Android Studio IDEopen in new window with latest stable version Giraffe version or above
    • A good connectivity

    TIP

    For more information about your DEV environment and installs please have a look to jetbrain related docsopen in new window

    _____ 🚀 Start the practical work here 🚀_____open in new window

    kmp codelab
    kmp codelab
    + + + 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 + + + + + +

    🚀 Presentation of Kotlin

    Kotlin is a modern programming language developed by JetBrains.

    Some features

    • 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.

    History

    • 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.

    Some numbers and facts

    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?
    kotlin used for what?

    Please find more statics here:

    Why switch from Java to Kotlin

    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:

    A decision tree to help you decide if you should use Kotlin

    decision tree
    decision tree

    Prerequisites

    📖 Further reading

    + + + 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 + + + + + +

    📅 Workshops

    (2023) Android makers : Kotlin Beyond Android

    am2023 logo
    am2023 logo

    Workshop link: awl.li/am23-ktopen in new window

    qr code
    qr code

    Agenda

    (2023) JNation : Let's discover the possibilities of Kotlin

    Workshop link: awl.li/jnation23-ktopen in new window

    qr code
    qr code

    Agenda

    (2023) Mobile DevOps summit

    qr code
    qr code

    Agenda

    (2024) MiXit

    • Titre : Développement front et back en Kotlin. Une visite guidée de KMP
    qr code
    qr code

    Agenda

    + + + 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 + + + + + +

    📚 Développement du backend

    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

    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.

    TP : développer une API avec 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?}") {
    +      // TODO
    +    }
    +  }
    +}
    +
    +
    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

    nodejs

    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.

    TP : API Rest avec Kotlin/JS et Express

    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 framework

    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.

    TP : Spring boot part 1 - développer la même API avec Spring Boot

    • 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) { /* TODO : implement */ }
    +
    +    @PostMapping
    +    fun addOne(@RequestBody customer : Customer) { /* TODO : implement */ }
    +
    +    @DeleteMapping("{id}")
    +    fun deleteOne(@PathVariable id : String) { /* TODO : implement */ }
    +}
    +

    TP : Spring boot partie 2 - ajouter une base de données

    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()
    +
    +    // use findByIdOrNull instad of findById because the latter returns an optional<Product> instead of Product?
    +    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

    TP : Spring boot partie 3 - ajouter des tests

    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.

    Projets terminés

    Aller plus loin

    Ces tutoriels officiels vont encore plus loin :

    Lien et références

    + + + 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 + + + + + +

    📚 Développement frontend

    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 :

    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

    • 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
    KMP

    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 et Kotlin/WASM

    • 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 😄.

    🧪 Application web Kotlin/WASM

    • 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

    🧪 Application web KotlinJS

    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

    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.

    🧪 Compose Web

    • 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
    compose multiplatform demo

    🧪 Compose desktop + Android app

    • 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
    compose multiplatform demo

    Pour aller plus loin

    + + + 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 + + + + + +
    Apprendre Kotlin

    Apprendre Kotlin

    A beginner's guide to a modern programming language

    Débuter →

    Bien commencer avec Kotlin

    lorem

    Syntaxe de base

    lorem

    Exemples

    lorem

    TIP

    This training is also available in Englishopen in new window / Cette formation est aussi disponible en Anglaisopen in new window

    Prerequisites

    • 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)

    Liens utiles

    + + + 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 + + + + + +

    📚 Fonctionnalités avancées de Kotlin

    Propriétés déléguées

    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.

    Concurrence et coroutines

    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.

    Littéral de fonction avec récepteur et constructeurs de type sécurisé

    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 // or this.length
    +println("hello".countCharacters()) // prints 5
    +

    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 } // extFn is a function literal
    +println("hello".extFn()) // prints 5
    +println(extFn("hello")) // prints 5
    +

    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).

    Le pattern monteur (Builder)

    Est une technique permettant de construire des objects avec une syntaxe éléganteopen in new window.

    // StirngBuilder uses the builder pattern
    +val text = StringBuilder("Temp")
    +  .append(1)
    +  .append(true)
    +  .append("friend")
    +  .toString()
    +

    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.

    Exercises

    Exercise 1

    1. open the java-integration-exercise projects in the materials folder.
    2. 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)
    3. 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)
    4. 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 + + + + + +

    📚 Fonctionnalités du langage 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.

    Caractéristiques de base

    Constructions de base (variables, flux de contrôle)

    • 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.

    Les fonctions

    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

    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.

    Énumérations

    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.

    Exercices

    Exercice 1

    Veuillez cliquer sur le lien pour consulter l'énoncéopen in new window

    Déplier pour consulter la solution

    Solutionopen in new window

    Exercise 2

    Veuillez cliquer sur le lien pour consulter l'énoncéopen in new window

    Déplier pour consulter la solution

    Solutionopen in new window

    Fonctionnalités intermédiaires

    Programmation orientée objet

    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.

    Data class

    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.

    Programmation fonctionnelle

    Concepts généraux

    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 et programmation fonctionnelle

    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.

    Programmation déclarative

    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 and Java interoperability

    • 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

    Exercices

    Exercice 3

    Voir l'exerciceopen in new window

    Solution(s)

    Solutionopen in new window

    Exercice 4

    Exerciceopen in new window

    Solutions(s)

    Solutionopen in new window

    Exercice 5

    Exerciceopen in new window

    Solution(s)

    Solutionopen in new window

    Exercice 6

    Exerciceopen in new window

    solution(s)

    Solutionopen in new window

    Exercice 7

    Exerciceopen in new window

    Solution(s)

    Solutionopen in new window

    Plus d'exercices et de lecture

    + + + 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 + + + + + +

    🛠 Construisons une app multiplateforme !

    En combinant KMP, KMM et Compose, il est possible de développer des applications mobiles et de bureau multiplateformes en utilisant uniquement Kotlin.

    Prérequis

    • Connaissance de base du développement en Kotlin (notamment la nullabilité, les fonctions en ligne et les fonctions lambda)
    • IDE Android Studioopen in new window avec la version stable la plus récente, version Giraffe ou supérieure
    • Une bonne connectivité

    TIP

    Pour plus d'informations sur votre environnement de développement (DEV) et les installations, veuillez consulter la documentation liée à JetBrains iciopen in new window.

    ____ 🚀 Demarrer la session pratique ici 🚀___open in new window

    kmp codelabopen in new window
    kmp codelab
    + + + 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 + + + + + +

    🚀 Présentation de Kotlin

    Kotlin est un langage de programmation moderne développé par JetBrains.

    Certaines fonctionnalités

    • 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.

    Histoire

    • 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.

    Quelques chiffres et faits

    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 ?
    kotlin utilisé pour quoi ?

    Veuillez trouver plus de statiques ici :

    Pourquoi passer de Java à Kotlin

    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 :

    Sources et plus de lecture

    + + + 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 + + + + + +

    📅 Workshops

    Android makers 2023: Kotlin Beyond Android

    am2023 logo
    am2023 logo

    Liens

    Lien du workshop: awl.li/am23-ktopen in new window

    qr code
    qr code

    Agenda

    Mobile DevOps summit 2023

    qr code
    qr code

    Agenda

    Devoxx Morocco 2023

    qr code
    qr code

    Agenda

    (2024) MiXit

    • Titre : Développement front et back en Kotlin. Une visite guidée de KMP
    qr code
    qr code

    Agenda

    + + + 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 + + + + + +
    Hello

    Hello

    A beginner's guide to a modern programming language

    Get started →

    Language features

    null safety, extensions, lambdas, Java interoperability and more

    Backend development

    With Ktor, spring and NodeJS

    Frontend development

    Compose multiplatform, Kotlin/JS, Kotlin/WASM and JVM frameworks

    Cross-platform development

    With KMP and Compose multiplatform

    Advanced Kotlin

    Coroutines, delegates, Function literal with receiver, DSLs and more

    Practical exercises and solutions

    All chapters have a set of exercises

    Welcome

    Who we are

    avatar
    avatar

    We 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

    TIP

    This training is also available in Frenchopen in new window / Cette formation est aussi disponible en Françaisopen in new window

    Prerequisites

    • Basic knowledge of object-oriented language like Java
    • Prepare your development environment and install stuff before the session (see Tooling section)
    + + + 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)}}));