├── .editorconfig ├── .gitignore ├── LICENSE.txt ├── README.md ├── README.pdf └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Custom files 2 | *.html 3 | *.yaml 4 | .npmrc 5 | ## -------------------- 6 | 7 | # Created by https://www.gitignore.io/api/java,gradle,windows,intellij+all,visualstudiocode 8 | # Edit at https://www.gitignore.io/?templates=java,gradle,windows,intellij+all,visualstudiocode 9 | 10 | ### Intellij+all ### 11 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 12 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 13 | 14 | # User-specific stuff 15 | .idea/**/workspace.xml 16 | .idea/**/tasks.xml 17 | .idea/**/usage.statistics.xml 18 | .idea/**/dictionaries 19 | .idea/**/shelf 20 | 21 | # Generated files 22 | .idea/**/contentModel.xml 23 | 24 | # Sensitive or high-churn files 25 | .idea/**/dataSources/ 26 | .idea/**/dataSources.ids 27 | .idea/**/dataSources.local.xml 28 | .idea/**/sqlDataSources.xml 29 | .idea/**/dynamic.xml 30 | .idea/**/uiDesigner.xml 31 | .idea/**/dbnavigator.xml 32 | 33 | # Gradle 34 | .idea/**/gradle.xml 35 | .idea/**/libraries 36 | 37 | # Gradle and Maven with auto-import 38 | # When using Gradle or Maven with auto-import, you should exclude module files, 39 | # since they will be recreated, and may cause churn. Uncomment if using 40 | # auto-import. 41 | # .idea/modules.xml 42 | # .idea/*.iml 43 | # .idea/modules 44 | # *.iml 45 | # *.ipr 46 | 47 | # CMake 48 | cmake-build-*/ 49 | 50 | # Mongo Explorer plugin 51 | .idea/**/mongoSettings.xml 52 | 53 | # File-based project format 54 | *.iws 55 | 56 | # IntelliJ 57 | out/ 58 | 59 | # mpeltonen/sbt-idea plugin 60 | .idea_modules/ 61 | 62 | # JIRA plugin 63 | atlassian-ide-plugin.xml 64 | 65 | # Cursive Clojure plugin 66 | .idea/replstate.xml 67 | 68 | # Crashlytics plugin (for Android Studio and IntelliJ) 69 | com_crashlytics_export_strings.xml 70 | crashlytics.properties 71 | crashlytics-build.properties 72 | fabric.properties 73 | 74 | # Editor-based Rest Client 75 | .idea/httpRequests 76 | 77 | # Android studio 3.1+ serialized cache file 78 | .idea/caches/build_file_checksums.ser 79 | 80 | ### Intellij+all Patch ### 81 | # Ignores the whole .idea folder and all .iml files 82 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 83 | 84 | .idea/ 85 | 86 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 87 | 88 | *.iml 89 | modules.xml 90 | .idea/misc.xml 91 | *.ipr 92 | 93 | # Sonarlint plugin 94 | .idea/sonarlint 95 | 96 | ### Java ### 97 | # Compiled class file 98 | *.class 99 | 100 | # Log file 101 | *.log 102 | 103 | # BlueJ files 104 | *.ctxt 105 | 106 | # Mobile Tools for Java (J2ME) 107 | .mtj.tmp/ 108 | 109 | # Package Files # 110 | *.jar 111 | *.war 112 | *.nar 113 | *.ear 114 | *.zip 115 | *.tar.gz 116 | *.rar 117 | 118 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 119 | hs_err_pid* 120 | 121 | ### VisualStudioCode ### 122 | .vscode/* 123 | !.vscode/settings.json 124 | !.vscode/tasks.json 125 | !.vscode/launch.json 126 | !.vscode/extensions.json 127 | 128 | ### VisualStudioCode Patch ### 129 | # Ignore all local history of files 130 | .history 131 | 132 | ### Windows ### 133 | # Windows thumbnail cache files 134 | Thumbs.db 135 | Thumbs.db:encryptable 136 | ehthumbs.db 137 | ehthumbs_vista.db 138 | 139 | # Dump file 140 | *.stackdump 141 | 142 | # Folder config file 143 | [Dd]esktop.ini 144 | 145 | # Recycle Bin used on file shares 146 | $RECYCLE.BIN/ 147 | 148 | # Windows Installer files 149 | *.cab 150 | *.msi 151 | *.msix 152 | *.msm 153 | *.msp 154 | 155 | # Windows shortcuts 156 | *.lnk 157 | 158 | ### Gradle ### 159 | .gradle 160 | build/ 161 | 162 | # Ignore Gradle GUI config 163 | gradle-app.setting 164 | 165 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 166 | !gradle-wrapper.jar 167 | 168 | # Cache of project 169 | .gradletasknamecache 170 | 171 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 172 | # gradle/wrapper/gradle-wrapper.properties 173 | 174 | ### Gradle Patch ### 175 | **/build/ 176 | 177 | # End of https://www.gitignore.io/api/java,gradle,windows,intellij+all,visualstudiocode 178 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Attribution-ShareAlike 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-ShareAlike 4.0 International Public 58 | License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-ShareAlike 4.0 International Public License ("Public 63 | License"). To the extent this Public License may be interpreted as a 64 | contract, You are granted the Licensed Rights in consideration of Your 65 | acceptance of these terms and conditions, and the Licensor grants You 66 | such rights in consideration of benefits the Licensor receives from 67 | making the Licensed Material available under these terms and 68 | conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Adapter's License means the license You apply to Your Copyright 84 | and Similar Rights in Your contributions to Adapted Material in 85 | accordance with the terms and conditions of this Public License. 86 | 87 | c. BY-SA Compatible License means a license listed at 88 | creativecommons.org/compatiblelicenses, approved by Creative 89 | Commons as essentially the equivalent of this Public License. 90 | 91 | d. Copyright and Similar Rights means copyright and/or similar rights 92 | closely related to copyright including, without limitation, 93 | performance, broadcast, sound recording, and Sui Generis Database 94 | Rights, without regard to how the rights are labeled or 95 | categorized. For purposes of this Public License, the rights 96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 | Rights. 98 | 99 | e. Effective Technological Measures means those measures that, in the 100 | absence of proper authority, may not be circumvented under laws 101 | fulfilling obligations under Article 11 of the WIPO Copyright 102 | Treaty adopted on December 20, 1996, and/or similar international 103 | agreements. 104 | 105 | f. Exceptions and Limitations means fair use, fair dealing, and/or 106 | any other exception or limitation to Copyright and Similar Rights 107 | that applies to Your use of the Licensed Material. 108 | 109 | g. License Elements means the license attributes listed in the name 110 | of a Creative Commons Public License. The License Elements of this 111 | Public License are Attribution and ShareAlike. 112 | 113 | h. Licensed Material means the artistic or literary work, database, 114 | or other material to which the Licensor applied this Public 115 | License. 116 | 117 | i. Licensed Rights means the rights granted to You subject to the 118 | terms and conditions of this Public License, which are limited to 119 | all Copyright and Similar Rights that apply to Your use of the 120 | Licensed Material and that the Licensor has authority to license. 121 | 122 | j. Licensor means the individual(s) or entity(ies) granting rights 123 | under this Public License. 124 | 125 | k. Share means to provide material to the public by any means or 126 | process that requires permission under the Licensed Rights, such 127 | as reproduction, public display, public performance, distribution, 128 | dissemination, communication, or importation, and to make material 129 | available to the public including in ways that members of the 130 | public may access the material from a place and at a time 131 | individually chosen by them. 132 | 133 | l. Sui Generis Database Rights means rights other than copyright 134 | resulting from Directive 96/9/EC of the European Parliament and of 135 | the Council of 11 March 1996 on the legal protection of databases, 136 | as amended and/or succeeded, as well as other essentially 137 | equivalent rights anywhere in the world. 138 | 139 | m. You means the individual or entity exercising the Licensed Rights 140 | under this Public License. Your has a corresponding meaning. 141 | 142 | 143 | Section 2 -- Scope. 144 | 145 | a. License grant. 146 | 147 | 1. Subject to the terms and conditions of this Public License, 148 | the Licensor hereby grants You a worldwide, royalty-free, 149 | non-sublicensable, non-exclusive, irrevocable license to 150 | exercise the Licensed Rights in the Licensed Material to: 151 | 152 | a. reproduce and Share the Licensed Material, in whole or 153 | in part; and 154 | 155 | b. produce, reproduce, and Share Adapted Material. 156 | 157 | 2. Exceptions and Limitations. For the avoidance of doubt, where 158 | Exceptions and Limitations apply to Your use, this Public 159 | License does not apply, and You do not need to comply with 160 | its terms and conditions. 161 | 162 | 3. Term. The term of this Public License is specified in Section 163 | 6(a). 164 | 165 | 4. Media and formats; technical modifications allowed. The 166 | Licensor authorizes You to exercise the Licensed Rights in 167 | all media and formats whether now known or hereafter created, 168 | and to make technical modifications necessary to do so. The 169 | Licensor waives and/or agrees not to assert any right or 170 | authority to forbid You from making technical modifications 171 | necessary to exercise the Licensed Rights, including 172 | technical modifications necessary to circumvent Effective 173 | Technological Measures. For purposes of this Public License, 174 | simply making modifications authorized by this Section 2(a) 175 | (4) never produces Adapted Material. 176 | 177 | 5. Downstream recipients. 178 | 179 | a. Offer from the Licensor -- Licensed Material. Every 180 | recipient of the Licensed Material automatically 181 | receives an offer from the Licensor to exercise the 182 | Licensed Rights under the terms and conditions of this 183 | Public License. 184 | 185 | b. Additional offer from the Licensor -- Adapted Material. 186 | Every recipient of Adapted Material from You 187 | automatically receives an offer from the Licensor to 188 | exercise the Licensed Rights in the Adapted Material 189 | under the conditions of the Adapter's License You apply. 190 | 191 | c. No downstream restrictions. You may not offer or impose 192 | any additional or different terms or conditions on, or 193 | apply any Effective Technological Measures to, the 194 | Licensed Material if doing so restricts exercise of the 195 | Licensed Rights by any recipient of the Licensed 196 | Material. 197 | 198 | 6. No endorsement. Nothing in this Public License constitutes or 199 | may be construed as permission to assert or imply that You 200 | are, or that Your use of the Licensed Material is, connected 201 | with, or sponsored, endorsed, or granted official status by, 202 | the Licensor or others designated to receive attribution as 203 | provided in Section 3(a)(1)(A)(i). 204 | 205 | b. Other rights. 206 | 207 | 1. Moral rights, such as the right of integrity, are not 208 | licensed under this Public License, nor are publicity, 209 | privacy, and/or other similar personality rights; however, to 210 | the extent possible, the Licensor waives and/or agrees not to 211 | assert any such rights held by the Licensor to the limited 212 | extent necessary to allow You to exercise the Licensed 213 | Rights, but not otherwise. 214 | 215 | 2. Patent and trademark rights are not licensed under this 216 | Public License. 217 | 218 | 3. To the extent possible, the Licensor waives any right to 219 | collect royalties from You for the exercise of the Licensed 220 | Rights, whether directly or through a collecting society 221 | under any voluntary or waivable statutory or compulsory 222 | licensing scheme. In all other cases the Licensor expressly 223 | reserves any right to collect such royalties. 224 | 225 | 226 | Section 3 -- License Conditions. 227 | 228 | Your exercise of the Licensed Rights is expressly made subject to the 229 | following conditions. 230 | 231 | a. Attribution. 232 | 233 | 1. If You Share the Licensed Material (including in modified 234 | form), You must: 235 | 236 | a. retain the following if it is supplied by the Licensor 237 | with the Licensed Material: 238 | 239 | i. identification of the creator(s) of the Licensed 240 | Material and any others designated to receive 241 | attribution, in any reasonable manner requested by 242 | the Licensor (including by pseudonym if 243 | designated); 244 | 245 | ii. a copyright notice; 246 | 247 | iii. a notice that refers to this Public License; 248 | 249 | iv. a notice that refers to the disclaimer of 250 | warranties; 251 | 252 | v. a URI or hyperlink to the Licensed Material to the 253 | extent reasonably practicable; 254 | 255 | b. indicate if You modified the Licensed Material and 256 | retain an indication of any previous modifications; and 257 | 258 | c. indicate the Licensed Material is licensed under this 259 | Public License, and include the text of, or the URI or 260 | hyperlink to, this Public License. 261 | 262 | 2. You may satisfy the conditions in Section 3(a)(1) in any 263 | reasonable manner based on the medium, means, and context in 264 | which You Share the Licensed Material. For example, it may be 265 | reasonable to satisfy the conditions by providing a URI or 266 | hyperlink to a resource that includes the required 267 | information. 268 | 269 | 3. If requested by the Licensor, You must remove any of the 270 | information required by Section 3(a)(1)(A) to the extent 271 | reasonably practicable. 272 | 273 | b. ShareAlike. 274 | 275 | In addition to the conditions in Section 3(a), if You Share 276 | Adapted Material You produce, the following conditions also apply. 277 | 278 | 1. The Adapter's License You apply must be a Creative Commons 279 | license with the same License Elements, this version or 280 | later, or a BY-SA Compatible License. 281 | 282 | 2. You must include the text of, or the URI or hyperlink to, the 283 | Adapter's License You apply. You may satisfy this condition 284 | in any reasonable manner based on the medium, means, and 285 | context in which You Share Adapted Material. 286 | 287 | 3. You may not offer or impose any additional or different terms 288 | or conditions on, or apply any Effective Technological 289 | Measures to, Adapted Material that restrict exercise of the 290 | rights granted under the Adapter's License You apply. 291 | 292 | 293 | Section 4 -- Sui Generis Database Rights. 294 | 295 | Where the Licensed Rights include Sui Generis Database Rights that 296 | apply to Your use of the Licensed Material: 297 | 298 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 299 | to extract, reuse, reproduce, and Share all or a substantial 300 | portion of the contents of the database; 301 | 302 | b. if You include all or a substantial portion of the database 303 | contents in a database in which You have Sui Generis Database 304 | Rights, then the database in which You have Sui Generis Database 305 | Rights (but not its individual contents) is Adapted Material, 306 | 307 | including for purposes of Section 3(b); and 308 | c. You must comply with the conditions in Section 3(a) if You Share 309 | all or a substantial portion of the contents of the database. 310 | 311 | For the avoidance of doubt, this Section 4 supplements and does not 312 | replace Your obligations under this Public License where the Licensed 313 | Rights include other Copyright and Similar Rights. 314 | 315 | 316 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 317 | 318 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 319 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 320 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 321 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 322 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 323 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 324 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 325 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 326 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 327 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 328 | 329 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 330 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 331 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 332 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 333 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 334 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 335 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 336 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 337 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 338 | 339 | c. The disclaimer of warranties and limitation of liability provided 340 | above shall be interpreted in a manner that, to the extent 341 | possible, most closely approximates an absolute disclaimer and 342 | waiver of all liability. 343 | 344 | 345 | Section 6 -- Term and Termination. 346 | 347 | a. This Public License applies for the term of the Copyright and 348 | Similar Rights licensed here. However, if You fail to comply with 349 | this Public License, then Your rights under this Public License 350 | terminate automatically. 351 | 352 | b. Where Your right to use the Licensed Material has terminated under 353 | Section 6(a), it reinstates: 354 | 355 | 1. automatically as of the date the violation is cured, provided 356 | it is cured within 30 days of Your discovery of the 357 | violation; or 358 | 359 | 2. upon express reinstatement by the Licensor. 360 | 361 | For the avoidance of doubt, this Section 6(b) does not affect any 362 | right the Licensor may have to seek remedies for Your violations 363 | of this Public License. 364 | 365 | c. For the avoidance of doubt, the Licensor may also offer the 366 | Licensed Material under separate terms or conditions or stop 367 | distributing the Licensed Material at any time; however, doing so 368 | will not terminate this Public License. 369 | 370 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 371 | License. 372 | 373 | 374 | Section 7 -- Other Terms and Conditions. 375 | 376 | a. The Licensor shall not be bound by any additional or different 377 | terms or conditions communicated by You unless expressly agreed. 378 | 379 | b. Any arrangements, understandings, or agreements regarding the 380 | Licensed Material not stated herein are separate from and 381 | independent of the terms and conditions of this Public License. 382 | 383 | 384 | Section 8 -- Interpretation. 385 | 386 | a. For the avoidance of doubt, this Public License does not, and 387 | shall not be interpreted to, reduce, limit, restrict, or impose 388 | conditions on any use of the Licensed Material that could lawfully 389 | be made without permission under this Public License. 390 | 391 | b. To the extent possible, if any provision of this Public License is 392 | deemed unenforceable, it shall be automatically reformed to the 393 | minimum extent necessary to make it enforceable. If the provision 394 | cannot be reformed, it shall be severed from this Public License 395 | without affecting the enforceability of the remaining terms and 396 | conditions. 397 | 398 | c. No term or condition of this Public License will be waived and no 399 | failure to comply consented to unless expressly agreed to by the 400 | Licensor. 401 | 402 | d. Nothing in this Public License constitutes or may be interpreted 403 | as a limitation upon, or waiver of, any privileges and immunities 404 | that apply to the Licensor or You, including from the legal 405 | processes of any jurisdiction or authority. 406 | 407 | 408 | ======================================================================= 409 | 410 | Creative Commons is not a party to its public licenses. 411 | Notwithstanding, Creative Commons may elect to apply one of its public 412 | licenses to material it publishes and in those instances will be 413 | considered the “Licensor.” The text of the Creative Commons public 414 | licenses is dedicated to the public domain under the CC0 Public Domain 415 | Dedication. Except for the limited purpose of indicating that material 416 | is shared under a Creative Commons public license or as otherwise 417 | permitted by the Creative Commons policies published at 418 | creativecommons.org/policies, Creative Commons does not authorize the 419 | use of the trademark "Creative Commons" or any other trademark or logo 420 | of Creative Commons without its prior written consent including, 421 | without limitation, in connection with any unauthorized modifications 422 | to any of its public licenses or any other arrangements, 423 | understandings, or agreements concerning use of licensed material. For 424 | the avoidance of doubt, this paragraph does not form part of the public 425 | licenses. 426 | 427 | Creative Commons may be contacted at creativecommons.org. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kotlin 2 | 3 | Kotlin es un lenguaje de programación de código abierto de tipo estático que se dirige a JVM, Android, JavaScript y Native. Está desarrollado por JetBrains. El proyecto se inició en 2010 y fue de código abierto desde muy temprano. El primer lanzamiento oficial 1.0 fue en febrero de 2016. 4 | 5 | ## Características 6 | 7 | Un lenguaje de programación se diseña generalmente con un propósito específico en mente. Este propósito puede ser cualquier cosa, desde servir a un entorno específico (por ejemplo, la web) hasta un determinado paradigma (por ejemplo, la programación funcional). En el caso de **Kotlin** el objetivo es construir un lenguaje productivo y pragmático, que tenga todas las características que un desarrollador necesita y que sea fácil de usar. 8 | 9 | Kotlin fue diseñado inicialmente para trabajar con otros lenguajes de JVM, pero ahora ha evolucionado para ser mucho más: también funciona en el navegador y como una aplicación nativa. 10 | 11 | Kotlin es multiparadigma, con soporte para paradigmas de programación orientada a objetos, procedimentales y funcionales, sin forzar el uso de ninguno de ellos. Por ejemplo, a diferencia de Java, puede definir funciones de nivel superior, sin tener que declararlas dentro de una clase. 12 | 13 | - 100% interoperable con Java. 14 | - 100% compatible con Java 6, por lo que puede crear aplicaciones para la mayoría de los dispositivos Android. 15 | - Se ejecuta en la JVM, puede transpilarse (traducirse) a JavaScript e incluso puede ejecutarse de forma nativa, con interoperabilidad con las bibliotecas C y Objective-C (macOs e iOS). 16 | - No hay necesidad de terminar las frases con punto y coma `;`. Los bloques de código están delimitados por corchetes `{ }`. 17 | - Soporte de primera clase para valores constantes y colecciones inmutables (ideal para programación paralela y funcional). 18 | - Las funciones pueden ser elementos de nivel superior (es decir, no hay necesidad de poner todo dentro de una clase). 19 | - Las funciones son ciudadanos de primera clase: pueden transmitirse como cualquier otro tipo y utilizarse como argumento de las funciones. Lambda (es decir, funciones anónimas) son muy compatibles con la biblioteca estándar. 20 | - No hay una palabra clave `static`, sino que hay mejores alternativas. 21 | - Clases de datos son clases especiales diseñadas para contener datos. 22 | - Todo es una expresión: `if`, `for`, etc... Todos pueden devolver valores. 23 | - La expresión `when` es como un interruptor con superpoderes. 24 | 25 | Más información: 26 | 27 | - [Comparison to Java - kotlinlang.org](https://kotlinlang.org/docs/comparison-to-java.html) 28 | 29 | - [Kotlin for server side - kotlinlang.org](https://kotlinlang.org/docs/server-overview.html) 30 | 31 | - [Kotlin Evolution - kotlinlang.org](https://kotlinlang.org/docs/kotlin-evolution.html) 32 | 33 | ## Kotlin en línea de comandos 34 | 35 | - Compilar la aplicación usando el compilador de Kotlin: `$ kotlinc name.kt -include-runtime -d name.jar` 36 | - Ejecutar el programa: `$ java -jar name.jar` 37 | - Ejecutar la consola 'REPL': `$ kotlinc-jvm` 38 | - Usar la línea de comandos para ejecutar scripts (.kts): `$ kotlinc -script name.kts [params]` 39 | - Compilar una biblioteca sin la 'runtime' para ser usada en otros programas: `$ kotlinc name.kt -d name.jar` 40 | - Ejecutar binarios producidos por el compilador de Kotlin: `$ kotlin -classpath name.jar HelloKt (HelloKt is the main class name inside the file named name.kt)` 41 | 42 | Más información: 43 | 44 | - [Kotlin command-line compiler - kotlinlang.org](https://kotlinlang.org/docs/command-line.html) 45 | 46 | ## Sintaxis básica 47 | 48 | El punto de entrada en un programa escrito en Kotlin (y en Java) es la función `main(args: Array)`. Esta función recibe un array que contiene los argumentos de la línea de comandos. 49 | 50 | ```kotlin 51 | fun main(args: Array) { 52 | println("Hello World!") 53 | } 54 | ``` 55 | 56 | Las funciones y variables en Kotlin pueden declararse en un **"nivel superior"**, es decir, directamente dentro de un paquete. 57 | 58 | Si un archivo Kotlin contiene una sola clase (potencialmente con declaraciones de nivel superior relacionadas), su nombre debe ser el mismo que el nombre de la clase, con la extensión '.kt'. Si un archivo contiene varias clases, o solo declaraciones de nivel superior, el nombre debe describir lo que contiene el archivo en formato _'UpperCamelCase'_ (e.g. `ProcessDeclarations.kt`) 59 | 60 | Kotlin sigue las convenciones de nomenclatura de Java. Los nombres de los paquetes se escriben siempre en minúsculas y sin guiones bajos (e.g. `org.example.myproject`) 61 | 62 | Los nombres de las clases y los objetos se escriben en _'UpperCamelCase'_: 63 | 64 | ```kotlin 65 | open class DeclarationProcessor { ... } 66 | 67 | object EmptyDeclarationProcessor : DeclarationProcessor() { ... } 68 | ``` 69 | 70 | Los nombres de funciones, propiedades y variables locales en _'lowerCamelCase'_: 71 | 72 | ```kotlin 73 | fun processDeclarations() { ... } 74 | 75 | var declarationCount = ... 76 | ``` 77 | 78 | Los nombres de las constantes (propiedades marcadas con `const`) deben usar nombres en mayúsculas y separados por un guión bajo: 79 | 80 | ```kotlin 81 | const val MAX_COUNT = 8 82 | val USER_NAME_FIELD = "UserName" 83 | ``` 84 | 85 | ### Variables y tipos básicos 86 | 87 | En Kotlin, **todo es un objeto** en el sentido de que podemos llamar funciones y propiedades de miembro en cualquier variable. Algunos de los tipos como los números, los caracteres o los booleanos pueden tener una representación interna especial que se representa como valores primitivos en tiempo de ejecución, pero para el usuario se comportan como clases ordinarias. 88 | 89 | La declaración de valores se realiza utilizando `var` o `val`: 90 | 91 | - Los **valores constantes** se declaran como `val` y son inmutables o _'read-only'_, es decir, que no se pueden reasignar. 92 | 93 | - Las **variables** se declaran como `var` y son mutables, por lo que se le pueden asignar un nuevo valor pero únicamente del mismo tipo declarado. 94 | 95 | La recomendación es crear valores constantes inmutables, que son más seguras en entornos _'multithreading'_ ya que no se pueden modificar y utilizar las variables mutables cuando sea necesario. 96 | 97 | Este soporte de primera clase para los valores constantes es importante por una razón: la programación funcional. En la programación funcional, el uso de los valores constantes permiten algunas optimizaciones que aumentan el rendimiento. Por ejemplo, los cálculos pueden ser paralelos ya que existe una garantía de que el valor no cambiará entre dos ejecuciones paralelas, dado que no puede cambiar. 98 | 99 | ```kotlin 100 | val fooVal = 10 // val es inmutable y no podrá ser reutilizada 101 | 102 | val otherVal 103 | otherVal = "My Value" // Podemos declarar la variable 'val' en una línea y asignarle valor posteriormente. Sigue siendo una sola asignación. 104 | 105 | var fooVar = 10 106 | fooVar = 20 // Se le puede asignar un nuevo valor pero únicamente del mismo tipo. 107 | ``` 108 | 109 | En la mayoría de los casos, Kotlin puede determinar o inferir cuál es el tipo de una variable, por lo que no tenemos que especificarla explícitamente. Cuando la variable **no se inicialice deberemos indicar explícitamente el tipo de la variable** ya que Kotlin no puede inferir el tipo si no se inicializa. 110 | 111 | ```kotlin 112 | val foo: Int = 7 113 | val bar = 10 // Kotlin infiere automáticamente el tipo 114 | val hello: String // Si no se inicializa hay que especificar el tipo 115 | ``` 116 | 117 | - [Basic types - kotlinlang.org](https://kotlinlang.org/docs/basic-types.html#basic-types) 118 | 119 | #### Numbers 120 | 121 | Kotlin proporciona los tipos `Byte`, `Short`, `Int` y `Long` para enteros y los tipos `Float` y `Double` para números en coma flotante: 122 | 123 | ```kotlin 124 | val double: Double = 64.0 // 64 bits 125 | val float: Float = 32.0F // or 32f (32 bits) 126 | 127 | val long: Long = 64L // 64 bits 128 | val int: Int = 32 // 32 bits 129 | val short: Short = 16 // 16 bits 130 | 131 | val byte: Byte = 8 // 8 bits 132 | val hexadecimal: Int = 0x16 133 | val binary: Int = 0b101 134 | val char: Char = 'a' 135 | ``` 136 | 137 | Todas las variables inicializadas con un entero no deben exceder el tamaño máximo de `Int` ya que Kotlin infiere el tipo `Int` si no se especifica explícitamente el tipo o se añade el apéndice 'L' al valor. En el caso de números en coma flotante, Kotlin infiere el tipo `Double` si no se indica el tipo explícitamente o se marca el valor en coma flotante con el apéndice 'F'. 138 | 139 | ```kotlin 140 | val a = 1 // Kotlin infiere el tipo 'Int' 141 | val b = 1L // Kotlin infiere el tipo 'Long' 142 | 143 | val c = 3.14 // Kotlin infiere el tipo 'Double' 144 | val d = 2.7123F // Kotlin infiere el tipo 'Float' 145 | ``` 146 | 147 | A diferencia de Java, en Kotlin **todos los tipos son objetos** y por tanto no hay _'wrappers'_ u objetos envoltorio tipo `Integer`, `Double`, etc... 148 | 149 | Los guiones bajos se pueden utilizar para hacer que los números grandes sean más legibles: 150 | 151 | ```kotlin 152 | val million = 1_000_000 153 | ``` 154 | 155 | La conversión debe ser invocada explícitamente. Hay conversiones desde un tipo al resto de tipos: 156 | 157 | - `toByte()` ➜ Byte 158 | - `toShort()` ➜ Short 159 | - `toInt()` ➜ Int 160 | - `toLong()` ➜ Long 161 | - `toFloat()` ➜ Float 162 | - `toDouble()` ➜ Double 163 | - `toChar()` ➜ Char 164 | 165 | ``` kotlin 166 | val otherLong = int.toLong() 167 | val direct = 25.toLong() 168 | ``` 169 | 170 | - [Numbers - kotlinlang.org](https://kotlinlang.org/docs/numbers.html) 171 | 172 | #### Characters 173 | 174 | Los caracteres no son números en Kotlin, a diferencia de Java. En Kotlin los caracteres se representan con el tipo `Char`: 175 | 176 | Los literales de carácter se escriben con comillas simples como por ejemplo `'a'`. Los caracteres especiales se escapan con la barra invertida `'\'`. Están soportadas las siguientes secuencias de escape: `\t`, `\b`, `\n`, `\r`, `\'`, `\"`, `\\`, `\$`. 177 | 178 | Podemos convertir de forma explícitia un carácter en un número de tipo `Int`: 179 | 180 | ```kotlin 181 | fun decimalDigitValue(c: Char): Int { 182 | if (c !in '0'..'9') 183 | throw IllegalArgumentException("Out of range") 184 | return c.toInt() - '0'.toInt() // Explicit conversions to numbers 185 | } 186 | ``` 187 | 188 | - [Characters - kotlinlang.org](https://kotlinlang.org/docs/characters.html) 189 | 190 | #### Strings 191 | 192 | Las cadenas son **secuencias de caracteres inmutables** y se representan con el tipo `String` de manera similar a Java. Las cadenas se crean usando las comillas dobles. El escapado de caracteres se hace con una barra invertida `'\'`. 193 | 194 | ```kotlin 195 | val fooString = "My String Is Here!" 196 | val barString = "Printing on a new line?\nNo Problem!" 197 | val bazString = "Do you want to add a tab?\tNo Problem!" 198 | println(fooString) 199 | println(barString) 200 | println(bazString) 201 | println("John Doe"[2]) // => h 202 | println("John Doe".startsWith("J")) // => true 203 | ``` 204 | 205 | Se puede acceder a los elementos de una cadena como si fuera un array (e.g. `s[i]`) e iterar con un bucle tipo `for`: 206 | 207 | ```kotlin 208 | for (c in str) { 209 | println(c) 210 | } 211 | ``` 212 | 213 | Se puede utilizar el operador `+` para concatenar cadenas entre sí y con valores de otro tipo siempre y cuando uno de los elementos de la expresión sea una cadena: 214 | 215 | ```kotlin 216 | val s = "abc" + 1 217 | println(s + "def") 218 | ``` 219 | 220 | Una cadena sin formato o _'raw string'_ está delimitada por una comilla triple ("""). Las cadenas sin formato pueden contener nuevas líneas y cualquier otro carácter. Estas cadenas sin formato también tiene soporte para las _'string templates'_: 221 | 222 | ```kotlin 223 | val fooRawString = """ 224 | fun helloWorld(val name : String) { 225 | println("Hello, world!") 226 | } 227 | val hello = $who 228 | val result = ${2 + 2} 229 | """ 230 | ``` 231 | 232 | Con la función `trimMargin()` podemos eliminar los espacios en blanco: 233 | 234 | ```kotlin 235 | val text = """ 236 | |Tell me and I forget. 237 | |Teach me and I remember. 238 | |Involve me and I learn. 239 | |(Benjamin Franklin) 240 | """.trimMargin() 241 | ``` 242 | 243 | - [Strings - kotlinlang.org](https://kotlinlang.org/docs/strings.html) 244 | 245 | ##### String templates 246 | 247 | Un literal de cadena puede contener expresiones de plantilla o _'template expressions'_, que son fragmentos de código que será evaluado y cuyo resultado será concatenado en la cadena. Son una forma simple y efectiva de incrustar valores, variables o incluso expresiones dentro de una cadena. 248 | 249 | Una expresión de plantilla comienza con un signo de dólar (`$`) y consisten en un nombre de una variable (por ejemplo `$i`) o en una expresión (como por ejemplo `${name.length}`) en cuyo caso se utilizan llaves (`{}`): 250 | 251 | ```kotlin 252 | val name = "John Doe" 253 | println("$name has ${name.length} characters") // => John Doe has 8 characters 254 | 255 | val age = 40 256 | println("You are ${if (age > 60) "old" else "young"}") // => You are young 257 | ``` 258 | 259 | Las plantillas son compatibles tanto dentro de cadenas sin procesar como dentro de cadenas escapadas. En caso de necesitar representar el literal del dólar en una cadena sin escapar se utiliza esta sintaxis: 260 | 261 | ```kotlin 262 | val price = """ 263 | ${'$'}9.99 264 | """ 265 | ``` 266 | 267 | - [String templates - kotlinlang.org](https://kotlinlang.org/docs/strings.html#string-templates) 268 | 269 | #### Arrays 270 | 271 | Una matriz está representada por la clase `Array` y es **invariante**, por lo que, por ejemplo, no se puede asignar un `Array` a un tipo de variable `Array`. 272 | 273 | En Kotlin, podemos crear una matriz de elementos del mismo tipo o de distinto tipo utilizando la función de biblioteca `arrayOf()` y pasándole los elementos a añadir: 274 | 275 | ```kotlin 276 | val cardNames = arrayOf("Jack", "Queen", "King", 3, false) 277 | println(cardNames[1]) // => Queen 278 | ``` 279 | 280 | Podemos forzar la creación de arrays del mismo tipo. De esta forma el compilador comprobará el tipo de los elementos que se añaden y evitará que se añadan elementos de tipos no válidos: 281 | 282 | ```kotlin 283 | val myArray = arrayOf(1, 2, 3, 4) 284 | println(myArray.contentToString()) // => [1, 2, 3, 4] 285 | ``` 286 | 287 | La biblioteca estándar de Kotlin provee funciones para crear arrays de tipos primitivos como `intArrayOf()`, `longArrayOf()`, `charArrayOf()`, `doubleArrayOf()`, etc... Cada una de estas funciones devuelven una instancia de su equivalente en Kotlin como `IntArray`, `LongArray`, `CharArray`, `DoubleArray`, etc...: 288 | 289 | ```kotlin 290 | val cards = intArrayOf(10, 11, 12) // IntArray 291 | println("${cards[1]}") // => 11 292 | ``` 293 | 294 | Para mejorar la eficiencia y rendimiento del código, cuando se utilicen tipos primitivos hay que utilizar las funciones `intArrayOf()`, `longArrayOf()`, etc.. en vez de `arrayOf()` para así evitar el coste asociado a las operaciones de _'boxing'/'unboxing'_. 295 | 296 | Alternativamente, podemos crear una matriz a partir de un tamaño inicial y una función, que se utiliza para generar cada elemento usando el constructor `Array()`: 297 | 298 | ```kotlin 299 | val allCards = Array(12, { i -> i + 1 }) 300 | println("${allCards.first()} - ${allCards.last()}") // => 1 - 12 301 | ``` 302 | 303 | Iterando sobre la matriz con `indices`: 304 | 305 | ```kotlin 306 | for (index in cardNames.indices) { 307 | println("Element $index is ${cardNames[index]}") 308 | } 309 | ``` 310 | 311 | Otra forma posible de iterar es usando la función `withIndex()`: 312 | 313 | ```kotlin 314 | for ((index, value) in cardNames.withIndex()) { 315 | println("$index - $value") 316 | } 317 | ``` 318 | 319 | - [Arrays - kotlinlang.org](https://kotlinlang.org/docs/arrays.html) 320 | 321 | ### Packages 322 | 323 | La palabra clave `package` funciona de la misma manera que en Java. El nombre del paquete se usa para construir el **"Fully Qualified Name"** (FQN) de una clase, objeto, interfaz o función. 324 | 325 | Todo el contenido (como clases y funciones) de un fichero fuente están contenidos en el paquete declarado. Los nombres de los paquetes se escriben en minúscula y sin guiones bajos: 326 | 327 | ```kotlin 328 | package com.example.kotlin 329 | 330 | class MyClass { /*...*/ } 331 | 332 | fun saySomething(): String { /*...*/ } 333 | ``` 334 | 335 | En el ejemplo, el FQN de la clase será `com.example.kotlin.MyClass`. 336 | 337 | Dado que podemos tener _'top-level functions'_ como la función `saySomething()` del ejemplo, el FQN de esta función será `com.example.kotlin.saySomething`. 338 | 339 | Si no se especifica un paquete, el contenido del fichero fuente pertenece al paquete **'default'**. 340 | 341 | - [Packages - kotlinlang.org](https://kotlinlang.org/docs/packages.html) 342 | 343 | ### Imports 344 | 345 | En Kotlin, usamos la declaración de importación para permitir que el compilador localice las clases e interfaces, propiedades, enumeraciones, funciones y objetos que se importarán. 346 | 347 | En Java, por otro lado, solo esta permitido importar clases o interfaces. 348 | 349 | ```kotlin 350 | // 'Bar' esta disponible en el código 351 | import foo.Bar 352 | 353 | // Si existe cierta ambigüedad podemos usar la palabra clave 'as' 354 | import foo.Bar 355 | import bar.Bar as bBar 356 | 357 | // Todo el contenido de 'foo' está disponible 358 | import foo.* 359 | ``` 360 | 361 | Por defecto, al igual que en Java, el compilador importa de forma implícita una serie de paquetes y por tanto están disponibles de forma automática. 362 | 363 | - [Imports - kotlinlang.org](https://kotlinlang.org/docs/packages.html#imports) 364 | 365 | ### Comentarios 366 | 367 | ```kotlin 368 | // Single-line comments start with // 369 | 370 | /* 371 | Multi-line comments look like this. 372 | */ 373 | ``` 374 | 375 | ### Control de flujo y bucles 376 | 377 | Kotlin tiene 4 construcciones de control de flujo: `if`, `when`, `for` y `while`. `if` y `when` son expresiones, por lo que devuelven un valor; `for` y `when` son declaraciones, por lo que no devuelven un valor. `if` y `when` también se pueden utilizar como sentencias, es decir, se pueden utilizar de forma autónoma y sin devolver un valor. 378 | 379 | Un bucle `for` puede usarse con cualquier elemento que proporcione un iterador como rangos, colecciones, etc...: 380 | 381 | ```kotlin 382 | for (c in "hello") { 383 | println(c) 384 | } 385 | 386 | for (i in 1..3) { 387 | println(i) 388 | } 389 | 390 | for (i in 6 downTo 0 step 2) { 391 | println(i) 392 | } 393 | ``` 394 | 395 | Los bucles `while` y `do-while` funcionan de la misma manera que en otros lenguajes: 396 | 397 | ```kotlin 398 | while (x > 0) { 399 | x-- 400 | } 401 | 402 | do { 403 | val y = retrieveData() 404 | } while (y != null) // y is visible here! 405 | ``` 406 | 407 | La instrucción `if` y `if..else` funciona igual que en Java. Además, en Kotlin los bloques `if` se pueden utilizar como una expresión que devuelve un valor. Por este motivo el operador ternario _'condition ? then: else'_ no es necesario en Kotlin: 408 | 409 | ```kotlin 410 | // Traditional usage 411 | var max = a 412 | if (a < b) max = b 413 | 414 | // With else 415 | var max: Int 416 | if (a > b) { 417 | max = a 418 | } else { 419 | max = b 420 | } 421 | 422 | // As expression 423 | val max = if (a > b) a else b 424 | 425 | // With blocks 426 | // returns a or 5 427 | var top = if (a > 5) { 428 | println("a is greater than 5") 429 | a 430 | } else { 431 | println("5 is greater than a") 432 | 5 433 | } 434 | ``` 435 | 436 | Los bloques `when` se pueden usar como una alternativa a las cadenas `if-else-if` o en substitución de los `switch`. Si no se proporciona ningún argumento, las condiciones de la rama son simplemente expresiones booleanas, y una rama se ejecuta cuando su condición es verdadera: 437 | 438 | ```kotlin 439 | when { 440 | x.isOdd() -> print("x is odd") 441 | x.isEven() -> print("x is even") 442 | else -> print("x is funny") 443 | } 444 | ``` 445 | 446 | La instrucción `when` se puede usar con un argumento. Si ninguna de las opciones coincide con el argumento, se ejecuta la opción del bloque `else`: 447 | 448 | ```kotlin 449 | when (x) { 450 | 1 -> print("x == 1") 451 | 2 -> print("x == 2") 452 | else -> { 453 | println("none of the above") // Nótese el uso de llaves para delimitar el bloque de código 454 | } 455 | } 456 | ``` 457 | 458 | La instrucción `when` se puede utilizar como una expresión que devuelve un valor. En este caso el bloque `else` es **obligatorio**. De hecho, la única excepción a esta regla es si el compilador puede garantizar que siempre devuelve un valor. Por lo tanto, si las ramas normales cubren todos los valores posibles, entonces no hay necesidad de una rama `else`: 459 | 460 | ```kotlin 461 | val result = when (i) { 462 | 0, 21 -> "0 or 21" 463 | in 1..20 -> "in the range 1 to 20" 464 | else -> "none of the above" 465 | } 466 | println(result) 467 | 468 | val check = true 469 | val result = when(check) { // All results are covered 470 | true -> println("it's true") 471 | false -> println("it's false") 472 | } 473 | ``` 474 | 475 | Se pueden utilizar expresiones arbitrarias, y no solo constantes, como condiciones en los bloques: 476 | 477 | ```kotlin 478 | when (x) { 479 | parseInt(s) -> print("s encodes x") 480 | else -> print("s does not encode x") 481 | } 482 | ``` 483 | 484 | Si muchos casos deben manejarse de la misma manera, las condiciones de la rama pueden combinarse con una coma: 485 | 486 | ```kotlin 487 | when (x) { 488 | 0, 1 -> print("x == 0 or x == 1") 489 | else -> print("otherwise") 490 | } 491 | ``` 492 | 493 | También podemos verificar si un valor está dentro `in` o no está dentro `!in` de un rango o una colección: 494 | 495 | ```kotlin 496 | when (x) { 497 | in 1..10 -> print("x is in the range") 498 | in validNumbers -> print("x is valid") 499 | !in 10..20 -> print("x is outside the range") 500 | else -> print("none of the above") 501 | } 502 | ``` 503 | 504 | ## Funciones 505 | 506 | Las funciones se declaran usando la palabra clave `'fun'`. Los nombres de las funciones empiezan con minúscula. Los parámetros de la función se especifican entre paréntesis después del nombre de la función y tienen la forma `'name: type'`. El **tipo de cada parámetro debe especificarse explícitamente** y no puede omitirse. 507 | 508 | ```kotlin 509 | fun powerOf(number: Int, exponent: Int) { ... } 510 | ``` 511 | 512 | Los parámetros de la función pueden tener opcionalmente un valor por defecto, que se utilizará en caso de se omita el argumento al invocar la función. El tipo de retorno de la función, si es necesario, se especifica después de los parámetros: 513 | 514 | ```kotlin 515 | fun hello(name: String = "world"): String { // valor por defecto 516 | return "Hello, $name!" 517 | } 518 | 519 | hello("foo") // => Hello, foo! 520 | hello(name = "bar") // => Hello, bar! 521 | hello() // => Hello, world! 522 | 523 | fun bye(bye: String = "Bye", name: String): String { 524 | return "$bye, $name!!" 525 | } 526 | 527 | bye(name = "John", bye = "Good bye") // => Good bye, John!! 528 | bye(name = "John") // => Bye, John!! 529 | ``` 530 | 531 | En la sobreescritura de métodos con valores por defecto siempre se utilizan los mismos valores de parámetros por defecto que el método base. Cuando se sobreescribe un método, los valores por defecto deben omitirse de la firma: 532 | 533 | ```kotlin 534 | open class A { 535 | open fun foo(i: Int = 10) { ... } 536 | } 537 | 538 | class B : A() { 539 | override fun foo(i: Int) { ... } // no default value allowed 540 | } 541 | ``` 542 | 543 | Si un parámetro por defecto precede a un parámetro sin valor predeterminado, el valor por defecto solo se puede usar llamando a la función con argumentos con nombre: 544 | 545 | ```kotlin 546 | fun foo(bar: Int = 0, baz: Int) { ... } 547 | 548 | foo(baz = 1) // The default value bar = 0 is used 549 | ``` 550 | 551 | Dado que Java no admite valores de parámetros por defecto en los métodos, deberá especificar todos los valores de parámetros explícitamente cuando llame a una función de Kotlin desde Java. Kotlin nos proporciona la funcionalidad para facilitar las llamadas de Java al anotar la función Kotlin con `'@JvmOverloads'`. Esta anotación le indicará al compilador de Kotlin que genere las funciones sobrecargadas de Java para nosotros. 552 | 553 | ```kotlin 554 | @JvmOverloads 555 | fun calCircumference(radius: Double, pi: Double = Math.PI): Double = (2 * pi) * radius 556 | 557 | // En Java 558 | double calCircumference(double radius, double pi); 559 | double calCircumference(double radius); 560 | ``` 561 | 562 | Cuando una función no devuelve ningún valor significativo, su tipo de devolución por defecto es `Unit`. En ese caso indicar el tipo de retorno es opcional. El tipo `Unit` es un objeto en Kotlin que es similar a los tipos `void` en Java y C. 563 | 564 | ```kotlin 565 | fun hello(name: String): Unit { 566 | print("Hello $name") 567 | } 568 | 569 | fun sayHello(name: String) { // compila ya que el compilador infiere el tipo 'Unit' 570 | print("Hello $name") 571 | } 572 | ``` 573 | 574 | Los parámetros con nombre permiten código más legible al nombrar los parámetros que se pasan a una función cuando se invoca. Una vez que se utiliza un nombre en un parámetro, el resto de parámetros también deben asignarse con nombre: 575 | 576 | ```kotlin 577 | fun area(width: Int, height: Int): Int { 578 | return width * height 579 | } 580 | 581 | area(10, 12) 582 | area(width = 10, height = 12) // código más legible 583 | area(height = 12, width = 10) // podemos cambiar el orden 584 | area(10, height = 12) // argumento por posición y argumentos con nombre 585 | area(width = 10, 12) // ¡incorrecto! no se permiten argumentos con nombre antes de argumentos por posición 586 | 587 | fun bar(k: Int, m: Long = 1L, j: Boolean = true) = println("$k - $m - $j") 588 | 589 | // Una vez que un parámetro ha sido nombrado, todos los siguientes parámetros deben ser nombrados 590 | bar(10) // => Se omiten los parámentros por defecto 591 | bar(15, 30L) 592 | bar(20, 2L, true) 593 | bar(m = 30L, j = false, k = 10) 594 | bar(k = 10, m = 20L, j = true) 595 | bar(5, m = 2L, j = true) 596 | bar(6, 1L, j = true) 597 | ``` 598 | 599 | Cuando se invoca una función con argumentos posicionales y con nombre, todos los argumentos posicionales deben colocarse antes del primero argumento con nombre. Por ejemplo, la llamada `f(1, y = 2)` está permitida, pero `f(x = 1, 2)` no está permitida. 600 | 601 | Para pasar un número variable de argumentos a una función podemos usar la palabra clave `'vararg'` delante del nombre de una variable. Por tanto la función aceptará una lista de parámetros separados por comas que el compilador envolverá en una array. Por tanto, dentro de la función accederemos a los parámetros mediante la notación de array. 602 | 603 | Este tipo de parámetros se puede combinar con otros parámetros. Normalmente el parámetro `'vararg'` será el último de la lista. Si hay otros parámetros después de `'vararg'`, deberán usarse parámetros con nombre: 604 | 605 | ```kotlin 606 | fun varargExample(vararg names: Int) { 607 | println("Argument has ${names.size} elements") 608 | } 609 | varargExample() // => Argument has 0 elements 610 | varargExample(1) // => Argument has 1 elements 611 | varargExample(1, 2, 3) // => Argument has 3 elements 612 | 613 | 614 | fun car(vararg model: String, year: Int) {} 615 | car("Audi", "A6", year = 2005) // parámetros con nombre después de 'vararg' 616 | ``` 617 | 618 | Para utilizar un array para suministrar un número variable de argumentos se utiliza el operador `'*'` también llamado _'spread operator'_ delante del nombre de la variable del array: 619 | 620 | ```kotlin 621 | val intArray = intArrayOf(1, 2, 3, 4) 622 | val array = Array(5, { i -> i + 1 }) 623 | varargExample(*intArray) // => Argument has 4 elements 624 | varargExample(*array.toIntArray()) // => Argument has 5 elements 625 | ``` 626 | 627 | Cuando una función consiste en una sola expresión, se pueden omitir los paréntesis. El cuerpo se especifica después de un símbolo `'='`: 628 | 629 | ```kotlin 630 | fun odd(x: Int): Boolean = x % 2 == 1 631 | ``` 632 | 633 | Declarar explícitamente el tipo de retorno de una función cuando es una expresión es opcional cuando puede ser inferido por el compilador o cuando el tipo de retorno es `'Unit'`. Cuando el cuerpo de una función es un bloque hay que especificar el tipo de retorno ya que el compilador no puede inferirlo: 634 | 635 | ```kotlin 636 | fun even(x: Int) = x % 2 == 0 // Optional 637 | 638 | fun printHello(name: String?) { // 'Unit' 639 | if (name != null) 640 | println("Hello ${name}") 641 | else 642 | println("Hi there!") 643 | // `return Unit` or `return` is optional 644 | } 645 | ``` 646 | 647 | A veces queremos devolver múltiples valores desde una función. Una forma es usar el tipo `'Pair'` de Kotlin. Esta estructura incluye dos valores a los que luego se puede acceder. Este tipo de Kotlin puede aceptar cualquier tipo que suministre a su constructor. Y, lo que es más, los dos tipos ni siquiera necesitan ser iguales. Kotlin también provee el tipo `'Triple'` que retorna tres valores: 648 | 649 | ```kotlin 650 | fun getNumbers(num: Int): Pair { 651 | require(num > 0, { "Error: num is less than 0" }) 652 | return Pair(num, num * 2) 653 | } 654 | 655 | val(num, num2) = getNumbers(10) // destructuring 656 | ``` 657 | 658 | En Kotlin, podemos hacer que la creación de una instancia _'Pair'_ sea más compacta y legible utilizando la función _'to'_, que es una función _'infix'_ en lugar del constructor de _'Pair'_. 659 | 660 | ```kotlin 661 | val nigeriaCallingCodePair = 234 to "Nigeria" 662 | val nigeriaCallingCodePair2 = Pair(234, "Nigeria") // Same as above 663 | ``` 664 | 665 | - [Functions - kotlinlang.org](https://kotlinlang.org/docs/functions.html) 666 | 667 | ### Extension functions 668 | 669 | Las **_'extension functions'_** son una forma de agregar nuevas funcionalidades a una clase sin tener que heredar de dicha clase. Esto es similar a los métodos de extensión de C#. Una función de extensión se declara fuera de la clase que quiere extender. En otras palabras, también es una _'top-level function'_. Junto con las funciones de extensión, Kotlin también admite propiedades de extensión. 670 | 671 | Para crear una _'extension function'_, debe prefijar el nombre de la clase que está extendiendo antes del nombre de la función. El nombre de la clase o el tipo en el que se define la extensión se denomina **tipo de receptor**, y el **objeto receptor** es la instancia de clase o el valor concreto sobre el que se llama a la función de extensión. 672 | 673 | ```kotlin 674 | fun String.remove(c: Char): String { // 'String' es el tipo receptor 675 | return this.filter { it != c } // 'this' corresponde al objeto receptor 676 | } 677 | 678 | println("Hello, world!".remove('l')) // => Heo, world! // "Hello World" es el objeto receptor 679 | ``` 680 | 681 | En caso de que una _'extension function'_ tenga la misma firma (mismo nombre y misma lista de parámetros) que una función miembro, es decir, una función de la clase, el compilador invocará antes la función miembro que la función de extensión aunque no se generará ningún error de compilación: 682 | 683 | ```kotlin 684 | class C { 685 | fun foo() { println("member") } 686 | } 687 | 688 | fun C.foo() { 689 | println("extension") 690 | } 691 | 692 | fun C.foo(i: Int) { 693 | println("extension & overrided") 694 | } 695 | 696 | C().foo() // => member 697 | C().foo(5) // => extension & overrided 698 | ``` 699 | 700 | ### Top-level functions 701 | 702 | **Las funciones de nivel superior son funciones que se definen fuera de cualquier clase, objeto o interfaz**. Esto significa que son funciones a las que llama directamente, sin la necesidad de crear ningún objeto o llamar a ninguna clase. Dado que Java no soporta este tipo de funciones el compilador de Kotlin genera una clase con métodos estáticos. Estas tipo de funciones son especialmente útiles para crear funciones de utilidad o de ayuda. 703 | 704 | ```kotlin 705 | // Code defined inside a file called 'UserUtils.kt' 706 | @file:JvmName("UserUtils") 707 | package com.example.project.utils 708 | 709 | fun checkUserStatus(): String { 710 | return "online" 711 | } 712 | ``` 713 | 714 | ### High-Order Functions 715 | 716 | Las funciones en Kotlin son de primera clase, lo que significa que pueden ser almacenadas en variables y estructuras de datos, pasadas como argumentos y devueltas desde otras funciones de orden superior. Puede operar con funciones de cualquier manera que sea posible para otros valores no funcionales. 717 | 718 | Para facilitar esto, Kotlin, como lenguaje de programación estáticamente tipado, utiliza una familia de tipos de función para representar funciones y proporciona un conjunto de construcciones de lenguaje especializadas, tales como expresiones lambda. 719 | 720 | Una _'high-order function'_ o **función de orden superior** es una función que puede tomar funciones como parámetros y/o devolver una función como tipo de retorno. 721 | 722 | ```kotlin 723 | // Función con dos parámetros, el segundo de ellos es una función 724 | fun foo(str: String, fn: (String) -> String): Unit { 725 | val applied = fn(str) 726 | println(applied) 727 | } 728 | foo("Hello", { it.reversed() }) // => olleH 729 | 730 | // Esta función de orden superior devuelve una función 731 | fun isPositive(n: Int): (Int) -> Boolean { 732 | return { n > 0 } // return a function. Instead 'return value' we have 'return { function }' 733 | } 734 | 735 | // Esta función de orden superior devuelve una función de forma más compacta 736 | fun modulo(k: Int): (Int) -> Boolean = { it % k == 0 } 737 | 738 | val evens = listOf(1, 2, 3, 4, 5, 6).filter(modulo(2)) // => [2, 4, 6] 739 | 740 | // Asignar la función a una variable 741 | val isEven: (Int) -> Boolean = modulo(2) 742 | 743 | listOf(1, 2, 3, 4).filter(isEven) // => [2, 4] 744 | listOf(5, 6, 7, 8).filter(isEven) // => [6, 8] 745 | ``` 746 | 747 | ### Functions types & Lambdas 748 | 749 | Un tipo función es un tipo que consta de una firma de función, es decir, dos paréntesis que contiene la lista de parámetros (que son opcionales) y un tipo de retorno. Ambas partes están separadas por el operador `'->'`. 750 | 751 | Cuando se define un tipo función, siempre se debe indicar explícitamente el tipo de retorno. Cuando se declaran funciones normales que devuelven `Unit`, se puede omitir el tipo de retorno ya que el compilador lo infiere, pero no se puede omitir en los tipos función. Además, debe poner los paréntesis para los parámetros, incluso cuando el tipo función no acepta ningún parámetro. 752 | 753 | ```kotlin 754 | fun executor(action:() -> Unit) { 755 | action() 756 | } 757 | 758 | // 'action' es el nombre del parámetro y su tipo es '() -> Unit' que es una función. 759 | // Por tanto el tipo de 'action' es un tipo función. 760 | ``` 761 | 762 | - Ejemplo de un tipo función que no toma parámetros y devuelve 'Unit': `() -> Unit` 763 | 764 | - Ejemplo de un tipo función que no toma parámetros y devuelve un String: `() -> String` 765 | 766 | - Ejemplo de un tipo función que toma un String y no devuelve nada: `(String) -> Unit` 767 | 768 | - Ejemplo de un tipo función que toma dos parámetros y no devuelve nada: `(String, Float) -> Unit` 769 | 770 | Debido a que un tipo función es solo un tipo, significa que puede asignar una función a una variable, puede pasarla como un argumento a otra función o puede devolverla desde una función tal y como suceden en las _`high-order functions'_: 771 | 772 | ```kotlin 773 | val saySomething: (String) -> Unit = { x -> println(x) } 774 | saySomething("Good morning") // => Good morning 775 | ``` 776 | 777 | Una forma de instanciar una función tipo es usando el operador `'::'`. También podemos usar este operardor para pasar un tipo función como parámetro de otra función especificando su nombre con el operador y sin utilizar los paréntesis: 778 | 779 | ```kotlin 780 | fun businessEmail(s: String): Boolean { 781 | return s.contains("@") && s.contains("business.com") 782 | } 783 | isAnEmail(::businessEmail) // Invocar una 'high-order function' pasándole otra función por su nombre 784 | 785 | fun tell(text: String) { 786 | println(text) 787 | } 788 | 789 | var saySomething: (String) -> Unit // La variable 'saySomething' es una variable de tipo función 790 | saySomething = ::tell // instanciar el tipo función y asignarlo a la variable 'saySomething' 791 | 792 | saySomething("Hello") //=> Hello 793 | ``` 794 | 795 | En particular, una **lambda es una función literal**: una función anónima que no se declara pero se usa directamente como una expresión. 796 | 797 | Básicamente, una lambda es un bloque de código que se puede pasar como cualquier otro literal (por ejemplo, simplemente como una cadena literal `"una cadena"`). La combinación de estas características permite a Kotlin soportar la programación funcional básica. 798 | 799 | En el ejemplo una variable 'sum' de tipo función y a la que le asignamos directamente una función _'lambda'_ con dos parámetros: 800 | 801 | ```kotlin 802 | // Asignando una función 'lambda' 803 | val sum: (Int, Int) -> Int = { x, y -> x + y } 804 | sum(10, 20) // => 30 805 | 806 | // Equivalente usando el operador '::' 807 | fun operation(x: Int, y: Int): Int { 808 | return x + y 809 | } 810 | val sum: (Int, Int) -> Int = ::operation 811 | sum(10, 20) // => 30 812 | ``` 813 | 814 | En Kotlin, por convención si una función _'lambda'_ tiene solo un parámetro, su declaración puede omitirse (junto con ->). El nombre del único parámetro será `'it'`. 815 | 816 | ```kotlin 817 | val isNegative: (Int) -> Boolean = { it < 0 } // este literal es del tipo '(it: Int) -> Boolean' 818 | isNegative(-5) // => true 819 | ``` 820 | 821 | Otra convención es que si el último parámetro de una función acepta una función, una expresión 'lambda' que es pasada como el argumento correspondiente se puede colocar **fuera de los paréntesis**: 822 | 823 | ```kotlin 824 | // lambda expression inside parentheses 825 | val upperCaseLetters = "Hello World".filter({ it.isUpperCase() }) 826 | 827 | // lambda outside parentheses 828 | val lowerCaseLetters = "Hello World".filter { it.isLowerCase() } 829 | 830 | println("$upperCaseLetters - $lowerCaseLetters") // => HW - elloorld 831 | ``` 832 | 833 | El siguiente ejemplo tenemos una función de orden superior que acepta una función lambda `{ (String) -> Boolean }` como parámetro. Se expresa como "acepta una función 'from String to Boolean'": 834 | 835 | ```kotlin 836 | // El parámetro 'email' podemos usarlo como una función que acepta una cadena y devuelve un booleano. 837 | fun isAnEmail(email: (String) -> Boolean) { 838 | email("myemail@example.com") 839 | } 840 | isAnEmail({ s: String -> s.contains("@") }) // forma completa 841 | isAnEmail { s: String -> s.contains("@") } // Los paréntesis son opcionales 842 | isAnEmail { it.contains("@") } // Uso de 'it' 843 | ``` 844 | 845 | Para parámetros no utilizados se utiliza el operador `'_'`: 846 | 847 | ```kotlin 848 | val unusedSecondParam: (String, Int) -> Boolean = { s, _ -> 849 | s.length > 10 850 | } 851 | unusedSecondParam("Hello World", 0) // 0 is unused 852 | ``` 853 | 854 | ### Anonymous functions 855 | 856 | Una función anónima se parece mucho a una declaración de función normal, excepto que se omite su nombre. Su cuerpo puede ser una expresión o un bloque: 857 | 858 | ```kotlin 859 | // Función anónima cuyo cuerpo es una expresión 860 | fun(x: Int, y: Int): Int = x + y 861 | 862 | // Función anónima con bloque 863 | fun(x: Int, y: Int): Int { 864 | return x + y 865 | } 866 | ``` 867 | 868 | El tipo de los parámetros de una función anónima pueden omitirse si se pueden inferir por el contexto: 869 | 870 | ```kotlin 871 | ints.filter(fun(item) = item > 0) 872 | ``` 873 | 874 | La inferencia de tipo de retorno para funciones anónimas funciona igual que para las funciones normales: el tipo de retorno se deduce automáticamente para funciones anónimas con un cuerpo de expresión y debe especificarse explícitamente (o se supone que es `'Unit'`) para funciones anónimas con un cuerpo de bloque. 875 | 876 | ### Closures 877 | 878 | Un **_'closure'_** es una función que tiene acceso a variables y parámetros que se definen en un ámbito externo. A diferencia de Java, las variables 'capturadas' pueden ser modificadas. 879 | 880 | ```kotlin 881 | fun printFilteredNamesByLength(length: Int) { 882 | val names = arrayListOf("Adam", "Andrew", "Chike", "Kechi") 883 | val filterResult = names.filter { 884 | it.length == length // 'length' se define fuera del ámbito de la lambda 885 | } 886 | println(filterResult) 887 | } 888 | ``` 889 | 890 | ### Local or Nested Functions 891 | 892 | Para llevar más lejos la modularización de programas, Kotlin nos proporciona funciones locales, también conocidas como funciones anidadas o _'nested functions'_. **Una función local es una función que se declara dentro de otra función**. 893 | 894 | Podemos hacer que nuestras funciones locales sean más concisas al no pasarles parámetros explícitamente. Esto es posible porque las funciones locales tienen acceso a todos los parámetros y variables de la función de cierre. 895 | 896 | ```kotlin 897 | fun printCircumferenceAndArea(radius: Double): Unit { 898 | 899 | fun calCircumference(radius: Double): Double = (2 * Math.PI) * radius 900 | val circumference = "%.2f".format(calCircumference(radius)) 901 | 902 | fun calArea(radius: Double): Double = (Math.PI) * Math.pow(radius, 2.0) 903 | val area = "%.2f".format(calArea(radius)) 904 | 905 | print("The circle circumference of $radius radius is $circumference and area is $area") 906 | } 907 | ``` 908 | 909 | ### Infix Functions 910 | 911 | Las funciones marcadas con la palabra clave `'infix'` se pueden llamar usando la notación _'infix'_ (omitiendo el punto y los paréntesis para la llamada). Estas funciones deben cumplir los siguientes requisitos: 912 | 913 | - Tienen que ser miembros de una clase o funciones de extensión 914 | 915 | - Deben tener un solo parámetro 916 | 917 | - Este parámetro no será `'vararg'` ni tener valor por defecto 918 | 919 | Para invocar una función `'infix'` en Kotlin no necesitamos usar la notación de puntos ni los paréntesis. Hay que tener en cuenta que las funciones `'infix'` siempre requieren que se especifiquen tanto el receptor como el parámetro. Cuando se invoca un método en el receptor actual, como por ejemplo dentro de la clase, se necesita usar explicitamente la notación `'this'`. A diferencia de las llamadas a métodos regulares, no se puede omitir. 920 | 921 | ```kotlin 922 | class Student { 923 | var kotlinScore = 0.0 924 | 925 | infix fun addKotlinScore(score: Double): Unit { 926 | this.kotlinScore = kotlinScore + score 927 | } 928 | 929 | fun build() { 930 | this addKotlinScore 95.0 // Correcto 931 | addKotlinScore(95.0) // Correcto 932 | addKotlinScore 95.0 // Incorrectp: hay que especificar el receptor ('this') 933 | } 934 | } 935 | 936 | val student = Student() 937 | student addKotlinScore 95.00 // Invocando la función usando la notación 'infix' 938 | student.addKotlinScore(95) // Invocando la función con notación normal 939 | ```` 940 | 941 | ### Inline functions 942 | 943 | El compilador de Kotlin crea una clase anónima en versiones anteriores de Java cuando creamos o utilizamos expresiones lambda. Esto genera una sobrecarga, además de la carga de memoria que se genera cuando en una función lambda hace uso de variables de fuera de su entorno como en las _'closures'_. 944 | 945 | Para evitar esta sobrecarga tenemos el modificador `'inline'` para las funciones. Una _'High-Order function'_ con el modificador `'inline'` se integrará durante la compilación del código. En otras palabras, el compilador copiará la 'lambda' (o función literal) y también el cuerpo de la función de orden superior y los pegará en el sitio de la llamada. 946 | 947 | Con este mecanismo, nuestro código se ha optimizado significativamente, no más creación de clases anónimas o asignaciones de memoria extra. Por otro lado el uso de `'inline'` hace que el compilador genere ficheros bytecode más grandes. Por esta razón, se recomienda encarecidamente que solo se incluyan funciones de orden superior más pequeñas que acepten lambda como parámetros. 948 | 949 | ## Clases y objetos 950 | 951 | ### Clases 952 | 953 | Las clases son los bloques de construcción principales de cualquier lenguaje de programación orientado a objetos. Las clases son esencialmente **tipos personalizados**: un grupo de variables y métodos unidos en una estructura coherente. Para definir una clase se usa la palabra clave `'class'`. 954 | 955 | ```kotlin 956 | class Invoice { ... } 957 | ``` 958 | 959 | La declaración de clase consiste en el nombre de la clase, el encabezado de la clase (especificando sus parámetros de tipo, el constructor primario, etc.) y el cuerpo de clase, rodeado de llaves. Tanto el encabezado como el cuerpo son opcionales. Si la clase no tiene cuerpo se pueden omitir las llaves. 960 | 961 | Si no se especifica visibilidad, la visibilidad por defecto es `public` y por tanto cualquiera puede crear instancias de dicha clase. 962 | 963 | ```kotlin 964 | class Empty 965 | ``` 966 | 967 | En comparación con Java, puede definir varias clases dentro del mismo archivo fuente. 968 | 969 | La clases pueden contener: 970 | 971 | - Constructores y bloques `'init'` 972 | 973 | - Funciones 974 | 975 | - Propiedades 976 | 977 | - Clases anidadas e internas 978 | 979 | - Declaraciones de tipo `'object'` 980 | 981 | #### Constructores 982 | 983 | Una clase en Kotlin puede tener un **constructor primario** y uno o más **constructores secundarios**. 984 | 985 | El constructor primario es parte del encabezado de la clase. Este constructor va después del nombre de la clase (y los parámetros de tipo que son opcionales). Por defecto, todos los constructores son públicos, lo que equivale efectivamente a que sean visible en todas partes donde la clase sea visible. 986 | 987 | ```kotlin 988 | class Person constructor(firstName: String) { ... } 989 | ``` 990 | 991 | Si el constructor principal no tiene anotaciones o modificadores de visibilidad, la palabra clave `'constructor'` se puede omitir: 992 | 993 | ```kotlin 994 | // Podemos omitir la palabra clave 'constructor' 995 | class Person(firstName: String) { ... } 996 | 997 | // Las anotaciones o modificadores de visibilidad requieren la palabra clave 'constructor' 998 | class Customer public @Inject constructor(name: String) { ... } 999 | ``` 1000 | 1001 | Si una clase no-abstracta no declara ningún constructor (primario o secundario), tendrá un constructor primario sin argumentos generado automáticamente. La visibilidad del constructor será **pública** por defecto. Si no desea que su clase tenga un constructor público, es necesario declarar un constructor vacío con una visibilidad que no sea la predeterminada: 1002 | 1003 | ```kotlin 1004 | // Clase con un constructor privado 1005 | class DontCreateMe private constructor () { ... } 1006 | ``` 1007 | 1008 | Para crear una instancia de una clase, se invoca al constructor como si de una función regular se tratase. En Kotlin no existe la palabra clave _'new'_: 1009 | 1010 | ```kotlin 1011 | class Person(val name: String) { 1012 | constructor(name: String, parent: Person) : this(name) { 1013 | parent.children.add(this) 1014 | } 1015 | } 1016 | 1017 | val person = Person("John") 1018 | ``` 1019 | 1020 | ##### Constructor primario 1021 | 1022 | El **constructor primario** no puede contener ningún código. El código de inicialización se puede colocar en bloques de inicialización, que se definen con la palabra clave `'init'`. 1023 | 1024 | Durante una inicialización de la instancia, los bloques de inicialización se ejecutan en el mismo orden en que aparecen en el cuerpo de la clase, intercalados con los inicializadores de propiedades: 1025 | 1026 | ```kotlin 1027 | class InitOrderDemo(name: String) { 1028 | val firstProperty = "First property: $name" 1029 | 1030 | init { 1031 | println("First initializer block that prints ${name}") 1032 | } 1033 | 1034 | val secondProperty = "Second property: ${name.length}" 1035 | 1036 | init { 1037 | println("Second initializer block that prints ${name.length}") 1038 | } 1039 | } 1040 | ``` 1041 | 1042 | Los bloques `'init'` pueden usarse para validar las propiedades o parámetros mediante la palabra clave `'require'`: 1043 | 1044 | ```kotlin 1045 | class Person (val firstName: String, val lastName: String, val age: Int?) { 1046 | init{ 1047 | require(firstName.trim().length > 0) { "Invalid firstName argument." } 1048 | require(lastName.trim().length > 0) { "Invalid lastName argument." } 1049 | 1050 | if (age != null) { 1051 | require(age >= 0 && age < 150) { "Invalid age argument." } 1052 | } 1053 | } 1054 | } 1055 | ``` 1056 | 1057 | Tenga en cuenta que los parámetros del constructor primario se pueden usar en los bloques de inicialización. También pueden ser utilizados en los inicializadores de las propiedades en el cuerpo de la clase: 1058 | 1059 | ```kotlin 1060 | class Customer(name: String) { 1061 | // Uso del parámetro 'name' para inicializar la propiedad 'customerKey' 1062 | val customerKey = name.toUpperCase() 1063 | } 1064 | ``` 1065 | 1066 | De hecho, para **declarar propiedades** e inicializarlas desde el constructor principal, Kotlin tiene una sintaxis concisa: 1067 | 1068 | ```kotlin 1069 | class Person(val firstName: String, val lastName: String, var age: Int) { ... } 1070 | ``` 1071 | 1072 | De la misma forma que las propiedades definidas en el cuerpo de la clase, las propiedades declaradas en el constructor primario pueden ser mutables (`'var'`) o de solo lectura (`'val'`). 1073 | 1074 | Cuando se usa el prefijo `'val'` Kotlin genera automáticamente el método `'getter()'` y cuando se usa el prefijo `'var'` Kotlin genera el `'getter()'` y `'setter()'`. Si no necesitamos los accesores se puede definir el constructor sin los prefijos. De esta forma podemos definir nuestros propios métodos accesores. 1075 | 1076 | En este ejemplo, el constructor principal de la primera clase define las propiedades, mientras que el segundo no lo hace: 1077 | 1078 | ```kotlin 1079 | // class with primary constructor that defines properties 1080 | class Info (var name: String, var number: Int) 1081 | 1082 | // class with primary constructor that does not define properties 1083 | class Info (name: String, number: Int) 1084 | ``` 1085 | 1086 | ##### Constructor secundario 1087 | 1088 | La clase también puede declarar uno o varios **constructores secundarios**, que se definen con la palabra clave `'constructor'`: 1089 | 1090 | ```kotlin 1091 | class Person { 1092 | // Constructor secundario 1093 | constructor(parent: Person) { 1094 | parent.children.add(this) 1095 | } 1096 | } 1097 | ``` 1098 | 1099 | Si la clase tiene un constructor primario, cada **constructor secundario debe delegar en el constructor primario**, ya sea directamente o indirectamente a través de otro/s constructor/es secundario/s. La delegación en otro constructor de la misma clase se hace usando la palabra clave `'this'`: 1100 | 1101 | ```kotlin 1102 | class Person(val name: String) { // Constructor primario 1103 | 1104 | // Constructor secundario 1105 | // Usamos 'this' para invocar al constructor primario 1106 | constructor(name: String, parent: Person) : this(name) { 1107 | parent.children.add(this) 1108 | } 1109 | } 1110 | ``` 1111 | 1112 | Hay que tenera en cuenta que el código en los bloques de inicialización se convierte efectivamente en parte del constructor primario. La delegación en el constructor primario ocurre como la primera instrucción en el constructor secundario, por lo que el código en todos los bloques de inicialización se ejecuta antes que el constructor secundario. Incluso si la clase no tiene un constructor primario, la delegación todavía ocurre implícitamente y los bloques de inicialización aún se ejecutan antes: 1113 | 1114 | ```kotlin 1115 | class Constructors { 1116 | init { 1117 | println("Init block") // Se ejecuta antes que el constructor secundario 1118 | } 1119 | constructor(i: Int) { 1120 | println("Constructor") 1121 | } 1122 | } 1123 | ``` 1124 | 1125 | La diferencia importante entre los constructores secundarios y primarios es que los parámetros de los constructores primarios pueden definir propiedades, mientras que los parámetros de un constructor secundario siempre son solo parámetros. 1126 | 1127 | Si los parámetros de un constructor primario también son propiedades, serán accesibles a lo largo de todo el ciclo de vida del objeto, al igual que las propiedades normales. Mientras que, si son simples parámetros, obviamente sólo son accesibles dentro del constructor, como cualquier otro parámetro de una función. 1128 | 1129 | #### Propiedades 1130 | 1131 | En Kotlin no se utiliza el concepto de 'campo' cuando hablamos de variables de instancia sino que se emplea el concepto de **propiedades**. 1132 | 1133 | Las propiedades de una clase pueden declararse como mutables _(`var`)_, o de inmutables o de sólo lectura _(`val`)_: 1134 | 1135 | ```kotlin 1136 | class Address { 1137 | var name: String = ... 1138 | var street: String = ... 1139 | var city: String = ... 1140 | var state: String? = ... 1141 | var zip: String = ... 1142 | } 1143 | ``` 1144 | 1145 | Para acceder a las propiedades de una clase usamos el operador punto `'.'` ya que a diferencia de Java no hay que utilizar `getters()` ni `setters()` si hemos definido la propiedad con `'val'` o `'var'`. Para usar la propiedad, simplemente nos referimos a ella por su nombre, como si fuera un campo en Java: 1146 | 1147 | ```kotlin 1148 | fun copyAddress(address: Address): Address { 1149 | val result = Address() // there's no 'new' keyword in Kotlin 1150 | result.name = address.name // accessors are called 1151 | result.street = address.street 1152 | // ... 1153 | return result 1154 | } 1155 | ``` 1156 | 1157 | #### 'Getters()' and 'Setters()' 1158 | 1159 | La sintaxis completa de definición de una propiedad en Kotlin: 1160 | 1161 | ```kotlin 1162 | {var|val} [: ] [= ] 1163 | [] 1164 | [] 1165 | ``` 1166 | 1167 | El inicializador y las funciones `'getter()'` (y `'setter()'` si es una propiedad mutable) son opcionales. El tipo de la propiedad es opcional si puede inferirse desde el inicializador o desde el tipo de retorno del `'getter()'`. 1168 | 1169 | ```kotlin 1170 | var allByDefault: Int? // error: se requiere un inicializador explícito. 1171 | var initialized = 1 // propiedad de tipo Int, getter y setter por defecto 1172 | 1173 | val simple: Int? // propiedad de tipo Int, getter por defecto, debe ser inicializada por el constructor 1174 | val inferredType = 1 // propiedad de tipo Int y getter por defecto 1175 | ``` 1176 | 1177 | Si las funciones `'getter()'` (y `'setter()'` en propiedades mutables) por defecto no son suficientes se puede codificar funciones `'getter()'` o `'setter()'` propias como cualquier otra función. Estas funciones están dentro de la propiedad y por tanto tienen que ser identadas correctamente 1178 | 1179 | ```kotlin 1180 | val isEmpty: Boolean 1181 | get() = this.size == 0 1182 | 1183 | var stringRepresentation: String 1184 | get() = this.toString() 1185 | set(value) { 1186 | setDataFromString(value) // parses the string and assigns values to other properties 1187 | } 1188 | ``` 1189 | 1190 | Nótese que por convención, el nombre del parámetro de la función `'setter()'` es `'value'` pero no es obligatorio y puede escogerse otro nombre. 1191 | 1192 | Las propiedades pueden ser **'private'**, **'protected'**, o **'public'** (visibilidad por defecto). 1193 | 1194 | #### Backing Fields 1195 | 1196 | El campo de respaldo o **_'backing field'_** es un campo generado automáticamente para cualquier propiedad que solo puede usarse dentro de los accesores (getter o setter). 1197 | 1198 | Estará presente solo si utiliza la implementación predeterminada de al menos uno de los accesores, o si un descriptor de acceso personalizado lo hace referencia a través del identificador `'field'`. Este campo de respaldo se usa para evitar la llamada recursiva y por tanto evitar un _'StackOverflowError'_. 1199 | 1200 | Kotlin proporciona automáticamente este campo de respaldo. Se puede hacer referencia a este campo en los accesores utilizando el identificador `'field'`: 1201 | 1202 | ```kotlin 1203 | var counter = 0 // Note: the initializer assigns the backing field directly 1204 | set(value) { 1205 | if (value >= 0) field = value 1206 | } 1207 | ``` 1208 | 1209 | Este campo es necesario ya que el siguiente código genera un _'StackOverflowError'_. Cuando Kotlin encuentra la propiedad 'selectedColor' llama al `'getter()'` correspondiente. Si usamos 'selectedColor' dentro de la definición del propio `'getter()'` es cuando se producen llamadas recursivas que acaban generando un desbordamiento de la pila. Kotlin provee del _'backing field'_ para evitarlo. 1210 | 1211 | ```kotlin 1212 | var selectedColor: Int = someDefaultValue 1213 | get() = selectedColor 1214 | set(value) { 1215 | this.selectedColor = value 1216 | doSomething() 1217 | } 1218 | 1219 | // Código correcto 1220 | var selectedColor: Int = someDefaultValue 1221 | get() = field 1222 | set(value) { 1223 | field = value 1224 | doSomething() 1225 | } 1226 | ``` 1227 | 1228 | #### Constantes en tiempo de compilación 1229 | 1230 | Las propiedades cuyo valor se conoce en el momento de la compilación se pueden marcar como constantes de tiempo de compilación utilizando el modificador `'const'`. Tales propiedades necesitan cumplir los siguientes requisitos: 1231 | 1232 | - Top-level o miembros de un `'objet'` 1233 | 1234 | - Inicializado con un valor de tipo String o un tipo primitivo 1235 | 1236 | - No tener un `'getter()'` propio 1237 | 1238 | Estas propiedades pueden ser utilizadas en anotaciones: 1239 | 1240 | ```kotlin 1241 | const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated" 1242 | 1243 | @Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... } 1244 | ``` 1245 | 1246 | #### Late-Initialized Properties and Variables 1247 | 1248 | Normalmente, las propiedades declaradas con un tipo no nulo deben inicializarse en el constructor. Sin embargo, bastante a menudo esto no es conveniente. Por ejemplo, las propiedades se pueden inicializar mediante la inyección de dependencias, o en el método de configuración de una prueba de unidad. En este caso, no puede proporcionar un inicializador que no sea nulo en el constructor, pero aún así desea evitar las comprobaciones nulas al hacer referencia a la propiedad dentro del cuerpo de una clase. 1249 | 1250 | Para manejar este caso, puede marcar la propiedad con el modificador `'lateinit'`: 1251 | 1252 | ```kotlin 1253 | public class MyTest { 1254 | lateinit var subject: TestSubject 1255 | 1256 | @SetUp fun setup() { 1257 | subject = TestSubject() 1258 | } 1259 | 1260 | @Test fun test() { 1261 | subject.method() // dereference directly 1262 | } 1263 | } 1264 | ``` 1265 | 1266 | Para usar este modificador hay que cumplir ciertos requisitos: 1267 | 1268 | - Se puede usar únicamente en las propiedades `'var'` declaradas dentro del cuerpo de una clase. Por tanto no se puede usar en propiedades declaradas en el constructor principal. 1269 | 1270 | - La propiedad no tiene un `'getter()'` o `'setter()'` personalizado. 1271 | 1272 | Acceder a una propiedad antes de que haya sido inicializada lanzará una _'UninitializedPropertyAccessException'_. 1273 | 1274 | #### Member Functions 1275 | 1276 | Una función miembro es una función que se define dentro de una clase, objeto o interfaz. Las funciones miembro se invocan con el operador `'.'`: 1277 | 1278 | ```kotlin 1279 | class Sample() { 1280 | fun foo() { 1281 | print("Foo") 1282 | } 1283 | } 1284 | 1285 | Sample().foo() // crea una instancia de 'Sample' e invoca el método 'foo' 1286 | ``` 1287 | 1288 | ### Herencia 1289 | 1290 | La herencia es fundamental para la programación orientada a objetos. Nos permite crear nuevas clases que reutilizan, amplían y/o modifican el comportamiento de los preexistentes. La clase preexistente se llama **superclase** (o clase base), y la clase nueva que estamos creando se llama **clase derivada**. Una clase derivada obtendrá implícitamente todos los campos, propiedades y métodos de la superclase (y de la superclase de la superclase si es el caso). 1291 | 1292 | Hay una restricción en cuanto a cuántas clases podemos heredar; en una JVM, solo puede tener **una clase base**. Pero se puede heredar de múltiples interfaces. 1293 | 1294 | La herencia es transitiva. Si la clase C se deriva de la clase B y esa clase B se deriva de una clase A dada, entonces la clase C es una clase derivada de A. 1295 | 1296 | Todas las clases en Kotlin tienen una superclase común `'Any'`, que es la superclase predeterminada para una clase sin supertipos declarados. Esta clase `'Any'` tiene unos pocos métodos básicos como `equals()` o `toString()`: 1297 | 1298 | ```kotlin 1299 | // Hereda de 'Any' implicitamente 1300 | class Example 1301 | ``` 1302 | 1303 | Para declarar que una clase hereda de una clase base, colocamos el tipo de la clase base después de dos puntos en el encabezado de la clase derivada. Por defecto en Kotlin las clases están cerradas a la herencia, es decir, son `'final'`. Para permitir que una clase sea heredada, hay que utilizar la palabra clave `'open'`. 1304 | 1305 | ```kotlin 1306 | open class Base(p: Int) 1307 | 1308 | // the derived class has a primary constructor 1309 | class DerivedWithConstructor(p: Int) : Base(p) 1310 | ``` 1311 | 1312 | Si la clase derivada tiene un constructor primario, la clase base puede (y debe) inicializarse allí mismo, utilizando los parámetros del constructor primario. 1313 | 1314 | Si la clase no tiene un constructor primario, entonces cada constructor secundario tiene que inicializar el tipo base usando la palabra clave `'super'`, o delegar a otro constructor que haga eso. Tenga en cuenta que en este caso, diferentes constructores secundarios pueden llamar a diferentes constructores de la clase base: 1315 | 1316 | ```kotlin 1317 | open class Base(p: Int) { 1318 | constructor(p: Int, q: Int): this(p) 1319 | } 1320 | 1321 | class DerivedWithoutConstructor : Base { 1322 | // calling the base constructor with super() 1323 | constructor(p: Int) : super(p) 1324 | } 1325 | ``` 1326 | 1327 | #### Sobreescritura de métodos 1328 | 1329 | Kotlin requiere **anotaciones explícitas** para la sobreescritura de funciones miembro. 1330 | 1331 | Para que una función pueda ser sobreescrita se utiliza la palabra clave `'open'` delante del nombre de la función. Dado que las clases son **finales** en Kotlin, sólo podemos utilizar la palabra clave `'open'` en funciones miembro de clases que también hayan sido definidas como `'open'`. 1332 | 1333 | Para indicar que una función en la clase derivada sobreescribe una función de la clase padre se utiliza la palabra clave `'override'` delante del nombre de la función. De esta forma le indicamos al compilador que esta función sobreescribe una función de la clase padre y puede realizar las comprobaciones en tiempo de compilación. 1334 | 1335 | Una función con la palabra clave `'override'` también es `'open'` por definición y puede ser sobreescrita por las subclases sucesivas. Es posible marcar una función `'override'` con la palabra clave `'final'` para evitar que sea sobreescrita. 1336 | 1337 | ```kotlin 1338 | open class Base { 1339 | open fun v() { ... } 1340 | open fun x(p: Int) { ... } 1341 | fun nv() { ... } 1342 | } 1343 | 1344 | class Derived: Base() { 1345 | override fun v() { ... } 1346 | 1347 | final override fun x(p: Int) { ... } // Restringir la sobreescritura 1348 | } 1349 | ``` 1350 | 1351 | En Kotlin, la herencia está regulada por la siguiente regla: si una clase hereda varias implementaciones del mismo miembro de sus superclases inmediatas, debe invalidar este miembro y proporcionar su propia implementación. Para denotar el supertipo del cual se toma la implementación heredada, usamos la palaba clave `'super'` calificado por el nombre de supertipo entre paréntesis angulares, por ejemplo, `super`: 1352 | 1353 | ```kotlin 1354 | open class A { 1355 | open fun f() { print("A") } 1356 | fun a() { print("a") } 1357 | } 1358 | 1359 | interface B { 1360 | fun f() { print("B") } // interface members are 'open' by default 1361 | fun b() { print("b") } 1362 | } 1363 | 1364 | class C() : A(), B { 1365 | // El compilador requiere que 'f()' sea sobreescrito para eliminar la ambigüedad 1366 | override fun f() { 1367 | super.f() // call to A.f() 1368 | super.f() // call to B.f() 1369 | } 1370 | } 1371 | ``` 1372 | 1373 | #### Sobreescritura de propiedades 1374 | 1375 | La sobreescritura de propiedades funciona de manera similar a la sobreescritura de métodos. 1376 | 1377 | Las propiedades declaradas en una superclase que luego se vuelven a declarar en una clase derivada deben ir precedidas por la palabra clave `'override'` y deben tener un tipo compatible. También se puede usar la palabra clave `'override'` como parte de la declaración de una propiedad en un constructor primario. 1378 | 1379 | Cada propiedad declarada puede ser sobreescrita por una propiedad con un inicializador o por una propiedad con un método `'getter()'` 1380 | 1381 | ```kotlin 1382 | open class Foo { 1383 | open val x: Int get() { ... } 1384 | } 1385 | 1386 | class Bar : Foo() { 1387 | override val x: Int = ... 1388 | } 1389 | 1390 | interface Foo1 { 1391 | val count: Int 1392 | } 1393 | 1394 | class Bar1(override val count: Int) : Foo1 1395 | ``` 1396 | 1397 | #### Orden de inicialización 1398 | 1399 | Durante la construcción de una nueva instancia de una clase derivada, **la inicialización de la clase base se realiza como primer paso** (precedida solo por la evaluación de los argumentos para el constructor de la clase base) y, por lo tanto, ocurre antes de que se ejecute la lógica de inicialización de la clase derivada. 1400 | 1401 | Por lo tanto, durante la inicialización de las propiedades de la clase base las propiedades de la clase derivada aún no se han inicializado. Si alguna de esas propiedades se utilizan (de forma directa o indirecta) en la inicialización de la clase base se pueden producir comportamientos extraños o errores en tiempo de ejecución. 1402 | 1403 | ```kotlin 1404 | open class Base(val name: String) { 1405 | init { 1406 | println("Initializing Base") 1407 | } 1408 | 1409 | open val size: Int = 1410 | name.length.also { println("Initializing size in Base: $it") } 1411 | } 1412 | 1413 | class Derived(name: String, val lastName: String) : Base(name.capitalize().also { println("Argument for Base: $it") }) { 1414 | init { 1415 | println("Initializing Derived") 1416 | } 1417 | 1418 | override val size: Int = 1419 | (super.size + lastName.length).also { println("Initializing size in Derived: $it") } 1420 | } 1421 | 1422 | // Argument for Base: Hello 1423 | // Initializing Base 1424 | // Initializing size in Base: 5 1425 | // Initializing Derived 1426 | // Initializing size in Derived: 10 1427 | ``` 1428 | 1429 | #### Invocar la implementación de la superclase 1430 | 1431 | El código en una clase derivada puede llamar a funciones en la superclase e implementaciones de accesores de propiedades usando la palabra clave `'super'`: 1432 | 1433 | ```kotlin 1434 | open class Foo { 1435 | open fun f() { println("Foo.f()") } 1436 | open val x: Int get() = 1 1437 | } 1438 | 1439 | class Bar : Foo() { 1440 | override fun f() { 1441 | super.f() // Calling the super function 1442 | println("Bar.f()") 1443 | } 1444 | override val x: Int get() = super.x + 1 1445 | } 1446 | ``` 1447 | 1448 | ### Clases abstractas 1449 | 1450 | Kotlin admite **clases abstractas** al igual que Java. Una clase abstracta es una clase con métodos marcados como abstractos y que por tanto no puede ser instanciada. Si una clase tiene uno o varios métodos abstractos es una clase abstracta y se indica con la palabra clave `'abstract'`. 1451 | 1452 | La subclase concreta de una clase abstracta deberá implementar todos los métodos y propiedades definidos en la clase abstracta; de lo contrario, también será considerada como una clase abstracta. 1453 | 1454 | ```kotlin 1455 | open class Person { 1456 | open fun fullName(): String { ... } 1457 | } 1458 | 1459 | abstract class Employee (val firstName: String, val lastName: String): Person() { 1460 | // Variable de intancia en una clase abstracta 1461 | val propFoo: String = "bla bla" 1462 | 1463 | abstract fun earnings(): Double 1464 | 1465 | // Podemos tener métodos con implementación por defecto 1466 | override fun fullName(): String { 1467 | return lastName + " " + firstName; 1468 | } 1469 | } 1470 | ``` 1471 | 1472 | Las clases abstractas pueden contener métodos con implementación por defecto como cualquier otra clase. Las subclases de la clase abstracta pueden sobreescribir la implementación predeterminada de un método pero solo si el método tiene el modificador `'open'`. Los métodos marcados como `'abstract'` también son `'open'` por defecto. Las clases abstractas también pueden definir variables de instancia al contrario que pasa con las interfaces. 1473 | 1474 | ### Interfaces 1475 | 1476 | Las interfaces en Kotlin son muy similares a Java 8. Pueden contener declaraciones de métodos abstractos, así como implementaciones de métodos. Lo que los diferencia de las clases abstractas es que las interfaces no pueden almacenar el estado, es decir, no pueden tener variables de instancia. Pueden tener propiedades, pero estas deben ser abstractas o proporcionar implementaciones de accesores. 1477 | 1478 | Una interfaz se define usando la palabra clave `'interface'`. Un método en una interfaz es abstracto por defecto si no se proporciona una implementación. 1479 | 1480 | ```kotlin 1481 | interface MyInterface { 1482 | fun bar() // abstract by default 1483 | fun foo() { 1484 | // optional body 1485 | } 1486 | } 1487 | ``` 1488 | 1489 | Una clase u objeto pueden implementar una o varias interfaces: 1490 | 1491 | ```kotlin 1492 | class Child : MyInterface { 1493 | override fun bar() { 1494 | // body 1495 | } 1496 | } 1497 | ``` 1498 | 1499 | En una interfaz se pueden declarar propiedades. Una propiedad declarada en una interfaz puede ser abstracta o puede proporcionar implementaciones para el `'getter()'` o `'setter()'`. Las propiedades declaradas en interfaces no pueden tener _'backing fields'_ y, por lo tanto, los accesores declarados en interfaces no pueden hacer referencia a ellos. 1500 | 1501 | ```kotlin 1502 | interface MyInterface { 1503 | val prop: Int // abstract 1504 | 1505 | val propertyWithImplementation: String 1506 | get() = "foo" 1507 | 1508 | fun foo() { 1509 | print(prop) 1510 | } 1511 | } 1512 | 1513 | class Child : MyInterface { 1514 | override val prop: Int = 29 1515 | } 1516 | ``` 1517 | 1518 | Una interfaz puede derivar de otras interfaces y, por lo tanto, proporcionar implementaciones para sus miembros y declarar nuevas funciones y propiedades. Naturalmente, las clases que implementen dicha interfaz solo tienen que definir las implementaciones que faltan: 1519 | 1520 | ```kotlin 1521 | interface Named { 1522 | val name: String 1523 | } 1524 | 1525 | interface Person : Named { 1526 | val firstName: String 1527 | val lastName: String 1528 | override val name: String get() = "$firstName $lastName" 1529 | } 1530 | 1531 | data class Employee( 1532 | // implementing 'name' is not required 1533 | override val firstName: String, 1534 | override val lastName: String, 1535 | val position: Position 1536 | ) : Person 1537 | ``` 1538 | 1539 | En el caso de clases que hereden de varias interfaces, para evitar ambigüedades la subclase deberá proporcionar implementaciones tanto para métodos que tienen una implementación en una de las interfaces como en métodos que tiene implementaciones en varias interfaces. 1540 | 1541 | ```kotlin 1542 | interface A { 1543 | fun foo() { print("A") } 1544 | fun bar() // abstract 1545 | } 1546 | 1547 | interface B { 1548 | fun foo() { print("B") } 1549 | fun bar() { print("bar") } 1550 | } 1551 | 1552 | class C : A { 1553 | override fun bar() { print("bar") } 1554 | } 1555 | 1556 | // la clase 'D' tieen que implementar tanto foo() como bar() 1557 | class D : A, B { 1558 | override fun foo() { 1559 | super.foo() 1560 | super.foo() 1561 | } 1562 | 1563 | override fun bar() { 1564 | super.bar() 1565 | } 1566 | } 1567 | ``` 1568 | 1569 | ### Visibilidad 1570 | 1571 | Las clases, objetos, interfaces, constructores, funciones, propiedades y sus _'setters'_ pueden tener modificadores de visibilidad. (Los _'setters'_ siempre tienen la misma visibilidad que la propiedad). 1572 | 1573 | - **Public** - Este es el valor predeterminado, y se puede acceder a cualquier clase, función, propiedad, interfaz u objeto que tenga este modificador desde cualquier lugar. 1574 | 1575 | - **Private** - Se puede acceder a una función, interfaz o clase de nivel superior que se declara como privada solo dentro del mismo archivo. 1576 | 1577 | Cualquier función o propiedad que se declare privada dentro de una clase, objeto o interfaz solo puede ser visible para otros miembros de esa misma clase, objeto o interfaz. 1578 | 1579 | Un constructor privado debe usar la palabra clave `'constructor'`. Si un constructor es marcado como privado no se puede instanciar un objeto con ese constructor. 1580 | 1581 | ```kotlin 1582 | class Car private constructor(val name: String, val plateNo: String) { 1583 | // .... 1584 | } 1585 | ``` 1586 | 1587 | - **Protected** - Solo se puede aplicar a propiedades o funciones dentro de una clase, objeto o interfaz, no se puede aplicar a funciones, clases o interfaces de nivel superior. Las propiedades o funciones con este modificador solo son accesibles dentro de la clase que lo define y cualquier subclase. 1588 | 1589 | - **Internal** - En un proyecto que tiene un módulo (módulo Gradle o Maven), una clase, objeto, interfaz o función especificada con este modificador dentro de ese módulo solo es accesible desde ese módulo. 1590 | 1591 | ### Data classes 1592 | 1593 | Las **Data classes** son una forma concisa de crear clases que solo contienen datos. Estas clases se definen con la palabra clave `'data'`. 1594 | 1595 | ```kotlin 1596 | data class User(val name: String, val age: Int) 1597 | ``` 1598 | 1599 | De forma automática el compilador crear los métodos `hashCode()`, `equals()`, `copy()` y `toString()` a partir de todas las propiedades declaradas en el constructor primario. También se generan las funciones `componentN()` que corresponden a las propiedades declaradas en orden en el constructor primario. 1600 | 1601 | Para evitar comportamientos extraños estas clases deben **cumplir ciertos requisitos**: 1602 | 1603 | - El constructor primario necesita tener al menos un parámetro. 1604 | 1605 | - Todos los parámetros del constructor primario estarán marcados como `'val'` o `'var'`. 1606 | 1607 | - Una _'data class'_ no puede ser `'abstract'`, `'open'`, `'sealed'` o `'inner'`. 1608 | 1609 | - (Antes de 1.1) Las _'data classes'_ no pueden extender de otras clases (pero pueden implementar interfaces). 1610 | 1611 | El compilador sólo tiene en cuenta las propiedades declaradas en el constructor primario a la hora de generar los métodos de forma automática. Por tanto, para excluir propiedades se deben declarar en el cuerpo de la clase. 1612 | 1613 | ```kotlin 1614 | data class DataClassExample(val x: Int, val y: Int, val z: Int) { 1615 | // Propiedad excluida 1616 | var xx; Int = 0 1617 | } 1618 | 1619 | val fooData = DataClassExample(1, 2, 4) 1620 | val fooCopy = fooData.copy(y = 100) 1621 | 1622 | // El formato de 'toString()' es el mismo 'ClassName(prop=xx, prop=yy, ....)' 1623 | println(fooData) // => DataClassExample(x=1, y=2, z=4) 1624 | println(fooCopy) // => DataClassExample(x=1, y=100, z=4) 1625 | ``` 1626 | 1627 | El compilador genera la función `copy()` que permite copiar un objeto y en caso necesario, crear la copia alterando algunas de sus propiedades y manteniendo el resto. 1628 | 1629 | ```kotlin 1630 | data class User(val name: String, val age: Int) 1631 | 1632 | // Función 'copy()' generada automáticamente 1633 | // fun copy(name: String = this.name, age: Int = this.age) = User(name, age) 1634 | 1635 | val jack = User(name = "Jack", age = 1) 1636 | 1637 | // Copiamos el objeto pero modificando la propiedad 'age' 1638 | val olderJack = jack.copy(age = 2) 1639 | ``` 1640 | 1641 | Las funciones `componentN()` permite desestructurar las propiedades: 1642 | 1643 | ```kotlin 1644 | val jane = User("Jane", 35) 1645 | val (name, age) = jane 1646 | println("$name, $age years of age") // => Jane, 35 years of age 1647 | ``` 1648 | 1649 | Cada tipo se deriva de `'Any'`, que viene con una declaración de método `'hashCode()'`. Esto es el equivalente de un método `'hashCode()'` de clase _'Object'_ de Java. Este método es importante cuando se insertan instancias del objeto en colecciones, como un mapa. Al implementar este método, se debe cumplir con una serie de requisitos: 1650 | 1651 | 1. Cuando se invoque en el mismo objeto más de una vez durante el tiempo de ejecución, el método `'hashCode()'` debe devolver constantemente el mismo valor, dado que el objeto no se modificó. 1652 | 1653 | 2. Si para dos objetos el método `'equals()'` devuelve true, entonces llamar al método `'hashCode()'` en cada uno de ellos debería devolver el mismo valor entero. 1654 | 1655 | 3. Si dos objetos no son iguales, es decir, que el método `'equals()'` devuelve false cuando se comparan, no es un requisito que cada método `'hashCode()'` del objeto devuelva valores distintos. Sin embargo, producir un entero distinto para objetos desiguales podría mejorar el rendimiento de las colecciones basadas en 'hash'. 1656 | 1657 | Las _'data classes'_ son un forma compacta y legible de devolver dos o más valores de una función. Otra alternativa, menos legible, es utilizar el tipo `'Pair'` o `'Triple'` proporcionado por Kotlin: 1658 | 1659 | ```kotlin 1660 | data class Result(val result: Int, val status: Boolean) 1661 | 1662 | fun checkStatus() = Result(10, true) // función que retorna un tipo 'Result' 1663 | 1664 | val (result, status) = checkStatus() // usamos la desestructuración de datos para acceder a los datos 1665 | ``` 1666 | 1667 | ### Sealed classes 1668 | 1669 | En Kotlin una **_'sealed class'_** es una clase abstracta (no se puede crear instancias) que otras clases pueden extender. Estas subclases se definen dentro del cuerpo de la _'sealed class'_, en el mismo archivo por lo que podemos conocer todas las subclases posibles simplemente viendo el archivo. 1670 | 1671 | Las _'sealed class'_ se utilizan para representar jerarquías de clases restringidas, de forma que una clase solo pueda heredar de un conjunto limidado de tipos. Son, en cierto sentido, una extensión de las clases de enumeración. 1672 | 1673 | - Podemos agregar el modificador `'abstract'`, pero esto es redundante porque estas clases son abstractas por defecto. 1674 | 1675 | - No pueden tener el modificador `'open'` ni `'final'`. 1676 | 1677 | - Podemos declarar clases de datos y objetos como subclases a una _'sealed class'_ (aún deben declararse en el mismo archivo). 1678 | 1679 | - No pueden tener constructores públicos ya que sus constructores son privados de forma predeterminada. 1680 | 1681 | ```kotlin 1682 | // shape.kt 1683 | 1684 | sealed class Shape 1685 | 1686 | class Circle : Shape() 1687 | class Triangle : Shape() 1688 | class Rectangle: Shape() 1689 | ``` 1690 | 1691 | ### Generics 1692 | 1693 | **_'Covariance'_** y **_'contravariance'_** son términos que hacen referencia a la capacidad de usar un tipo más derivado (más específico) o menos derivado (menos específico) que el indicado originalmente. Los parámetros de tipo genérico admiten estos términos para proporcionar mayor flexibilidad a la hora de asignar y usar tipos genéricos. Cuando se hace referencia a un sistema de tipos, se definen como: 1694 | 1695 | - **_'Covariance'_** -> Permite usar un tipo más derivado que el especificado originalmente. Puede asignar una instancia de `Class` a una variable de tipo `Class`. 1696 | 1697 | - **_'Contravariance'_** -> Permite usar un tipo más genérico (menos derivado) que el especificado originalmente. Puede asignar una instancia de `Class` a una variable de tipo `Class`. 1698 | 1699 | -* **_'Invariance'_** -> Significa que solo se puede usar el tipo especificado originalmente. Así, un parámetro de tipo genérico invariable no es covariante ni contravariante. No se puede asignar una instancia de `List` a una variable de tipo `List` o viceversa. 1700 | 1701 | Al igual que en Java, en Kotlin las clases pueden tener tipos con parámetros. 1702 | 1703 | ```kotlin 1704 | class Box(t: T) { 1705 | var value = t 1706 | } 1707 | ``` 1708 | 1709 | En general, para crear una instancia de una clase genérica tenemos que proveer el tipo a la clase: 1710 | 1711 | ```kotlin 1712 | val box: Box = Box(1) 1713 | ``` 1714 | 1715 | Si los parámetros se pueden inferir, como por ejemplo de los argumentos del constructor o por algún otro medio, se pueden omitir los argumentos de tipo: 1716 | 1717 | ```kotlin 1718 | val box = Box(1) // '1' tiene tipo Int así que el compilador infiere el tipo "Box" 1719 | ``` 1720 | 1721 | #### La palabra clave 'out' 1722 | 1723 | Digamos que queremos crear una clase de productor que producirá un resultado de algún tipo 'T'. A veces; queremos asignar ese valor producido a una referencia que es de un supertipo del tipo 'T'. 1724 | 1725 | Para lograr eso usando Kotlin, necesitamos usar la palabra clave `'out'` en el tipo genérico. Esto significa que podemos asignar esta referencia a cualquiera de sus supertipos. El valor de salida solo puede ser producido por la clase dada pero no consumido: 1726 | 1727 | ```kotlin 1728 | class ParameterizedProducer(private val value: T) { 1729 | fun get(): T { 1730 | return value 1731 | } 1732 | } 1733 | 1734 | val a = ParameterizedProducer("string") // ParameterizedProducer 1735 | val x: ParameterizedProducer = a // Correcto 1736 | 1737 | val b = ParameterizedProducer(10) // ParameterizedProducer 1738 | val y: ParameterizedProducer = b // Correcto 1739 | val z: ParameterizedProducer = b // ¡Error de compilación! 1740 | ``` 1741 | 1742 | #### La palabra clave 'in' 1743 | 1744 | A veces, tenemos una situación opuesta, lo que significa que tenemos una referencia de tipo T y queremos poder asignarla al subtipo de T. 1745 | 1746 | Podemos usar la palabra clave `'in'` en el tipo genérico si queremos asignarlo a la referencia de su subtipo. La palabra clave `'in'` solo se puede utilizar en el tipo de parámetro que se consume, no se produce: 1747 | 1748 | ```kotlin 1749 | class ParameterizedConsumer { 1750 | fun toString(value: T): String { // 'toString()' will only be consuming a value of type T. 1751 | return value.toString() 1752 | } 1753 | } 1754 | 1755 | val a = ParameterizedConsumer() 1756 | 1757 | val b: ParameterizedConsumer = a // Correcto 1758 | val c: ParameterizedConsumer = a // Correcto 1759 | val d: ParameterizedConsumer = a // ¡Error de compilación! 1760 | ``` 1761 | 1762 | #### Star projections 1763 | 1764 | Hay situaciones en las que no es importante el tipo específico de un valor. Para ello usamos el operador `'*'` o _'star projection'_: 1765 | 1766 | ```kotlin 1767 | fun printArray(array: Array<*>) { 1768 | array.forEach { println(it) } 1769 | } 1770 | 1771 | // Podemos pasar una matriz de cualquier tipo al método 'printArray()' 1772 | printArray(arrayOf(1,2,3)) 1773 | 1774 | printArray(arrayOf("hello", "World!!", 5)) 1775 | ``` 1776 | 1777 | #### Generic functions 1778 | 1779 | Las funciones también pueden ser genéricas en los tipos que utilizan. Esto permite escribir una función que puede funcionar con cualquier tipo, en lugar de solo un tipo específico. Para ello, definimos los parámetros de tipo en la firma de función. 1780 | 1781 | ```kotlin 1782 | fun choose(t1: T, t2: T, t3: T): T { 1783 | return when (Random().nextInt(3)) { 1784 | 0 -> t1 1785 | 1 -> t2 1786 | else -> t3 1787 | } 1788 | } 1789 | 1790 | // Podemos usar esta función con enteros. Si el compilador puede inferir el tipo se puede omitir. 1791 | val r = choose(5, 7, 9) 1792 | val r = choose(5, 7, 9) 1793 | 1794 | // También es válido usar la función con Strings 1795 | val s = choose("BMW", "Audi", "Ford") 1796 | val s = choose("BMW", "Audi", "Ford") 1797 | ``` 1798 | 1799 | #### Generic constraints 1800 | 1801 | El conjunto de todos los tipos posibles que pueden sustituirse por un parámetro de tipo dado puede estar restringido por restricciones genéricas. 1802 | 1803 | El tipo más común de restricción es un límite superior que corresponde a la palabra clave de extensión de Java: 1804 | 1805 | ```kotlin 1806 | fun > sort(list: List) { ... } 1807 | 1808 | sort(listOf(1, 2, 3)) // OK. Int is a subtype of Comparable 1809 | sort(listOf(HashMap())) // Error: HashMap is not a subtype of Comparable> 1810 | ``` 1811 | 1812 | El límite superior predeterminado (si no se especifica) es `'Any?'`. 1813 | 1814 | ### Nested classes 1815 | 1816 | Al igual que las funciones, Kotlin permite las clases internas, es decir, clases definidas dentro de otra clase. Son equivalentes a las clases internas estáticas en Java. 1817 | 1818 | ```kotlin 1819 | class OuterClass { 1820 | 1821 | class NestedClass { 1822 | fun nestedClassFunc() { } 1823 | } 1824 | } 1825 | 1826 | val nestedClass = OuterClass.NestedClass().nestedClassFunc() 1827 | ``` 1828 | 1829 | ### Inner class 1830 | 1831 | Las clases internas, por otro lado, pueden hacer referencia a la clase externa en la que se declaró. Para crear una clase interna, colocamos la palabra clave `'inner'` antes de la palabra clave `'class'`. 1832 | 1833 | ```kotlin 1834 | class OuterClass() { 1835 | val oCPropt: String = "Yo" 1836 | 1837 | inner class InnerClass { 1838 | fun innerClassFunc() { 1839 | val outerClass = this@OuterClass 1840 | print(outerClass.oCPropt) 1841 | } 1842 | } 1843 | } 1844 | val demo = OuterClass().InnerClass().innerClassFunc() // => yo 1845 | ``` 1846 | 1847 | ### Enumeraciones 1848 | 1849 | **Las clases de enumeración son similares a los tipos _'enum'_ de Java**. El uso más básico de las clases de enumeración es la implementación de enumeraciones de tipos seguros. Cada constante de la enumeración es un objeto. Las constantes de la enumeración están separadas por comas. 1850 | 1851 | ```kotlin 1852 | enum class Country { 1853 | Spain, France, Portugal 1854 | } 1855 | ``` 1856 | 1857 | Las enumeraciones pueden tener constructor: 1858 | 1859 | ```kotlin 1860 | enum class Direction(val angle: Int) { 1861 | North(90), West(180), South(270), East(0) 1862 | } 1863 | ``` 1864 | 1865 | En Kotlin las constantes de la enumeración pueden declarar sus propias clases anónimas con sus métodos correspondientes, así como sobreescribir métodos primarios. 1866 | 1867 | Si la enumeración define algún miembro, debe separar las definiciones de constantes de enumeración de las definiciones de miembros con un punto y coma, al igual que en Java. 1868 | 1869 | ```kotlin 1870 | enum class ProtocolState { 1871 | WAITING { 1872 | override fun signal() = TALKING 1873 | }, 1874 | 1875 | TALKING { 1876 | override fun signal() = WAITING 1877 | }; 1878 | 1879 | abstract fun signal(): ProtocolState 1880 | } 1881 | ``` 1882 | 1883 | En Kotlin las enumeraciones disponen de forma predeterminada de los métodos: 1884 | 1885 | - `EnumClass.valueOf(value: String): EnumClass` -> Devuelve la constante de enumeración por su nombre. Lanza un _'IllegalArgumentException'_ si no existe la constante. 1886 | 1887 | - `EnumClass.values(): Array` -> Retorna un array con las constantes de enumeración. 1888 | 1889 | Además de los métodos las instancias de enumeración vienen con dos propiedades predefinidas. Uno es `'name'` de tipo 'String' y el segundo es `'ordinal'` de tipo 'Int' para obtener la posición de la constante dentro de la enumeración, teniendo en cuenta que empiezan por 0: 1890 | 1891 | ```kotlin 1892 | enum class Country { 1893 | Spain, France, Portugal 1894 | } 1895 | 1896 | println(Country.Spain) // => Spain 1897 | println(Country.valueOf("Spain")) // => Spain 1898 | 1899 | println(Country.Portugal.name) // => Portugal 1900 | println(Country.France.ordinal) // => 1 1901 | 1902 | fun countries() { 1903 | for (country in Country.values()) { 1904 | println("Country: $country") 1905 | } 1906 | } 1907 | ``` 1908 | 1909 | ### Objects 1910 | 1911 | Los objetos son muy similares a las clases. A veces necesitamos crear un objeto con una ligera modificación de alguna clase, sin declarar explícitamente una nueva subclase para ello. Java maneja este caso con clases internas anónimas. Kotlin generaliza ligeramente este concepto con _'object expressions'_ y _'objects declarations'_. 1912 | 1913 | Estas son algunas de las características de los objetos en Kotlin: 1914 | 1915 | - Pueden tener propiedades, métodos y un bloque init. 1916 | 1917 | - Estas propiedades o métodos pueden tener modificadores de visibilidad. 1918 | 1919 | - No pueden tener constructores (primarios o secundarios). 1920 | 1921 | - Pueden extender otras clases o implementar una interfaz. 1922 | 1923 | Hay importantes diferencias semánticas entre un _'object expression'_ y un _'object declaration'_: 1924 | 1925 | - Los _'object expression'_ se ejecutan (y se inicializan) inmediatamente, donde se usan. 1926 | 1927 | - Los _'object declaration'_ se inicializan cuando se accede por primera vez. 1928 | 1929 | - Por su parte, un _'companion object'_ se inicializa cuando se cargala clase correspondiente. 1930 | 1931 | ### Objects expressions 1932 | 1933 | Para crear un objeto de una clase anónima que hereda de algún tipo (o tipos), escribimos: 1934 | 1935 | ```kotlin 1936 | fun countClicks(window: JComponent) { 1937 | var clickCount = 0 1938 | var enterCount = 0 1939 | 1940 | window.addMouseListener(object : MouseAdapter() { 1941 | override fun mouseClicked(e: MouseEvent) { 1942 | clickCount++ 1943 | } 1944 | 1945 | override fun mouseEntered(e: MouseEvent) { 1946 | enterCount++ 1947 | } 1948 | }) 1949 | // ... 1950 | } 1951 | ``` 1952 | 1953 | #### Objects declarations 1954 | 1955 | Colocamos la palabra clave `'object'` antes del nombre del objeto que queremos crear. De hecho, estamos creando un **SINGLETON** cuando creamos objetos en Kotlin usando esta construcción ya que solo existe una instancia de un objeto. 1956 | 1957 | ```kotlin 1958 | object ObjectExample { 1959 | val baseUrl: String = "http://www.myapi.com/" 1960 | fun hello(): String { 1961 | return "Hello" 1962 | } 1963 | } 1964 | 1965 | println(ObjectExample.hello()) // => Hello 1966 | 1967 | fun useObject() { 1968 | ObjectExample.hello() // => Hello 1969 | val someRef: Any = ObjectExample // Usamos el nombre de los objetos tal como son 1970 | } 1971 | ``` 1972 | 1973 | Al igual que una declaración de variable, una declaración de objeto no es una expresión y no se puede utilizar en el lado derecho de una declaración de asignación. 1974 | 1975 | Los objetos en Kotlin pueden utilizarse también para crear constantes. 1976 | 1977 | ```kotlin 1978 | object APIConstants { 1979 | val baseUrl: String = "http://www.myapi.com/" 1980 | } 1981 | ``` 1982 | 1983 | ##### Companion objects 1984 | 1985 | Los _'companion objects'_ son un tipo de _'object declaration'_. Como Kotlin no admite clases, métodos o propiedades estáticas como las que tenemos en Java, Kotlin provee los _'companion objects'_. Estos objetos son básicamente un objeto que pertenece a una clase que se conoce como la clase complementaria del objeto. Este objeto se indica con la palabra clave `'companion'`. 1986 | 1987 | Similar a los métodos estáticos en Java, un _'companion object'_ no está asociado con una instancia de clase, sino con la propia clase. 1988 | 1989 | Se puede llamar a los miembros del _'companion object'_ usando simplemente el nombre de la clase como el calificador, como si fuera un método estático. 1990 | 1991 | Un _'companion object'_ puede tener nombre que facilitará el ser invocado desde Java aunque es opcional. 1992 | 1993 | ```kotlin 1994 | class Person private constructor(var firstName: String, var lastName: String) { 1995 | 1996 | // Podemos omitir el nombre del objeto 1997 | companion object { 1998 | var count: Int = 0 1999 | fun create(firstName: String, lastName: String): Person = Person(firstName, lastName) 2000 | 2001 | // Podemos tener bloques 'init' dentro de un 'companion object' 2002 | init { 2003 | println("Person companion object created") 2004 | } 2005 | } 2006 | } 2007 | val person = Person.create("John", "Doe") 2008 | 2009 | class MyClass { 2010 | 2011 | fun sayHello() = println("hello") 2012 | 2013 | // Objeto con el nombre 'Factory' y que utilizaremos como 'Factory Pattern' 2014 | companion object Factory { 2015 | fun create(): MyClass = MyClass() 2016 | 2017 | fun sayHelloFromCompanion() = MyClass().sayHello() // Podemos acceder a miembros de la clase 2018 | } 2019 | } 2020 | 2021 | val myClass = MyClass.create() 2022 | MyClass().sayHello() // incorrecto 2023 | MyClass.Factory.sayHelloFromCompanion() // Invocar un método del 'companion' 2024 | ``` 2025 | 2026 | ## Other 2027 | 2028 | ### Destructuring data 2029 | 2030 | Los objetos pueden ser desestructurados en múltiples variables. Esta sintaxis se llama **declaración de desestructuración**. Una declaración de desestructuración crea múltiples variables a la vez. 2031 | 2032 | ```kotlin 2033 | val (a, b, c) = fooCopy 2034 | println("$a $b $c") // => 1 100 4 2035 | ``` 2036 | 2037 | Desestructurando en un bucle `'for'`: 2038 | 2039 | ```kotlin 2040 | for ((a, b, c) in listOf(fooData)) { 2041 | println("$a $b $c") // => 1 100 4 2042 | } 2043 | 2044 | val mapData = mapOf("a" to 1, "b" to 2) 2045 | // Map.Entry is destructurable as well 2046 | for ((key, value) in mapData) { 2047 | println("$key -> $value") 2048 | } 2049 | ``` 2050 | 2051 | ### Colecciones 2052 | 2053 | Kotlin proporciona su API de colecciones como una biblioteca estándar construida sobre la API de colecciones de Java como _'ArrayList'_, _'Maps'_, etc... Kotlin tiene dos variantes de colecciones: **mutables** e **inmutables**. Una colección mutable nos brinda la capacidad de modificar una colección ya sea agregando, eliminando o reemplazando un elemento. Las colecciones inmutables no se pueden modificar y no tienen estos métodos de ayuda. 2054 | 2055 | #### Lists - [Inmutable] 2056 | 2057 | **Una lista es una colección ordenada de elementos**. Esta es una colección popular ampliamente utilizada. 2058 | 2059 | Podemos crear una **lista inmutable** usando la función `listOf()`. Los elementos no se pueden agregar ni eliminar. 2060 | 2061 | ```kotlin 2062 | val fooList = listOf("a", "b", "c", 1, false) 2063 | val numbers: List = listOf(1, 2, 3, 4) 2064 | val emptyList: List = emptyList() // lista vacía 2065 | val nonNullsList: List = listOfNotNull(2, 45, 2, null, 5, null) // lista de valores no nulos 2066 | 2067 | println(fooList.size) // => 3 2068 | println(fooList.first()) // => a 2069 | println(fooList.last()) // => c 2070 | println(fooList.indexOf("b")) // 1 2071 | 2072 | // Se puede acceder a los elementos de una lista por su índice 2073 | println(fooList[1]) // => b 2074 | ``` 2075 | 2076 | Se puede crear una **lista mutable** utilizando la función `mutableListOf()`: 2077 | 2078 | ```kotlin 2079 | val fooMutableList = mutableListOf("a", "b", "c") 2080 | fooMutableList.add("d") 2081 | println(fooMutableList.last()) // => d 2082 | println(fooMutableList.size) // => 4 2083 | ``` 2084 | 2085 | Con la función `'arrayListOf()'` crea una lista mutable y devuelve un tipo _'ArrayList'_ de la API de colecciones de Java. 2086 | 2087 | #### Sets - [Inmutable] 2088 | 2089 | **Un conjunto o 'set' es una colección desordenada de elementos únicos**. En otras palabras, es una colección que no admite duplicados. 2090 | 2091 | Podemos crear un conjunto (o 'set') inmutable utilizando la función `'setOf()'`: 2092 | 2093 | ```kotlin 2094 | val fooSet = setOf("a", "b", "c") 2095 | println(fooSet.contains("a")) // => true 2096 | println(fooSet.contains("z")) // => false 2097 | ``` 2098 | 2099 | Con la función `'mutableSetOf()'` podemos crear un conjunto mutable: 2100 | 2101 | ```kotlin 2102 | // creates a mutable set of int types only 2103 | val intsMutableSet: MutableSet = mutableSetOf(3, 5, 6, 2, 0) 2104 | intsMutableSet.add(8) 2105 | intsMutableSet.remove(3) 2106 | ``` 2107 | 2108 | La función `'hashSetOf()'` retorna un _'HashSet'_ de la API de colecciones de Java el cual almacena los elementos en una tabla 'hash'. Podemos añadir o quitar elementos de este conjunto porque es **mutable**. 2109 | 2110 | La función `'linkedSetOf()'` retorna un _'LinkedHashSet'_ de la API de colecciones de Java. También es un conjunto mutable. 2111 | 2112 | #### Maps - [Inmutable] 2113 | 2114 | Los mapas asocian una clave a un valor. Las claves deben ser únicas, y por tanto no se permite duplicados. En cambio no hay obligación de que los valores asociados sean únicos. Cada clave sólo podrá asociarse a un solo elemento. De esa manera, cada clave se puede usar para identificar de forma única el valor asociado, ya que el mapa se asegura de que no pueda haber claves duplicadas en la colección. Los mapas implementan un forma eficiente de obtener el valor correspondiente a una determinada clave. 2115 | 2116 | Podemos crear un **mapa ('map') inmutable** usando la función `'mapOf()'`: 2117 | 2118 | ```kotlin 2119 | val fooMap = mapOf("a" to 8, "b" to 7, "c" to 9) 2120 | 2121 | // Se puede acceder a los valores en el mapa por su clave 2122 | println(fooMap["a"]) // => 8 2123 | 2124 | // iterar por un mapa con un bucle 'for' 2125 | for ((key, value) in fooMap) { 2126 | println("Key $key and value $value") 2127 | } 2128 | ``` 2129 | 2130 | La función `'linkedHashMap()'` retorna un _'LinkedHasMap'_ de la API de colecciones de Java, que es **mutable**. 2131 | 2132 | La función `'sortedMapOf()'` retorna un _'SortedMap'_ de la API de colecciones de Java que también es **mutable**. 2133 | 2134 | #### Sequences 2135 | 2136 | Las secuencias representan colecciones _'lazily-evaluated'_. Podemos crear una secuencia utilizando la función `'generateSequence()'`. Las secuencias son excelentes cuando el tamaño de la colección es desconocido a priori: 2137 | 2138 | ```kotlin 2139 | val fooSequence = generateSequence(1, { it + 1 }) 2140 | val x = fooSequence.take(10).toList() 2141 | println(x) // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 2142 | 2143 | // An example of using a sequence to generate Fibonacci numbers: 2144 | fun fibonacciSequence(): Sequence { 2145 | var a = 0L 2146 | var b = 1L 2147 | fun next(): Long { 2148 | val result = a + b 2149 | a = b 2150 | b = result 2151 | return a 2152 | } 2153 | return generateSequence(::next) 2154 | } 2155 | 2156 | val y = fibonacciSequence().take(10).toList() 2157 | println(y) // => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] 2158 | ``` 2159 | 2160 | Kotlin proporciona _'higher-order functions'_ para trabajar con colecciones: 2161 | 2162 | ```kotlin 2163 | val z = (1..9).map { it * 3 } 2164 | .filter { it < 20 } 2165 | .groupBy { it % 2 == 0 } 2166 | .mapKeys { if (it.key) "even" else "odd" } 2167 | 2168 | println(z) // => {odd=[3, 9, 15], even=[6, 12, 18]} 2169 | ``` 2170 | 2171 | ### Rangos de valores 2172 | 2173 | Un rango se define como un intervalo que tiene un valor de inicio y un valor final. **Los rangos son cerrados**, lo que significa que el valor inicial y final están incluidos en el rango. Los rangos se crean con el operados `..` o con funciones como `rangeTo()` o `downTo()`. 2174 | 2175 | Para crear un intervalo sin incluir el último elemento usamos la función `until`. 2176 | 2177 | ```kotlin 2178 | val oneToNine = 1..9 2179 | val oneToFive: IntRange = 1.rangeTo(5) 2180 | 2181 | val fiveToOne = 5.downTo(1) 2182 | print(fiveToOne) // => 5 downTo 1 step 1 2183 | 2184 | val oneToTen = (1..10).step(2).reversed() // => 9, 7, 5, 3, 1 2185 | println("${tenToOne.first} - ${tenToOne.last}") // => 10 - 1 2186 | 2187 | val oneToFour = 1.until(5) 2188 | print(r) // => 1..4 2189 | ``` 2190 | 2191 | Lost tipos `IntRange`, `LongRange`, `CharRange` tienen una característica extra y es que permite iterar sobre los intervalos. 2192 | 2193 | Una vez que se crea un intervalo, se puede usar el operador `in` para probar si un valor dado está incluido en el intervalo o el operador `!in` para comprobar si un valor no está en el intervalo: 2194 | 2195 | ```kotlin 2196 | // Iterar con un bucle 'for' 2197 | for (i in 1..10) { // equivalent of 1 <= i && i <= 10 2198 | print(i) 2199 | } 2200 | 2201 | // Iterar en sentido inverso 2202 | for (i in 4 downTo 1) { 2203 | print(i) 2204 | } 2205 | 2206 | // Iterar por un intervalo sin incluir el último elemento 2207 | for (i in 1 until 10) { 2208 | // i in [1, 10), 10 is excluded 2209 | println(i) 2210 | } 2211 | 2212 | // Pasos arbitrarios 2213 | for (i in 1..4 step 2) { 2214 | print(i) 2215 | } 2216 | 2217 | for (i in 4 downTo 1 step 2) { 2218 | print(i) 2219 | } 2220 | ``` 2221 | 2222 | ### Smart Casting 2223 | 2224 | Podemos verificar si un objeto es de un tipo en particular usando el operador `is` o si no es de un tipo con el operador `!is`. 2225 | 2226 | Si un objeto pasa una verificación de tipo entonces se puede usar como ese tipo sin realizar la conversión explícitamente: 2227 | 2228 | ```kotlin 2229 | fun smartCastExample(x: Any): Boolean { 2230 | if (x is Boolean) { 2231 | // x is automatically cast to Boolean 2232 | return x 2233 | } else if (x is Int) { 2234 | // x is automatically cast to Int 2235 | return x > 0 2236 | } else if (x is String) { 2237 | // x is automatically cast to String 2238 | return x.isNotEmpty() 2239 | } else { 2240 | return false 2241 | } 2242 | } 2243 | println(smartCastExample("Hello, world!")) // => true 2244 | println(smartCastExample("")) // => false 2245 | println(smartCastExample(5)) // => true 2246 | println(smartCastExample(0)) // => false 2247 | println(smartCastExample(true)) // => true 2248 | ``` 2249 | 2250 | La conversión inteligente (_'smart cast'_) también funciona con bloques `when` o bucles `while`: 2251 | 2252 | ```kotlin 2253 | fun smartCastWhenExample(x: Any) = when (x) { 2254 | is Boolean -> x 2255 | is Int -> x > 0 2256 | is String -> x.isNotEmpty() 2257 | else -> false 2258 | } 2259 | ``` 2260 | 2261 | ### Conversión explícita o _'Explicit Casting'_ 2262 | 2263 | Podemos usar el operador `as` (o el operador de **conversión no segura** o _'unsafe cast operator'_) para convertir explícitamente una referencia de un tipo a otro tipo en Kotlin. Si la operación de conversión explícita es ilegal, tenga en cuenta que se lanzará una excepción de tipo _'ClassCastException'_. 2264 | 2265 | Para evitar que se lance una excepción al realizar la conversión, podemos usar el operador de **conversión seguro** `as?`. Este operador intentará la conversión y si no se puede realizar la conversión devolverá `'null'` en vez de lanzar la excepción. Por tanto la variable que contiene el resultado de una conversión segura debe ser capaz de mantener un resultado nulo: 2266 | 2267 | ```kotlin 2268 | val circle = shape as Circle 2269 | 2270 | val circle: Circle? = shape as? Circle // Conversión segura 2271 | ``` 2272 | 2273 | ### Valores nulos ('nullable types') 2274 | 2275 | Para que una variable contenga el valor '**null**' debe especificarse explícitamente como _'nullable'_. Una variable se puede especificar como _'nullable'_ agregando un `?` a su tipo. 2276 | 2277 | Podemos acceder a una variable o método _'nullable'_ utilizando el operador `'?.'` también llamado _'Safe Call Operator'_. Un método o variable sólo será invocado si tiene una valor no nulo. En caso de que sea nulo será ignorado evitando un _'NullPointerException'_ 2278 | 2279 | Kotlin provee el operador `'?:'`, también llamado _'Elvis Operator'_ para especificar un valor alternativo para usar si una variable es nula. Cuando la expresión de la izquierda del operador `'?:'` no es nulo entonces lo devuelve. En caso de que sea nulo devuelve la expresión de la derecha. La expresión de la derecha sólo será evaluada si la expresión de la izquierda es 'null'. 2280 | 2281 | ```kotlin 2282 | val name: String = null // no compilará ya que no puede contener valores nulos 2283 | var fooNullable: String? = "abc" 2284 | 2285 | fooNullable?.length // => 3 2286 | 2287 | // 'Elvis Operator' 2288 | fooNullable?.length ?: -1 // => 3 2289 | 2290 | fooNullable = null 2291 | val len: Int? = fooNullable?.length // El tipo de retorno de 'fooNullable' puede ser 'null' y por tanto debemos usar Int? 2292 | 2293 | fooNullable?.length // => null 2294 | fooNullable?.length ?: -1 // => -1 2295 | 2296 | // Encadenar 'safe calls'. La cadena retorna 'null' si alguna de ellas es 'null' 2297 | fun getCountryNameSafe(person: Person?): String? { 2298 | return person?.address?.city?.country?.name 2299 | } 2300 | 2301 | // Dado que 'throw' y 'return' son expresiones en Kotlin se pueden usar en la parte derecha del operador 'Elvis' 2302 | fun foo(node: Node): String? { 2303 | val parent = node.getParent() ?: return null 2304 | val name = node.getName() ?: throw IllegalArgumentException("name expected") 2305 | // ... 2306 | } 2307 | ``` 2308 | 2309 | De manera similar, podemos devolver tipos _'nullable'_ y no _'nullable'_ desde una función. 2310 | 2311 | ```kotlin 2312 | fun getName(): String? = name // Esta función puede o no devolver una referencia nula. 2313 | 2314 | fun getNotNullName(): String = name ?: "John" // Esta función no devolverá una referencia nula 2315 | 2316 | getName() // => null 2317 | getNotNullName() // => John 2318 | ``` 2319 | 2320 | Con _'smart cast'_, el compilador rastrea las condiciones dentro de una expresión `'if'`. Si realizamos la verificación de que una variable no es nula, entonces el compilador nos permitirá acceder a la variable como si hubiera sido declarada como un tipo no anulable: 2321 | 2322 | ```kotlin 2323 | var l = if (name != null) name.length else -1 2324 | ``` 2325 | 2326 | El operador de aserción no-nulo `'!!'` convierte cualquier valor a un tipo no nulo y lanza una excepción _'NullPointerException'_ si el valor es nulo. 2327 | 2328 | ```kotlin 2329 | val length: Int = name!!.length 2330 | ``` 2331 | 2332 | ### Igualdad 2333 | 2334 | En Kotlin hay tenemos la **igualdad estructural** y la **igualdad referencial**. 2335 | 2336 | La igualdad estructural se comprueba con la operación `'=='` y la parte contraria `'!='` y se utiliza para comprobar si dos valores o variables son iguales (`equals()`) 2337 | 2338 | ```kotlin 2339 | if (a == b) { 2340 | // ... 2341 | } else { 2342 | // ... 2343 | } 2344 | ``` 2345 | 2346 | La igualdad referencial se comprueba con la operación `'==='` y su contraparte `'!=='` y evalúa a `true` si y sólo si dos referencias apuntan al mismo objeto. 2347 | 2348 | ### [Standard Library Functions] 2349 | 2350 | Son funciones que proporciona Kotlin para aumentar la biblioteca estándar de Java. 2351 | 2352 | #### [Apply] 2353 | 2354 | `'apply'` es una función de extensión de la biblioteca estándar de Kotlin declarada en `'Any'`, por lo que puede ser invocada en cualquier tipo de instancia. `'apply'` acepta una expresión lambda que es invocada y el receptor es la instancia donde es llamada. La función `'apply'` devuelve una instacia del original. 2355 | 2356 | Su uso principal es hacer que el código que necesita inicializar una instancia sea más legible permitiendo que las funciones y las propiedades se llamen directamente dentro de la función antes de devolver el valor en sí. 2357 | 2358 | ```kotlin 2359 | data class Person(var firstName: String, var lastName : String) 2360 | var person = Person("John", "Doe") 2361 | 2362 | person.apply { this.firstName = "Bruce" } 2363 | print(person) // => Person(firstName=Bruce, lastName=Doe) 2364 | 2365 | // 'apply' retorna la instancia original. 2366 | person.apply { this.firstName = "Bruce" }.firstName = "Steve" 2367 | print(person) // => Person(firstName=Steve, lastName=Doe) 2368 | ``` 2369 | 2370 | #### [Let] 2371 | 2372 | La función `'let'` toma el objeto sobre el que se invoca como parámetro y devuelve el resultado de la expresión lambda. Es útil cuando desea ejecutar algún código en un objeto antes de devolver algún valor diferente y no necesita mantener una referencia al original: 2373 | 2374 | ```kotlin 2375 | fun main(args: Array) { 2376 | var str = "Hello World" 2377 | str.let { println("$it!!") } // => Hello World!! 2378 | println(str) // => Hello World 2379 | } 2380 | 2381 | var strLength = str.let { "$it function".length } // devuelve el resultado de la expresión lambda 2382 | println("strLength is $strLength") // => strLength is 25 2383 | ``` 2384 | 2385 | #### [With] 2386 | 2387 | La función `'with'` es una función de nivel superior diseñada para los casos en los que desea llamar a múltiples funciones en un objeto y no desea repetir el receptor cada vez. La función `'with'` acepta un receptor y un cierre para operar en dicho receptor: 2388 | 2389 | ```kotlin 2390 | data class Person(var firstName: String, var lastName : String) 2391 | var person = Person("John", "Doe") 2392 | 2393 | with(person) 2394 | { 2395 | firstName = "Bruce" 2396 | lastName = "Doe" 2397 | } 2398 | 2399 | // notación sin 'with' 2400 | person.firstName = "John" 2401 | person.lastName = "Doe" 2402 | ``` 2403 | 2404 | La última expresión en un bloque `'with'` se retorna como resultado: 2405 | 2406 | ```kotlin 2407 | var name = with(person) 2408 | { 2409 | firstName = "John" 2410 | lastName = "Doe" 2411 | "$firstName $lastName" // se retorna este valor y se almacena en 'name' 2412 | } 2413 | println(name) // => John Doe 2414 | ``` 2415 | 2416 | #### [Run] 2417 | 2418 | `'Run'` es una función que combina las características de `'with'` y `'let'`. Esto significa que se pasa una expresión lambda a la función `'run'` y la instancia del objeto es el receptor. El valor de retorno de la expresión lambda se usa como valor de retorno: 2419 | 2420 | ```kotlin 2421 | person.run { 2422 | this.firstName = "Bruce" 2423 | } 2424 | print(person) // => Person(firstName=Bruce, lastName=Doe) 2425 | 2426 | ``` 2427 | 2428 | La diferencia clave entre `'let'` y `'run'` es que con `'run'` el receptor es la instancia, mientras que en `'let'`, el argumento de la expresión lambda es la instancia. 2429 | 2430 | #### [Repeat] 2431 | 2432 | Esta función acepta un entero y una función literal. La función literal será invocada las veces indicadas por el valor entero. 2433 | 2434 | ```kotlin 2435 | repeat(10, { println("Hello") }) 2436 | ``` 2437 | 2438 | #### [Lazy] 2439 | 2440 | La función `'lazy'` es una función cuya utilidad es envolver funciones costosas en términos de rendimiento o de recursos y que serán invocadas cuando sean requeridas por primera vez. La ventaja de utilizar esta función proporcionada por la biblioteca estándar de Kotlin es que el compilador mantendrá la invocación sincronizada evitando que sea invocada más de una vez. 2441 | 2442 | ```kotlin 2443 | fun readStringFromDatabase(): String = ... // expensive operation 2444 | val lazyString = lazy { readStringFromDatabase() } 2445 | ``` 2446 | 2447 | #### [Use] 2448 | 2449 | La función `'use'` es similar a la declaración `'try-with-resources'` presente en Java 7. La función `'use'` se define como una función de extensión de la interfaz _'Closeable'_. Ejecuta la función y luego 'cierra' el recurso de forma segura. 2450 | 2451 | ### Assertions 2452 | 2453 | Kotlin proporciona un conjunto de funciones que nos permiten agregar una cantidad limitada de **especificaciones formales** a nuestro código. Una especificación formal es una aserción que siempre debe ser verdadera o falsa en la ubicación cuando se ejecuta la aserción. Estos también se conocen como contratos o diseño por contrato: 2454 | 2455 | - `'require()'` y `'requireNotNull()'` lanza una excepción de tipo _'IllegalArgumentException'_ y se utiliza para garantizar que los argumentos cumplan el contrato. 2456 | 2457 | - `'assert()'` lanza una excepción _'AssertionException'_ y se utiliza para garantizar que nuestro estado interno es consistente. 2458 | 2459 | - `'check()'` y `'error()'` lanza una excepción _'IllegalStateException'_ y también se usa para mantener la consistencia del estado interno. 2460 | 2461 | Estas funciones son similares. La clave que las diferencia es el tipo de excepción que se plantea. 2462 | 2463 | ```kotlin 2464 | fun neverEmpty(str: String) { 2465 | require(str.length > 0, { "String should not be empty" }) 2466 | println(str) 2467 | } 2468 | 2469 | fun foo(k: Int, value: Boolean) { 2470 | require(k > 10, { "k should be greater than 10" }) // => throws an IllegalArgumentException 2471 | requireNotNull(k) // => throws an IllegalArgumentException if the value is null. 2472 | check(value) // => throws an IllegalStateException if the value is false 2473 | if (k == 20) error("Error: k == 20") // => throws an IllegalStateException 2474 | } 2475 | ``` 2476 | 2477 | ### Excepciones 2478 | 2479 | En Kotlin todas las excepciones son subclases de la clase `'Throwable'`. Cada excepción tiene un mensaje, un seguimiento de la pila y una causa opcional. **Kotlin no tiene _'checked exceptions'_ a diferencia de Java, que realiza la distinción entre tipos de excepciones**. 2480 | 2481 | Para lanzar un objeto de excepción, se utiliza la palabra clave `'throw'`: 2482 | 2483 | ```kotlin 2484 | throw Exception("Message") 2485 | ``` 2486 | 2487 | Para capturar una excepción lanzada se utiliza un bloque `'try'`: 2488 | 2489 | ```kotlin 2490 | try { 2491 | // some code 2492 | } 2493 | catch (e: SomeException) { 2494 | // handler 2495 | } 2496 | finally { 2497 | // optional finally block 2498 | } 2499 | ``` 2500 | 2501 | Puede haber 0 o más bloques `'catch'`. Los bloques `'finally'` son opcionales y puede omitirse. Sin embargo, tiene que haber al menos un bloque `'catch'` o `'finally'`. 2502 | 2503 | Al igual que muchas otras instrucciones en Kotlin, `'try'` es una expresión y por tanto puede devolver un valor: 2504 | 2505 | ```kotlin 2506 | val a: Int? = try { parseInt(input) } catch (e: NumberFormatException) { null } 2507 | ``` 2508 | 2509 | El valor devuelto por un `'try'` que actúa como expresión es la última expresión en el bloque `'try'` o la última expresión en el bloque `'catch'`. El contenido del bloque `'finally'` no afecta al resultado de la expresión. 2510 | 2511 | `'throw'` es una expresión en Kotlin, así que se puede usar, por ejemplo, como parte de una _'Elvis expression'_: 2512 | 2513 | ```kotlin 2514 | val s = person.name ?: throw IllegalArgumentException("Name required") 2515 | ``` 2516 | 2517 | El tipo de retorno de una expresión `'throw'` es el tipo especial `'Nothing'`. Este tipo no tiene valores y se utiliza para marcar ubicaciones del código que nunca se pueden alcanzar. 2518 | 2519 | ```kotlin 2520 | fun fail(message: String): Nothing { 2521 | throw IllegalArgumentException(message) 2522 | } 2523 | ``` 2524 | 2525 | Cuando llame a la función del ejemplo anterior, el compilador sabrá que la ejecución no continúa más allá de la llamada: 2526 | 2527 | ```kotlin 2528 | val s = person.name ?: fail("Name required") 2529 | println(s) // 's' is known to be initialized at this point 2530 | ``` 2531 | 2532 | Otro caso en el que puede encontrar este tipo es la inferencia de tipos. La variante _'nullable'_ de este tipo, `'Nothing?'`, tiene exactamente un valor posible, que es el valor `'null'`. Si se usa el valor nulo para inicializar un valor de un tipo inferido y no hay otra información que se pueda usar para determinar un tipo más específico, el compilador inferirá el tipo `'Nothing?'`: 2533 | 2534 | ```kotlin 2535 | val x = null // 'x' tiene el tipo `Nothing?` 2536 | val l = listOf(null) // 'l' tiene el tipo `List 2537 | ``` 2538 | 2539 | ## Anotaciones 2540 | 2541 | Las **anotaciones** permiten a los desarrolladores agregar un significado adicional a las clases, interfaces, parámetros, etc., en el momento de la compilación. Las anotaciones pueden ser utilizadas por el compilador o por su propio código a través de la reflexión en tiempo de ejecución. Dependiendo del valor de la anotación, el significado del programa o los datos puede cambiar. 2542 | 2543 | ### [@JvmStatic] 2544 | 2545 | Kotlin representa funciones de nivel de paquete (funciones fuera de una clase) como métodos estáticos. Kotlin también puede generar métodos estáticos para funciones definidas en **_'objects'_** y **_'companin objects'_** si anota esas funciones como `'@JvmStatic'`. Si usa esta anotación, el compilador generará tanto un método estático en la clase envolvente del objeto como un método de instancia en el propio objeto. 2546 | 2547 | ```kotlin 2548 | class C { 2549 | companion object { 2550 | @JvmStatic fun foo() {} 2551 | fun bar() {} 2552 | } 2553 | } 2554 | 2555 | // Ahora 'foo()' es estático en Java pero no 'bar()' 2556 | C.foo(); // correcto 2557 | C.bar(); // error: 'bar()' no es un método estático 2558 | C.Companion.foo(); // correcto 2559 | C.Companion.bar(); // la única forma de invocar a 'bar()' 2560 | 2561 | object Obj { 2562 | @JvmStatic fun foo() {} 2563 | fun bar() {} 2564 | } 2565 | 2566 | // In Java: 2567 | Obj.foo(); // correcto 2568 | Obj.bar(); // error 2569 | Obj.INSTANCE.bar(); // correcto, una llamada a través de la instancia 'Singleton' 2570 | Obj.INSTANCE.foo(); // correcto 2571 | ``` 2572 | 2573 | ### [@Throws] 2574 | 2575 | Dado que todas las excepciones en Kotlin son _'unchecked exceptions'_, no es necesario agregar una lista de posibles excepciones a las firmas de métodos como las que hay en Java. Sin embargo, es posible que deseamos informar a los usuarios de Java que nuestra API produce excepciones en ciertas situaciones. Podemos hacer esto utilizando la anotación `'@Throws'`, que se utiliza para indicar al compilador que genere cláusulas de lanzamiento en los métodos generados. 2576 | 2577 | ```kotlin 2578 | @Throws(FileNotFoundException::class) 2579 | fun fileExists(path: String) { 2580 | // ... 2581 | } 2582 | ``` 2583 | 2584 | ### [@JvmOverloads] 2585 | 2586 | Dada una función con parámetros por defecto, `'@JvmOverloads'` hará que el compilador cree múltiples métodos sobrecargados para cada parámetro predeterminado. 2587 | 2588 | ### [@JvmName] 2589 | 2590 | Podemos cambiar el nombre del fichero creado por Kotlin con la anotación `'@JvmName'`: 2591 | 2592 | ```kotlin 2593 | // example.kt (sin @JvmName) 2594 | package demo 2595 | 2596 | class Foo 2597 | 2598 | fun bar() { ... } 2599 | 2600 | // En Java 2601 | new demo.Foo(); 2602 | demo.ExampleKt.bar(); 2603 | 2604 | 2605 | // Usamos la anotación '@JvmName' al principio del fichero para indicar al compilador el nombre del fichero 2606 | @file:JvmName("DemoUtils") 2607 | 2608 | package demo 2609 | 2610 | class Foo 2611 | 2612 | fun bar() { ... } 2613 | 2614 | // Ahora en Java 2615 | new demo.Foo(); 2616 | demo.DemoUtils.bar(); 2617 | ``` 2618 | 2619 | ### [@JvmMultifileClass] 2620 | 2621 | Además de indicarle al compilador el nombre del fichero con `'@JvmName'` podemos indicarle que combine todas las funciones de nivel superior de varios ficheros en Kotlin en una única clase Java con la anotación `'@JvmMultifileClass'`. 2622 | 2623 | ## [Reflection] 2624 | 2625 | **Reflection** es el nombre dado a la inspección del código en tiempo de ejecución en lugar de tiempo de compilación. Puede usarse para crear instancias de clases, buscar funciones e invocarlas, inspeccionar anotaciones, buscar campos y descubrir parámetros y genéricos, todo sin conocer esos detalles en el momento de la compilación. 2626 | 2627 | Por ejemplo, si necesitamos persistir tipos en una base de datos y a priori no conocemos el tipo de datos podemos utilizar la reflexión para conocer el tipo de datos en tiempo de ejecución y crear la SQL apropiada a ese tipo. 2628 | 2629 | Para usar la reflexión en Kotlin hay que importar el paquete `kotlin.reflect`. 2630 | 2631 | `'KClass'` es el tipo central utilizado en la reflexión de Kotlin. Cada tipo tiene una instancia de `'KClass'` en tiempo de ejecución que contiene detalles de las funciones, propiedades, anotaciones, etc., para ese tipo. Para obtener una instancia de `'KClass'` para cualquier tipo, usamos la sintaxis especial `'::class'` en una instancia de ese tipo: 2632 | 2633 | ```kotlin 2634 | val name = "George" 2635 | val kclass = name::class // => class kotlin.String 2636 | 2637 | data class Person(val firstName: String, val lastName: String) 2638 | println(Person::class.qualifiedName) // => Person 2639 | println(Person::class.isData) // => true 2640 | ``` 2641 | 2642 | Podemos obtener una referencia a la clase utilizando el _'fully qualified name or FQN'_ de la clase y la API 'reflection' de Java. Si el compilador no encuentra la clase lanza una _'ClassNotFoundException'_: 2643 | 2644 | ```kotlin 2645 | package com.example 2646 | data class Person(val firstName: String, val lastName: String) 2647 | 2648 | val kClass = Class.forName("com.example.Person").kotlin // => class com.example.Personal 2649 | ``` 2650 | 2651 | Para crear instancias de tipo sin conocer el tipo en tiempo de ejecución podemos invocar la función `'createInstance()'` en una referencia de `'KClass'`. Podemos usar esta función con clases sin parámetros o con parámetros opcionales, es decir, que tengan valor por defecto: 2652 | 2653 | ```kotlin 2654 | class PositiveInteger(value: Int = 0) 2655 | 2656 | fun createInteger(kclass: KClass): PositiveInteger { 2657 | return kclass.createInstance() 2658 | } 2659 | ``` 2660 | 2661 | Podemos devolver una lista de todos los constructores declarados en un tipo dado usando la propiedad `'constructor'` disponible en el tipo `'KClass'`. Podemos instanciar una clase usando el constructor con la instrucción `'call'` o `'callBy'`: 2662 | 2663 | ```kotlin 2664 | class Person constructor(val firstName: String, val lastName: String) 2665 | 2666 | fun printConstructors(kclass: KClass) { 2667 | kclass.constructors.forEach { 2668 | println(it.parameters) 2669 | } 2670 | } 2671 | printConstructors(Person::class) // Muestra el/los constructor/es de la clase 'Person' 2672 | 2673 | // Recupera el primer constructor. Si no encuentra ninguno lanza una excepción. 2674 | val constructor = Person::class.constructors.first() 2675 | val person = constructor.call("John", "Doe") // Invocar al constructor con 'call' 2676 | println(person.firstName) // => John 2677 | ``` 2678 | 2679 | Además de los constructores de una clase, también podemos acceder y listar las funciones de una clase con la propiedad `'functions'` disponible en el tipo `'KClass'`: 2680 | 2681 | ```kotlin 2682 | class Person constructor(val firstName: String, val lastName: String) { 2683 | fun getName(): String { 2684 | return "$firstName $lastName" 2685 | } 2686 | } 2687 | 2688 | fun printFunctions(kclass: KClass) { 2689 | kclass.functions.forEach { 2690 | println(it.name) 2691 | } 2692 | } 2693 | 2694 | printFunctions(Person::class) // => getName equals hashCode toString 2695 | 2696 | val function = Person::class.functions.find { it.name == "getName" } 2697 | val person = Person("John", "Doe") 2698 | function?.call(person) // => John Doe 2699 | ``` 2700 | 2701 | ## Coroutines 2702 | 2703 | (todo) 2704 | 2705 | ## Testing 2706 | 2707 | KotlinTest es el framework para probar y testear el código en Kotlin. Añadir la dependencia a Gradle: `testCompile 'io.kotlintest:kotlintest:x.y.z'`. 2708 | 2709 | Normalmente, para mantener ordenada la estructura del proyecto los ficheros de test se ubican en `src/test/kotlin` 2710 | 2711 | Una especificación o _'spec'_ es simplemente la manera en que las pruebas se presentan en los archivos de clase. Hay varias especificaciones diferentes disponibles como **FunSpec**, **StringSpec+*, **ShouldSpec**. etc... 2712 | 2713 | La especificación **FunSpec** permite crear pruebas similares al estilo _jUnit_. Para escribir un test unitario invocamos la función _'test'_ que toma dos parámetros. El primer parámetro es una descripción de la prueba unitaria y el segundo es una función literal que contiene el cuerpo de la prueba. La descripción o nombre de la prueba aparecerá en la salida, así que permite saber que prueba/s han pasado la prueba y cuáles han fallado. 2714 | 2715 | ```kotlin 2716 | class StringTestWithFunSpec : FunSpec() { 2717 | init { 2718 | test("String.startsWith should be true for a prefix") { 2719 | "helloworld".startsWith("hello") shouldBe true 2720 | } 2721 | test("String.endsWith should be true for a prefix") { 2722 | "helloworld".endsWith("world") shouldBe true 2723 | } 2724 | 2725 | } 2726 | } 2727 | ``` 2728 | 2729 | La especificación **StringSpec** es la especificación recomendada por los autores de Kotlin y es la especificación más simple y compacta ya que reduce la sintaxis al mínimo. Se escribe una cadena seguida de una expresión lambda para probar el código: 2730 | 2731 | ```kotlin 2732 | class StringTestWithStringSpec : StringSpec() { 2733 | init { 2734 | "strings.length should return size of string" { 2735 | "hello".length shouldBe 5 2736 | "hello" shouldBe haveLength(5) 2737 | } 2738 | } 2739 | } 2740 | ``` 2741 | 2742 | La especificación **ShouldSpec** es similar a **FunSpec** pero usa la palabra clave `'should'` en vez de `'test'`: 2743 | 2744 | ```kotlin 2745 | class StringTestWithShouldSpec : ShouldSpec() { 2746 | init { 2747 | should("return the length of the string") { 2748 | "sammy".length shouldBe 5 2749 | "".length shouldBe 0 2750 | } 2751 | // Nested form 2752 | "String.length" { 2753 | should("return the length of the string") { 2754 | "sammy".length shouldBe 5 2755 | "".length shouldBe 0 2756 | } 2757 | } 2758 | } 2759 | } 2760 | ``` 2761 | 2762 | La especificación **WordSpec** usa también la palabra clave `'should'`. Esta especificación permite anidar las pruebas: 2763 | 2764 | ```kotlin 2765 | class StringTestWithWordSpec : WordSpec() { 2766 | init { 2767 | "String.length" should { 2768 | "return the length of the string" { 2769 | "sammy".length shouldBe 5 2770 | "".length shouldBe 0 2771 | } 2772 | } 2773 | } 2774 | } 2775 | ``` 2776 | 2777 | La especificación **BehaviorSpec** utiliza las palabras clave `'given'`, `'when'` y `'then'` para crear pruebas unitarias más cercanas al lenguaje natural: 2778 | 2779 | ```kotlin 2780 | class StringTestWithBehaviorSpec : BehaviorSpec() { 2781 | init { 2782 | given("a stack") { 2783 | val stack = Stack() 2784 | `when`("an item is pushed") { 2785 | stack.push("kotlin") 2786 | then("the stack should not be empty") { 2787 | stack.isEmpty() shouldBe true 2788 | } 2789 | } 2790 | `when`("the stack is popped") { 2791 | stack.pop() 2792 | then("it should be empty") { 2793 | stack.isEmpty() shouldBe false 2794 | } 2795 | } 2796 | } 2797 | } 2798 | } 2799 | ``` 2800 | 2801 | La especificación **FeatureSpec** es similar a la especificación **BehaviorSpec** pero utiliza las palabras clave `'feature'` y `'scenario'`: 2802 | 2803 | ```kotlin 2804 | class StringTestWithFeatureSpec : FeatureSpec() { 2805 | init { 2806 | feature("Hello World") { 2807 | scenario("should starts with 'Hello'") { 2808 | "Hello World".startsWith("Hello") 2809 | } 2810 | scenario("should ends with 'World'") { 2811 | "Hello World".endsWith("World") 2812 | } 2813 | } 2814 | } 2815 | } 2816 | ``` 2817 | 2818 | Los **matchers** prueban alguna propiedad, indicada por el nombre del **matcher**, más allá de la simple igualdad. Por ejemplo, un comparador puede verificar si una cadena está vacía o si un entero es positivo. 2819 | 2820 | ```kotlin 2821 | // [String matchers] 2822 | class StringTestWithDifferentMatchers : StringSpec() { 2823 | init { 2824 | "Tests string prefixes" { 2825 | "Hello".startsWith("He") shouldBe true 2826 | "Hello" shouldBe startWith("He") 2827 | } 2828 | "Tests substrings"{ 2829 | "Hello" shouldBe include("el") 2830 | } 2831 | "Test string suffixes" { 2832 | "Hello".endsWith("llo") shouldBe true 2833 | "Hello" shouldBe endWith("llo") 2834 | } 2835 | "Tests the length of a string" { 2836 | "Hello".length shouldBe 5 2837 | "Hello" shouldBe haveLength(5) 2838 | } 2839 | "Tests the equality using a regular expression" { 2840 | "Hello" shouldBe match("He...") 2841 | } 2842 | } 2843 | } 2844 | 2845 | // [Collection matchers] 2846 | class CollectionTestWithDifferentMatchers : StringSpec() { 2847 | private val listWithDifferentIntegers = listOf(1, 2, 3, 4, 5) 2848 | private val mapWithKeyAndValues = mapOf(1 to "Hello", 2 to "World") 2849 | 2850 | init { 2851 | "Tests that a collection should contain the given element" { 2852 | listWithDifferentIntegers shouldBe contain(3) 2853 | } 2854 | "Test the size of the collection" { 2855 | listWithDifferentIntegers shouldBe haveSize(5) 2856 | } 2857 | "Tests that the collections should be sorted" { 2858 | listWithDifferentIntegers shouldBe sorted() 2859 | } 2860 | "Tests that the collection has a single element that is equal to the given element" { 2861 | listWithDifferentIntegers shouldNotBe singleElement(2) 2862 | } 2863 | "Tests that the collection contains all the given elements. The order of these elements does not matter." { 2864 | listWithDifferentIntegers shouldBe containsAll(1, 2, 4) 2865 | } 2866 | "Tests whether the collection is empty or not" { 2867 | listWithDifferentIntegers shouldNotBe beEmpty() 2868 | } 2869 | "Tests whether the map contains mapping from a key to any value" { 2870 | mapWithKeyAndValues shouldBe haveKey(2) 2871 | } 2872 | "Tests whether the map contains the value for at least one key" { 2873 | mapWithKeyAndValues shouldBe haveValue("Hello") 2874 | } 2875 | "Tests that the map contains the exact mapping of the key to the value" { 2876 | mapWithKeyAndValues shouldBe contain(2, "World") 2877 | } 2878 | } 2879 | } 2880 | 2881 | // [Floating point matchers] 2882 | // En valores en punto flotante más que la igualdad absoluta se utiliza la 'tolerancia' que es el valor mínimo entre dos valores que satisfacen el criterio de igualdad 2883 | class FloatNumberTestWithTolerance : StringSpec() { 2884 | private val randomDouble = 18.005 2885 | private val enoughDouble = 18.006 2886 | 2887 | init { 2888 | "Test if two numbers are equals" { 2889 | randomDouble shouldNotBe equals(enoughDouble) 2890 | randomDouble shouldBe (enoughDouble plusOrMinus 0.01) 2891 | } 2892 | } 2893 | } 2894 | 2895 | // [Exception matchers] 2896 | // 'shouldThrow fallará si se lanza una excepción diferente 2897 | class ExceptionTest : StringSpec() { 2898 | init { 2899 | "Testing IllegalArgumentException" { 2900 | shouldThrow { 2901 | addNumberToTwo(10.0) shouldEqual 10.5 2902 | } 2903 | } 2904 | } 2905 | } 2906 | 2907 | @Throws(IllegalArgumentException::class) 2908 | fun addNumberToTwo(a: Any): Int { 2909 | if (a !is Int) { 2910 | throw IllegalArgumentException("Number must be an integer") 2911 | } 2912 | return 2 + a 2913 | } 2914 | ``` 2915 | 2916 | Los **matchers** se pueden combinar usando los operadores de la lógica booleana como `'and'` y `'or'`: 2917 | 2918 | ```kotlin 2919 | class CombiningMatchers : StringSpec() { 2920 | init { 2921 | "Combining matchers" { 2922 | "Hello World" should (startWith("Hel") and endWith("rld")) 2923 | } 2924 | } 2925 | } 2926 | ``` 2927 | 2928 | Un **inspector** en KotlinTest es la forma más fácil de probar el contenido de _'collections'_: 2929 | 2930 | ```kotlin 2931 | val kings = listOf("Stephen I", "Henry I", "Henry II", "Henry III", "William I", "William III") 2932 | 2933 | class InspectorTests : StringSpec() { 2934 | init { 2935 | "all kings should have a regal number" { 2936 | forAll(kings) { 2937 | it should endWith("I") 2938 | } 2939 | } 2940 | "only one king has the name Stephen" { 2941 | forOne(kings) { 2942 | it should startWith("Stephen") 2943 | } 2944 | } 2945 | "some kings have regal number II" { 2946 | forSome(kings) { 2947 | it should endWith("II") 2948 | } 2949 | } 2950 | "at least one King has the name Henry" { 2951 | forAtLeastOne(kings) { 2952 | it should startWith("Henry") 2953 | } 2954 | } 2955 | } 2956 | } 2957 | ``` 2958 | 2959 | A veces es posible que sea necesario ejecutar algo de código, antes de que se ejecuten las pruebas o después de que se completen todas las pruebas (sean exitosas o no). Esto se puede lograr mediante el uso de la clase abstracta `'ProjectConfig'`. Para usar esto, simplemente se crea un objeto que extienda de esta clase abstracta y asegurarse que esté en la ruta de la clase. KotlinTest lo encontrará automáticamente y lo invocará: 2960 | 2961 | ```kotlin 2962 | object codeExecutionBeforeAndAfterTestCases : ProjectConfig() { 2963 | override fun beforeAll() { 2964 | // ...code 2965 | } 2966 | 2967 | override fun afterAll() { 2968 | // ...code 2969 | } 2970 | } 2971 | ``` 2972 | 2973 | ## Java Interop 2974 | 2975 | ### Calling Java from Kotlin 2976 | 2977 | Kotlin está diseñado teniendo en cuenta la interoperabilidad de Java. El código Java existente puede llamarse desde Kotlin de una manera natural, y el código Kotlin también se puede usar desde Java sin problemas. 2978 | 2979 | Casi todo el código de Java se puede utilizar sin problemas: 2980 | 2981 | ```kotlin 2982 | import java.util.* 2983 | 2984 | fun demo(source: List) { 2985 | val list = ArrayList() 2986 | // 'for'-loops work for Java collections: 2987 | for (item in source) { 2988 | list.add(item) 2989 | } 2990 | // Operator conventions work as well: 2991 | for (i in 0..source.size - 1) { 2992 | list[i] = source[i] // get and set are called 2993 | } 2994 | } 2995 | ``` 2996 | 2997 | #### 'Getters' and 'Setters' 2998 | 2999 | Los métodos que siguen las convenciones de Java para _'getters'_ y _'setters'_ (métodos sin argumentos con nombres que comienzan con 'get' y métodos con argumentos únicos con nombres que comienzan con 'set') se representan como **propiedades** en Kotlin. 3000 | 3001 | Los métodos de acceso booleanos (donde el nombre del _'getter'_ comienza con 'is' y el nombre del _'setter'_ comienza con 'set') se representan como propiedades que tienen el mismo nombre que el método _'getter'_: 3002 | 3003 | ```kotlin 3004 | import java.util.Calendar 3005 | 3006 | fun calendarDemo() { 3007 | val calendar = Calendar.getInstance() 3008 | if (calendar.firstDayOfWeek == Calendar.SUNDAY) { // call getFirstDayOfWeek() 3009 | calendar.firstDayOfWeek = Calendar.MONDAY // call setFirstDayOfWeek() 3010 | } 3011 | if (!calendar.isLenient) { // call isLenient() 3012 | calendar.isLenient = true // call setLenient() 3013 | } 3014 | } 3015 | ``` 3016 | 3017 | Si la clase Java solo tiene un _'setter'_, no será visible como una propiedad en Kotlin, ya que Kotlin no admite propiedades que tengan únicamente el método _'setter'_. 3018 | 3019 | #### 'Void' como retorno 3020 | 3021 | Si un método Java devuelve `'void'`, devolverá `'Unit'` cuando se llame desde Kotlin. Si, por casualidad, alguien usa ese valor de retorno, el compilador de Kotlin lo asignará en el sitio de la llamada, ya que el valor en sí mismo se conoce de antemano (es `'Unit'`). 3022 | 3023 | #### Escapar palabras clave en Kotlin 3024 | 3025 | Algunas de las palabras clave de Kotlin son identificadores válidos en Java, como por ejemplo `'in'`, `'object'`, `'is'`, etc... Si una biblioteca de Java usa una palabra clave de Kotlin para un método, se puede escapar usando las comillas invertidas (`): 3026 | 3027 | ```kotlin 3028 | // Java 3029 | public class Date { 3030 | public void when(str:String) { .... } 3031 | } 3032 | 3033 | // Kotlin 3034 | date.`when`("2016") 3035 | ``` 3036 | 3037 | #### Null-Safety 3038 | 3039 | Cualquier referencia en Java puede ser nula, lo que hace que los requisitos de Kotlin de seguridad con los valores nulos no sean prácticos para los objetos procedentes de Java. Los tipos de declaraciones de Java se tratan especialmente en Kotlin y se llaman `'platform types'`. Los controles nulos son relajados para tales tipos, por lo que las garantías de seguridad para ellos son las mismas que en Java. 3040 | 3041 | ```kotlin 3042 | val list = ArrayList() // non-null (constructor result) 3043 | list.add("Item") 3044 | val size = list.size // non-null (primitive int) 3045 | val item = list[0] // platform type inferred (ordinary Java object) 3046 | 3047 | item.substring(1) // allowed, may throw an exception if item == null 3048 | ``` 3049 | 3050 | #### 'Checked exceptions' 3051 | 3052 | Kotlin no tiene _'checked exceptions'_. Por lo tanto, los métodos Java que tienen _'checked exceptions'_ se tratan de la misma manera que el resto de métodos. 3053 | 3054 | ### Calling Kotlin from Java 3055 | 3056 | Al igual que Java se puede usar sin problemas en Kotlin, Kotlin se puede usar fácilmente desde Java. 3057 | 3058 | #### Top-level functions 3059 | 3060 | La JVM no admite funciones de nivel superior. Por lo tanto, para hacer que funcionen con Java, el compilador Kotlin crea una clase Java con el nombre del paquete. Las funciones se definen luego como métodos estáticos Java en esta clase, que deben ser instanciados antes de su uso. 3061 | 3062 | ```kotlin 3063 | // Kotlin 3064 | package org.example.utils 3065 | fun cube(n: Int): Int = n * n * n 3066 | 3067 | // Java 3068 | import org.example.utils.Utils; 3069 | UtilsKt.cube(3); 3070 | ``` 3071 | 3072 | Como se indica en la sección de "Anotaciones", podemos indicar al compilador el nombre del fichero con la anotación `'@JvmName'`: 3073 | 3074 | ```kotlin 3075 | // Kotlin 3076 | @file:JvmName("Utils") 3077 | package org.example.utils 3078 | fun cube(n: Int): Int = n * n * n 3079 | 3080 | // Java 3081 | import org.example.utils.Utils; 3082 | Utils.cube(3); 3083 | ``` 3084 | 3085 | #### Default parameters 3086 | 3087 | la JVM no tiene soporte para los parámetros por defecto. Por lo tanto, cuando una función se define con los valores predeterminados, el compilador debe crear una sola función sin los parámetros predeterminados. Sin embargo, podemos indicarle al compilador que cree múltiples sobrecargas de la función para cada parámetro predeterminado con la anotación `'@JvmOverloads'`. Luego, los usuarios de Java pueden ver las diversas funciones y elegir cuál es la más adecuada. Esta anotación funciona tanto para constructores, funciones o métodos estáticos: 3088 | 3089 | ```kotlin 3090 | // Kotlin 3091 | class Foo @JvmOverloads constructor(x: Int, y: Double = 0.0) { 3092 | @JvmOverloads fun f(a: String, b: Int = 0, c: String = "abc") { ... } 3093 | } 3094 | 3095 | // Java 3096 | // Constructors: 3097 | Foo(int x, double y) 3098 | Foo(int x) 3099 | 3100 | // Methods 3101 | void f(String a, int b, String c) { } 3102 | void f(String a, int b) { } 3103 | void f(String a) { } 3104 | ``` 3105 | 3106 | #### Objects and static methods 3107 | 3108 | Los _'named objects'_ y los _'companion objects'_ se generan como instancias **'singleton'** de una clase. Sin embargo, podemos indicar al compilador que genere la función como una método estático en Java con la anotación `'@JvmStatic'`: 3109 | 3110 | ```kotlin 3111 | // Kotlin 3112 | object Console { 3113 | fun clear() : Unit { } // Normal 3114 | @JvmStatic fun exit() : Unit { } // Con anotación 3115 | } 3116 | 3117 | // Java 3118 | Console.INSTANCE.clear() // Normal 3119 | Console.exit() // Con anotación 3120 | ``` 3121 | 3122 | #### Checked exceptions 3123 | 3124 | En Java, solo podemos detectar las _'checked exceptions'_ si están declaradas en el método, incluso si el cuerpo del método lanza esa excepción. Por lo tanto, si tenemos una función que se utilizará desde Java y queremos permitir que las personas detecten una excepción, debemos informar al compilador para que agregue la excepción a la firma del método. Para ello usamos la anotación `'@Throws'`: 3125 | 3126 | ```kotlin 3127 | // Kotlin 3128 | @Throws(IOException::class) 3129 | fun createDirectory(file: File) { 3130 | if (file.exists()) throw IOException("Directory already exists") 3131 | file.createNewFile() 3132 | } 3133 | 3134 | // Java 3135 | try { 3136 | UtilsKt.createDirectory(new File("file.txt")); 3137 | } catch (IOException e) { 3138 | // handle exception here 3139 | } 3140 | ``` 3141 | 3142 | --- 3143 | 3144 | ## Summary 3145 | 3146 | ### Basics 3147 | 3148 | #### Package definition and imports 3149 | 3150 | Package specification should be at the top of the source file: 3151 | 3152 | ```kotlin 3153 | package my.demo 3154 | 3155 | import kotlin.text.* 3156 | 3157 | // ... 3158 | ``` 3159 | 3160 | #### Entry point 3161 | 3162 | An entry point of a Kotlin application is the `main()` function: 3163 | 3164 | ```kotlin 3165 | fun main(args: Array) { 3166 | println("Hello, World") 3167 | } 3168 | ``` 3169 | 3170 | #### Comments 3171 | 3172 | Just like most modern languages, Kotlin supports single-line (or end-of-line) and multi-line (block) comments: 3173 | 3174 | ```kotlin 3175 | // This is an end-of-line comment 3176 | 3177 | /* This is a block comment 3178 | on multiple lines. */ 3179 | ``` 3180 | 3181 | #### Declaring variables 3182 | 3183 | Read-only local variables are defined using the keyword `val`. They can be assigned a value only once: 3184 | 3185 | ```kotlin 3186 | val a: Int = 1 // immediate assignment 3187 | val b = 2 // `Int` type is inferred 3188 | val c: Int // Type required when no initializer is provided 3189 | c = 3 // deferred assignment 3190 | ``` 3191 | 3192 | Variables that can be reassigned use the `var` keyword: 3193 | 3194 | ```kotlin 3195 | var x = 5 // `Int` type is inferred 3196 | x += 1 3197 | ``` 3198 | 3199 | Top-level variables: 3200 | 3201 | ```kotlin 3202 | val PI = 3.14 3203 | var x = 0 3204 | 3205 | fun incrementX() { 3206 | x += 1 3207 | } 3208 | ``` 3209 | 3210 | #### Nullable values and null checks 3211 | 3212 | A reference must be explicitly marked as nullable when `null` value is possible. 3213 | 3214 | ```kotlin 3215 | var name: String? = null 3216 | 3217 | val length: Int 3218 | length = name?.length ?: 0 // length, or 0 if name is null 3219 | length = name?.length ?: return // length, or return when name is null 3220 | length = name?.length ?: throw Error() // length, or throw error when name is null 3221 | ``` 3222 | 3223 | Return `null` if `str` does not hold an integer: 3224 | 3225 | ```kotlin 3226 | fun parseInt(str: String): Int? { 3227 | // ... 3228 | } 3229 | ``` 3230 | 3231 | Use a function returning nullable value: 3232 | 3233 | ```kotlin 3234 | fun printProduct(arg1: String, arg2: String) { 3235 | val x = parseInt(arg1) 3236 | val y = parseInt(arg2) 3237 | 3238 | // Using `x * y` yields error because they may hold nulls. 3239 | if (x != null && y != null) { 3240 | // x and y are automatically cast to non-nullable after null check 3241 | println(x * y) 3242 | } 3243 | else { 3244 | println("'$arg1' or '$arg2' is not a number") 3245 | } 3246 | } 3247 | ``` 3248 | 3249 | #### String templates 3250 | 3251 | ```kotlin 3252 | var a = 1 3253 | // simple name in template: 3254 | val s1 = "a is $a" 3255 | 3256 | a = 2 3257 | // arbitrary expression in template: 3258 | val s2 = "${s1.replace("is", "was")}, but now is $a" 3259 | ``` 3260 | 3261 | ### Control Flow 3262 | 3263 | #### 'If' as an expression 3264 | 3265 | In Kotlin, `if` can also be used as an expression: 3266 | 3267 | ```kotlin 3268 | fun bigger(a: Int, b: Int) = if (a > b) a else b 3269 | ``` 3270 | 3271 | #### 'For' loop 3272 | 3273 | ```kotlin 3274 | val items = listOf("apple", "banana", "kiwifruit") 3275 | for (item in items) { 3276 | println(item) 3277 | } 3278 | ``` 3279 | 3280 | ```kotin 3281 | val items = listOf("apple", "banana", "kiwifruit") 3282 | for (index in items.indices) { 3283 | println("item at $index is ${items[index]}") 3284 | } 3285 | ``` 3286 | 3287 | #### 'While' loop 3288 | 3289 | ```kotlin 3290 | val items = listOf("apple", "banana", "kiwifruit") 3291 | var index = 0 3292 | while (index < items.size) { 3293 | println("item at $index is ${items[index]}") 3294 | index++ 3295 | } 3296 | ``` 3297 | 3298 | #### 'When' expression 3299 | 3300 | ```kotlin 3301 | fun numberTypeName(x: Number) = when(x) { 3302 | 0 -> "Zero" // Equality check 3303 | in 1..4 -> "Four or less" // Range check 3304 | 5, 6, 7 -> "Five to seven" // Multiple values 3305 | is Byte -> "Byte" // Type check 3306 | else -> "Some number" 3307 | } 3308 | ``` 3309 | 3310 | ```kotlin 3311 | fun describe(obj: Any): String = 3312 | when (obj) { 3313 | 1 -> "One" 3314 | "Hello" -> "Greeting" 3315 | is Long -> "Long" 3316 | !is String -> "Not a string" 3317 | else -> "Unknown" 3318 | } 3319 | ``` 3320 | 3321 | #### 'When' expression with predicates 3322 | 3323 | ```kotlin 3324 | fun signAsString(x: Int)= when { 3325 | x < 0 -> "Negative" 3326 | x == 0 -> "Zero" 3327 | else -> "Positive" 3328 | } 3329 | ``` 3330 | 3331 | ### Functions 3332 | 3333 | #### Declaring function 3334 | 3335 | Function having two `Int` parameters with `Int` return type: 3336 | 3337 | ```kotlin 3338 | fun sum(a: Int, b: Int): Int { 3339 | return a + b 3340 | } 3341 | ``` 3342 | 3343 | #### Single-expression function 3344 | 3345 | Function with an expression body and inferred return type: 3346 | 3347 | ```kotlin 3348 | fun sum(a: Int, b: Int) = a + b 3349 | ``` 3350 | 3351 | #### Return 'Unit' 3352 | 3353 | Function returning no meaningful value: 3354 | 3355 | ```kotlin 3356 | fun printSum(a: Int, b: Int): Unit { 3357 | println("sum of $a and $b is ${a + b}") 3358 | } 3359 | ``` 3360 | 3361 | `Unit` return type can be omitted: 3362 | 3363 | ```kotlin 3364 | fun printSum(a: Int, b: Int) { 3365 | println("sum of $a and $b is ${a + b}") 3366 | } 3367 | ``` 3368 | 3369 | #### Function types 3370 | 3371 | `() -> Unit` - takes no arguments and returns nothing (Unit). 3372 | `(Int, Int) -> Int` - takes two arguments of type Int and returns Int. 3373 | `(() -> Unit) -> Int` - takes another function and returns Int. 3374 | `(Int) -> () -> Unit` - takes argument of type Int and returns function. 3375 | 3376 | #### Function literals 3377 | 3378 | ```kotlin 3379 | // Simple lambda expression 3380 | val add: (Int, Int) -> Int = { i, j -> i + j } 3381 | 3382 | val printAndDouble: (Int) -> Int = { 3383 | println(it) 3384 | // When single parameter, we can reference it using `it` 3385 | it * 2 // In lambda, last expression is returned 3386 | } 3387 | 3388 | // Anonymous function alternative 3389 | val printAndDoubleFun: (Int) -> Int = fun(i: Int): Int { 3390 | println(i) // Single argument can’t be referenced by `it` 3391 | return i * 2 // Needs return like any function 3392 | } 3393 | 3394 | val i = printAndDouble(10) // 10 3395 | print(i) // 20 3396 | ``` 3397 | 3398 | #### Extension functions 3399 | 3400 | ```kotlin 3401 | fun Int.isEven() = this % 2 == 0 3402 | print(2.isEven()) // true 3403 | 3404 | fun List.average() = 1.0 * sum() / size 3405 | print(listOf(1, 2, 3, 4).average()) // 2.5 3406 | ``` 3407 | 3408 | ### Classes 3409 | 3410 | #### Primary constructor 3411 | 3412 | ```kotlin 3413 | // val declares a read-only property, var a mutable one 3414 | class Person(val name: String, var age: Int) 3415 | // name is read-only, age is mutable 3416 | ``` 3417 | 3418 | #### Inheritance 3419 | 3420 | ```kotlin 3421 | open class Person(val name: String) { 3422 | open fun hello() = "Hello, I am $name" 3423 | // Final by default so we need open 3424 | } 3425 | 3426 | class PolishPerson(name: String) : Person(name) { 3427 | override fun hello() = "Dzień dobry, jestem $name" 3428 | } 3429 | ``` 3430 | 3431 | #### Properties with accessors 3432 | 3433 | ```kotlin 3434 | class Person(var name: String, var surname: String) { 3435 | var fullName: String 3436 | get() = "$name $surname" 3437 | set(value) { 3438 | val (first, rest) = value.split(" ", limit = 2) 3439 | name = first 3440 | surname = rest 3441 | } 3442 | } 3443 | ``` 3444 | 3445 | #### Data classes 3446 | 3447 | ```kotlin 3448 | data class Person(val name: String, var age: Int) 3449 | val mike = Person("Mike", 23) 3450 | 3451 | // Modifier data adds: 3452 | // 1. toString that displays all primary constructor properties 3453 | print(mike.toString()) // Person(name=Mike, age=23) 3454 | 3455 | // 2. equals that compares all primary constructor properties 3456 | print(mike == Person("Mike", 23)) // True 3457 | print(mike == Person("Mike", 21)) // False 3458 | 3459 | // 3. hashCode that is based on all primary constructor properties 3460 | val hash = mike.hashCode() 3461 | print(hash == Person("Mike", 23).hashCode()) // True 3462 | print(hash == Person("Mike", 21).hashCode()) // False 3463 | 3464 | // 4. component1, component2 etc. that allows deconstruction 3465 | val (name, age) = mike 3466 | print("$name $age") // Mike 23 3467 | 3468 | // 5. copy that returns copy of object with concrete properties changed 3469 | val jake = mike.copy(name = "Jake") 3470 | ``` 3471 | 3472 | ### Collection Literals 3473 | 3474 | ```kotlin 3475 | listOf(1,2,3,4) // List 3476 | mutableListOf(1,2,3,4) // MutableList 3477 | 3478 | setOf("A", "B", "C") // Set 3479 | mutableSetOf("A", "B", "C") // MutableSet 3480 | 3481 | arrayOf('a', 'b', 'c') // Array 3482 | 3483 | mapOf(1 to "A", 2 to "B") // Map 3484 | mutableMapOf(1 to "A", 2 to "B") 3485 | // MutableMap 3486 | 3487 | sequenceOf(4,3,2,1) // Sequence 3488 | 3489 | 1 to "A" // Pair 3490 | 3491 | List(4) { it * 2 } // List 3492 | generateSequence(4) { it + 2 } // Sequence 3493 | ``` 3494 | 3495 | ### Collection Processing 3496 | 3497 | ```kotlin 3498 | students 3499 | .fiter { it.passing && it.averageGrade > 4.0 } 3500 | // Only passing students 3501 | .sortedByDescending { it.averageGrade } 3502 | // Starting from ones with biggest grades 3503 | .take(10) // Take first 10 3504 | .sortedWith(compareBy({ it.surname }, { it.name })) 3505 | // Sort by surname and then name 3506 | 3507 | generateSequence(0) { it + 1 } 3508 | // Infinitive sequence of next numbers starting on 0 3509 | .filter { it % 2 == 0 } // Keep only even 3510 | .map { it * 3 } // Triple every one 3511 | .take(100) // Take first 100 3512 | .average() // Count average 3513 | 3514 | // Most important functions for collection processing 3515 | val l = listOf(1,2,3,4) 3516 | //filter - returns only elements matched by predicate 3517 | l.filter { it % 2 == 0 } // [2, 4] 3518 | 3519 | // map - returns elements after transformation 3520 | l.map { it * 2 } // [2, 4, 6, 8] 3521 | 3522 | // flatMap - returns elements yielded from results of trans. 3523 | l.flatMap { listOf(it, it + 10) } // [1, 11, 2, 12, 3, 13, 4, 14] 3524 | 3525 | // fold/reduce - accumulates elements 3526 | l.fold(0.0) { acc, i -> acc + i } // 10.0 3527 | l.reduce { acc, i -> acc * i } // 24 3528 | 3529 | // forEach/onEach - perfons an action on every element 3530 | l.forEach { print(it) } // Prints 1234, returns Unit 3531 | l.onEach { print(it) } // Prints 1234, returns [1, 2, 3, 4] 3532 | 3533 | // partition - splits into pair of lists 3534 | val (even, odd) = l.partition { it % 2 == 0 } 3535 | print(even) // [2, 4] 3536 | print(odd) // [1, 3] 3537 | 3538 | // min/max/minBy/maxBy 3539 | l.min() // 1, possible because we can compare Int 3540 | l.minBy { -it } // 4 3541 | l.max() // 4, possible because we can compare Int 3542 | l.maxBy { -it } // 1 3543 | 3544 | // first/firstBy 3545 | l.first() // 1 3546 | l.first { it % 2 == 0 } // 2 (first even number) 3547 | 3548 | // count - count elements matched by predicate 3549 | l.count { it % 2 == 0 } // 2 3550 | 3551 | // sorted/sortedBy - returns sorted collection 3552 | listOf(2,3,1,4).sorted() // [1, 2, 3, 4] 3553 | l.sortedBy { it % 2 } // [2, 4, 1, 3] 3554 | 3555 | // groupBy - group elements on collection by key 3556 | l.groupBy { it % 2 } // Map: {1=[1, 3], 0=[2, 4]} 3557 | 3558 | // distinct/distinctBy - returns only unique elements 3559 | listOf(1,1,2,2).distinct() // [1, 2] 3560 | ``` 3561 | 3562 | #### Mutable vs immutable collection processing functions 3563 | 3564 | ```kotlin 3565 | val list = mutableListOf(3,4,2,1) 3566 | val sortedResult = list.sorted() // Returns sorted 3567 | println(sortedResult) // [1, 2, 3, 4] 3568 | println(list) // [3, 4, 2, 1] 3569 | 3570 | val sortResult = list.sort() // Sorts mutable collection 3571 | println(sortResult) // kotlin.Unit 3572 | println(list) // [1, 2, 3, 4] 3573 | ``` 3574 | 3575 | ### Extension Functions 3576 | 3577 | | | Returns 'Receiver' | Returns 'Results of lambda' | 3578 | | :---------------------------- | :----------------: | :-------------------------: | 3579 | | Reference to receiver: 'it' | also | let | 3580 | | Reference to receiver: 'this' | apply | run/with | 3581 | 3582 | ```kotlin 3583 | val dialog = Dialog().apply { 3584 | title = "Dialog title" 3585 | onClick { print("Clicked") } 3586 | } 3587 | ``` 3588 | 3589 | ### Delegates 3590 | 3591 | ```kotlin 3592 | // Lazy - calculates value before first usage 3593 | val i by lazy { print("init "); 10 } 3594 | print(i) // Prints: init 10 3595 | print(i) // Prints: 10 3596 | 3597 | // notNull - returns last setted value, or throws error if no value has been set 3598 | 3599 | // observable/vetoable - calls function every time value changes. In vetoable function also decides if new value should be set. 3600 | var name by observable("Unset") { p, old, new -> 3601 | println("${p.name} changed $old -> $new") 3602 | } 3603 | name = "Marcin" 3604 | // Prints: name changed Unset -> Marcin 3605 | 3606 | // Map/MutableMap - finds value on map by property name 3607 | val map = mapOf("a" to 10) 3608 | val a by map 3609 | print(a) // Prints: 10 3610 | ``` 3611 | 3612 | ### Visibility Modifiers 3613 | 3614 | | Modifier | Class members | Top-level | 3615 | | :--------------- | :------------------------------------------------ | :------------------------- | 3616 | | Public (default) | Visible everywhere | Visible everywhere | 3617 | | Private | Visible only in the same class | Visible in the same class | 3618 | | Protected | Visible only in the sambe class and subclasses | Not allowed | 3619 | | Internal | Visible in the same module if class is accessible | Visible in the same module | 3620 | 3621 | --- 3622 | 3623 | ## Referencias 3624 | 3625 | - 3626 | - 3627 | - 3628 | - 3629 | - 3630 | - 3631 | - 3632 | - 3633 | - 3634 | 3635 | ## Licencia 3636 | 3637 | [![Licencia de Creative Commons](https://i.creativecommons.org/l/by-sa/4.0/80x15.png)](http://creativecommons.org/licenses/by-sa/4.0/) 3638 | Esta obra está bajo una [licencia de Creative Commons Reconocimiento-Compartir Igual 4.0 Internacional](http://creativecommons.org/licenses/by-sa/4.0/). 3639 | -------------------------------------------------------------------------------- /README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxgcrz/_learning_kotlin_/6d4e5f3ebd95e50dd2f69de3cc5290e9538495f8/README.pdf -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "learning_kotlin", 3 | "version": "1.0.0", 4 | "description": "Learning Kotlin", 5 | "private": true, 6 | "main": "README.md", 7 | "author": "@alxgcrz", 8 | "license": "CC-BY-4.0", 9 | "homepage": "https://alxgcrz.com/kotlin.html", 10 | "scripts": { 11 | "pandoc:create": "pandoc -d custom", 12 | "pandoc:copy": "copy %npm_config_filename% %npm_config_html%", 13 | "pdf:create": "mdpdf README.md README.pdf", 14 | "pdf:copy": "copy README.pdf %npm_config_pdf%", 15 | "live": "live-server %npm_config_filename%" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/alxgcrz/_learning_kotlin_.git" 20 | } 21 | } 22 | --------------------------------------------------------------------------------