{"id":671776,"date":"2025-12-31T04:14:22","date_gmt":"2025-12-31T03:14:22","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=kotlin&#038;p=671776"},"modified":"2025-12-31T04:14:26","modified_gmt":"2025-12-31T03:14:26","slug":"evaluacion-de-kotlin-en-proyectos-reales","status":"publish","type":"kotlin","link":"https:\/\/blog.jetbrains.com\/es\/kotlin\/2025\/12\/evaluacion-de-kotlin-en-proyectos-reales\/","title":{"rendered":"Evaluaci\u00f3n de Kotlin en proyectos reales"},"content":{"rendered":"<p><em>Art\u00edculo de <a href=\"https:\/\/www.linkedin.com\/in\/urs-peter-70a2882\/\" target=\"_blank\" rel=\"noreferrer noopener\" data-type=\"link\" data-id=\"https:\/\/bit.ly\/urs-peter-linked-in\">Urs Peter<\/a>, ingeniero de software s\u00e9nior y formador de Kotlin certificado por JetBrains. Para los lectores que busquen una forma m\u00e1s estructurada de desarrollar sus habilidades en Kotlin, Urs tambi\u00e9n dirige el <\/em><a href=\"https:\/\/academy.xebia.com\/upskilling\/kotlin-academy\/\" target=\"_blank\" rel=\"noreferrer noopener\"><em>Kotlin Upskill Program<\/em><\/a><em> en Xebia Academy.<\/em><\/p>\n<p><em>Este es el segundo art\u00edculo de <\/em><strong><em>La gu\u00eda definitiva para adoptar Kotlin en un entorno dominado por Java<\/em><\/strong><em>, una serie que sigue c\u00f3mo crece la adopci\u00f3n de Kotlin entre equipos reales, desde la curiosidad de un \u00fanico desarrollador hasta la transformaci\u00f3n de toda la empresa.<\/em><\/p>\n<p>Lea la primera parte: <a href=\"https:\/\/blog.jetbrains.com\/kotlin\/2025\/10\/getting-started-with-kotlin-for-java-developers\/\" target=\"_blank\" rel=\"noreferrer noopener\">Introducci\u00f3n a Kotlin para desarrolladores Java<\/a><\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n<h2 class=\"wp-block-heading\"><strong>La fase de evaluaci\u00f3n: m\u00e1s all\u00e1 de la experimentaci\u00f3n con Kotlin<\/strong><\/h2>\n<p>Una vez que se sienta c\u00f3modo haciendo pruebas en Kotlin, es hora de una evaluaci\u00f3n m\u00e1s exhaustiva. Puede recurrir a dos enfoques principales:<\/p>\n<ol>\n<li><a href=\"#1.-build-a-new-microservice\/application-in-kotlin\" data-type=\"link\" data-id=\"https:\/\/blog.jetbrains.com\/#1.-build-a-new-microservice\/application-in-kotlin\">Crear un nuevo microservicio o aplicaci\u00f3n en Kotlin<\/a><\/li>\n<li><a href=\"#2.-extend\/convert-an-existing-java-application\" data-type=\"link\" data-id=\"https:\/\/blog.jetbrains.com\/#2.-extend\/convert-an-existing-java-application\">Ampliar o convertir una aplicaci\u00f3n Java existente<\/a><\/li>\n<\/ol>\n<h3 id=\"1.-build-a-new-microservice\/application-in-kotlin\" class=\"wp-block-heading\"><strong>1. Crear un nuevo microservicio o aplicaci\u00f3n en Kotlin<\/strong><\/h3>\n<p>Empezando de cero con una nueva aplicaci\u00f3n o microservicio disfrutar\u00e1 de toda la experiencia Kotlin sin las limitaciones del c\u00f3digo heredado. Este enfoque suele ser el m\u00e1s pr\u00e1ctico para el aprendizaje, y muestra con mayor claridad los puntos fuertes de Kotlin.<\/p>\n<div class=\"wp-block-columns is-layout-flex wp-container-3 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis: 66.66%;\">\n<p><strong>Consejo pro<\/strong>: pida ayuda a un experto durante esta fase. Aunque los desarrolladores conf\u00edan en sus capacidades por naturaleza, evitar los primeros errores en forma de Kotlin al estilo Java y la falta de bibliotecas impulsadas por Kotlin puede ahorrar meses de deuda t\u00e9cnica.<\/p>\n<\/div>\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis: 33.33%;\">\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-646384\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/10\/unnamed-5.png\" alt=\"\" width=\"600\" height=\"400\" \/><\/figure>\n<\/div>\n<\/div>\n<p>As\u00ed es como puede evitar los errores m\u00e1s comunes al utilizar Kotlin desde un entorno Java:<\/p>\n<p><em>Error: elegir un marco de trabajo diferente del que utiliza en Java.<\/em><\/p>\n<p>Consejo: <strong>c\u00ed\u00f1ase a su marco actual<\/strong>.\u00a0<\/p>\n<p>Lo m\u00e1s probable es que haya utilizado Spring Boot con Java, as\u00ed que util\u00edcelo tambi\u00e9n con Kotlin. La compatibilidad de Spring Boot con Kotlin es excepcional, por lo que no le beneficia de ning\u00fan modo utilizar otra cosa. Es m\u00e1s, se ver\u00eda obligado a aprender no solo un nuevo lenguaje, sino tambi\u00e9n un nuevo marco de trabajo, lo que \u00fanicamente a\u00f1ade complejidad sin aportar ninguna ventaja.<\/p>\n<p><strong>Importante<\/strong>: Spring interfiere con el principio de \u00abherencia por dise\u00f1o\u00bb de Kotlin, que exige marcar expl\u00edcitamente las clases abiertas para poder extenderlas.<\/p>\n<p>Para evitar a\u00f1adir la palabra clave abierta a todas las clases relacionadas con Spring (como <code>@Configuration<\/code>, etc.), utilice el siguiente complemento de compilaci\u00f3n: <a href=\"https:\/\/kotlinlang.org\/docs\/all-open-plugin.html#spring-support\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/kotlinlang.org\/docs\/all-open-plugin.html#spring-support<\/a>. Si crea un proyecto Spring con la conocida herramienta en l\u00ednea <a href=\"https:\/\/start.spring.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">Spring initializr<\/a>, este complemento de compilaci\u00f3n ya estar\u00e1 configurado para usted.<\/p>\n<p><em>Error: escribir Kotlin al estilo Java, confiando en las API comunes de Java en lugar de en la biblioteca est\u00e1ndar de Kotlin:<\/em><em>\u00a0<\/em><\/p>\n<p>Esta lista puede ser muy larga, as\u00ed que centr\u00e9monos en los errores m\u00e1s comunes:<\/p>\n<h4 class=\"wp-block-heading\"><em>Error 1: utilizar Java Stream en lugar de Kotlin Collections.<\/em><\/h4>\n<p>Consejo: <strong>utilice siempre Kotlin Collections.<\/strong><\/p>\n<p>Las Kotlin Collections son totalmente interoperables con las Java Collections, pero est\u00e1n equipadas con funciones de orden superior sencillas y cargadas de funcionalidades que dejan Java Stream obsoleto.\u00a0<\/p>\n<p>A continuaci\u00f3n se muestra un ejemplo cuyo objetivo es elegir los 3 productos m\u00e1s vendidos por ingresos (precio * n\u00famero vendido) agrupados por categor\u00eda de producto:<\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">record Product(String name, String category, double price, int sold){}\n\nList products = List.of(\n           new Product(\"Lollipop\", \"sweets\", 1.2, 321),\n           new Product(\"Broccoli\", \"vegetable\", 1.8, 5);\n\nMap&lt;String, List&gt; top3RevenueByCategory =\n       products.stream()\n          .collect(Collectors.groupingBy(\n                Product::category,\n                Collectors.collectingAndThen(\n                    Collectors.toList(),\n                    list -&gt; list.stream()\n                              .sorted(Comparator.comparingDouble(\n                                  (Product p) -&gt; p.price() * p.sold())\n                                   .reversed())\n                                   .limit(3)\n                                   .toList()\n                       \t\t)\n          )\n);<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">val top3RevenueByCategory: Map&lt;String, List&gt; =\n   products.groupBy { it.category }\n       .mapValues { (_, list) -&gt;\n           list.sortedByDescending { it.price * it.sold }.take(3)\n       }<\/pre>\n<p>La interoperabilidad entre Kotlin y Java le permite trabajar con clases y registros Java como si fueran nativos de Kotlin, aunque tambi\u00e9n podr\u00eda utilizar una clase (de datos) Kotlin en su lugar.<\/p>\n<h4 class=\"wp-block-heading\"><em>Error 2: seguir utilizando los Optional de Java.<\/em><\/h4>\n<p>Consejo: <strong>adopte los tipos anulables<\/strong>.\u00a0<\/p>\n<p>Una de las principales razones por las que los desarrolladores Java se pasan a Kotlin es por la compatibilidad con la nulabilidad integrada en Kotlin, que dice adi\u00f3s a las NullPointerExceptions. Por lo tanto, intente utilizar solo tipos \u00abNullable\u00bb, y no use m\u00e1s \u00abOptionals\u00bb. \u00bfTodav\u00eda tiene \u00abOptionals\u00bb en sus interfaces? As\u00ed es como puede deshacerse f\u00e1cilmente de ellos convirti\u00e9ndolos en tipos \u00abNullable\u00bb:<\/p>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/Let\u2019s assume this repository is hard to change, because it\u2019s a library you depend on\nclass OrderRepository {\n      \/\/it returns Optional, but we want nullable types\n      fun getOrderBy(id: Long): Optional = \u2026\n}\n\n\/\/Simply add an extension method and apply the orElse(null) trick\nfun OrderRepository.getOrderByOrNull(id: Long): Order? = \n                                    getOrderBy(id).orElse(null)\n\n\/\/Now enjoy the safety and ease of use of nullable types:\n\n\/\/Past:\n val g = repository.getOrderBy(12).flatMap { product -&gt;\n     product.goody.map { it.name }\n}.orElse(\"No goody found\")\n\n\/\/Future:\n val g = repository.getOrderByOrNull(12)?.goody?.name ?: \"No goody found\"<\/pre>\n<h4 class=\"wp-block-heading\"><em>Error 3: seguir utilizando envoltorios est\u00e1ticos.<\/em><\/h4>\n<p>Consejo: <strong>adopte los m\u00e9todos de extensi\u00f3n<\/strong>.\u00a0<\/p>\n<p>Los m\u00e9todos de extensi\u00f3n le ofrecen muchas ventajas:<\/p>\n<ul>\n<li>Hacen que su c\u00f3digo sea mucho m\u00e1s fluido y legible que los envoltorios.<\/li>\n<li>Se pueden encontrar con la finalizaci\u00f3n de c\u00f3digo, lo que no ocurre con los envoltorios.<\/li>\n<li>Dado que las extensiones deben importarse, le permiten utilizar selectivamente la funcionalidad ampliada en una secci\u00f3n espec\u00edfica de su aplicaci\u00f3n.<\/li>\n<\/ul>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/Very common approach in Java to add additional helper methods\npublic class DateUtils {\n      public static final DateTimeFormatter DEFAULT_DATE_TIME_FORMATTER = \n           DateTimeFormatter.ofPattern(\"yyyy-MM-dd HH:mm:ss\");\n\n      public String formatted(LocalDateTime dateTime, \n\t\t              DateTimeFormatter formatter) {\n         return dateTime.format(formatter);\n      }\n\n      public String formatted(LocalDateTime dateTime) {\n         return formatted(dateTime, DEFAULT_DATE_TIME_FORMATTER);\n      }\n}\n\n\/\/Usage\n formatted(LocalDateTime.now());\n<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">val DEFAULT_DATE_TIME_FORMATTER: DateTimeFormatter = \nDateTimeFormatter.ofPattern(\"yyyy-MM-dd HH:mm:ss\")\n\n\/\/Use an extension method, including a default argument, which omits the need for an overloaded method.\u00a0\nfun LocalDateTime.asString(\n\u00a0\u00a0\u00a0formatter: DateTimeFormatter = DEFAULT_DATE_TIME_FORMATTER): String =\u00a0\n      this.format(formatter)\n\n\/\/Usage\nLocalDateTime.now().formatted()<\/pre>\n<p>Tenga en cuenta que Kotlin ofrece m\u00e9todos y variables de nivel superior. Esto implica que podemos simplemente declarar, por ejemplo, el <em><code>DEFAULT_DATE_TIME_FORMATTER<\/code><\/em> de nivel superior sin necesidad de vincularlo a un objeto como ocurre en Java.<\/p>\n<h4 class=\"wp-block-heading\"><em>Error 4: depender (torpemente) de las API de Java.<\/em><\/h4>\n<p>Consejo: <strong>utilice la correspondiente de Kotlin.\u00a0<\/strong><\/p>\n<p>La biblioteca est\u00e1ndar de Kotlin utiliza m\u00e9todos de extensi\u00f3n para hacer que las bibliotecas Java sean mucho m\u00e1s f\u00e1ciles de usar, aunque la implementaci\u00f3n subyacente siga siendo Java. Casi todas las principales bibliotecas y marcos de trabajo de terceros, como Spring, han hecho lo mismo.<\/p>\n<p><strong>Ejemplo de biblioteca est\u00e1ndar:<\/strong><\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">String text;\ntry (\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var reader = new BufferedReader(\n                  new InputStreamReader(new FileInputStream(\"out.txt\"), \n            StandardCharsets.UTF_8))) {\n\u00a0\u00a0\u00a0text = reader\n            .lines()\n            .collect(Collectors.joining(System.lineSeparator()));\n}\nSystem.out.println(\"Downloaded text: \" +\u00a0 text + \"n\");<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/Kotlin has enhanced the Java standard library with many powerful extension methods, like on java.io.*, which makes input stream processing a snap due to its fluent nature, fully supported by code completion\n\nval text = FileInputStream(\"path\").use {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0     it.bufferedReader().readText()\n           }\nprintln(\"Downloaded text: $textn\");<\/pre>\n<p><strong>Ejemplo en Spring:<\/strong><br \/><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">final var books =\u00a0 RestClient.create()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.get()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.uri(\"http:\/\/...\/api\/books\")\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.retrieve()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.body( new ParameterizedTypeReference&lt;List&gt;(){}); \/\/ \u21e6 inconvenient ParameterizedTypeReference<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import org.springframework.web.client.body\nval books = RestClient.create()\n\u00a0\u00a0\u00a0.get()\n\u00a0\u00a0\u00a0.uri(\"http:\/\/...\/api\/books\")\n\u00a0\u00a0\u00a0.retrieve()\n\u00a0\u00a0\u00a0.body&lt;List&gt;() \/\/\u21e6 Kotlin offers an extension that only requires the type without the need for a ParameterizedTypeReference<\/pre>\n<h4 class=\"wp-block-heading\"><em>Error 5: utilizar un archivo distinto para cada clase p\u00fablica.<\/em><\/h4>\n<p>Consejo: <strong>combine clases p\u00fablicas relacionadas en un \u00fanico archivo.\u00a0<\/strong><\/p>\n<p>Esto le permite hacerse una idea de c\u00f3mo est\u00e1 estructurado un (sub)dominio sin tener que navegar por decenas de archivos.<\/p>\n<p><strong>Java<\/strong><\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-647331\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/10\/unnamed-15.png\" alt=\"\" width=\"1114\" height=\"934\" \/><\/figure>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/For domain classes consider data classes - see why below\ndata class User(val email: String,\n            \/\/Use nullable types for safety and expressiveness\n\u00a0 \u00a0        val avatarUrl: URL? = null,\u00a0\n           var isEmailVerified: Boolean)\n\ndata class Account(val user:User,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0val address: Address,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0val mfaEnabled:Boolean,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0val createdAt: Instant)\n\ndata class Address(val street: String,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0val city: String,\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 val postalCode: String)<\/pre>\n<h4 class=\"wp-block-heading\"><em>Error 6: depender del paradigma de programaci\u00f3n mutable.<\/em><\/h4>\n<p>Consejo: <strong>adopte la inmutabilidad, el valor predeterminado en Kotlin<\/strong><\/p>\n<p>La tendencia en muchos lenguajes de programaci\u00f3n (incluido Java) es clara: la inmutabilidad est\u00e1 ganando a la mutabilidad.\u00a0<\/p>\n<p>La raz\u00f3n es sencilla: la inmutabilidad evita efectos secundarios no deseados, lo que hace que el c\u00f3digo sea m\u00e1s seguro, m\u00e1s predecible y m\u00e1s f\u00e1cil de razonar. Tambi\u00e9n simplifica la concurrencia, ya que los datos inmutables se pueden compartir libremente entre hilos sin riesgo de condiciones de carrera.<\/p>\n<p>Por eso la mayor\u00eda de los lenguajes modernos \u2014Kotlin entre ellos\u2014 enfatizan la inmutabilidad predeterminada o la fomentan en gran medida. En Kotlin, la inmutabilidad es la opci\u00f3n predeterminada, aunque la mutabilidad sigue siendo una opci\u00f3n cuando es realmente necesaria.<\/p>\n<p>He aqu\u00ed una gu\u00eda r\u00e1pida de la <strong>potencia de la inmutabilidad<\/strong> de Kotlin:<\/p>\n<p>1. Use <code>val<\/code> en lugar de <code>var<\/code><\/p>\n<p>Priorice <code>val<\/code> sobre <code>var<\/code>. IntelliJ IDEA le notificar\u00e1 si ha utilizado un <code>var<\/code> para el que se podr\u00eda utilizar un <code>val<\/code>.\u00a0<\/p>\n<p>2. Utilice clases de datos (inmutables) con <code>copy(...)<\/code><\/p>\n<p>Para las clases relacionadas con dominios, utilice clases <code>data<\/code> con <code>val<\/code>. Las clases <code>data<\/code> de Kotlin se comparan a menudo con los <code>records<\/code> de Java. Aunque existe cierto solapamiento, las clases <code>data<\/code> ofrecen la genial funcionalidad <code>copy(...)<\/code>, cuya ausencia hace que la transformaci\u00f3n de <code>record<\/code> (a menudo necesaria en la l\u00f3gica empresarial) sea tan tediosa:<\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/only immutable state\npublic record Person(String name, int age) {\n\u00a0\u00a0\u00a0\/\/Lack of default parameters requires overloaded constructor\n\u00a0\u00a0\u00a0public Person(String name) {\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this(name, 0);\n\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\/\/+ due to lack of String interpolation\n\u00a0 public String sayHi() {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return \"Hello, my name is \" + name + \" and I am \" + age + \" years old.\";\n\u00a0\u00a0\u00a0}\n}\n\n\/\/Usage\nfinal var jack = new Person(\"Jack\", 42);\njack: Person[name=Jack, age=5]\n\n\/\/The issue is here: transforming a record requires manually copying the identical state to the new instance \u2639\ufe0f\nfinal var fred = new Person(\"Fred\", jack.name);<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/also supports mutable state (var)\ndata class Person(val name: String,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0val age: Int = 0) {\n\u00a0 \/\/string interpolation\n\u00a0 fun sayHi() = \"Hi, my name is $name and I am $age years old.\"\n}\nval jack = Person(\"Jack\", 42)\njack: Person(name=Jack, age=42)\n\n\/\/Kotlin offers the copy method, which, due to the \u2018named argument\u2019 feature, allows you to only adjust the state you want to change \ud83d\ude03\nval fred = jack.copy(name = \"Fred\")\nfred: Person(name=Fred, age=42)<\/pre>\n<p>As\u00ed pues, utilice clases de datos para clases relacionadas con dominios siempre que sea posible. Su naturaleza inmutable garantiza una experiencia segura, concisa y sin complicaciones al trabajar con el n\u00facleo de su aplicaci\u00f3n.\u00a0\u00a0\u00a0\u00a0\u00a0<\/p>\n<p>Consejo: <strong>priorice las colecciones inmutables sobre las mutables<\/strong><\/p>\n<p>Las colecciones inmutables tienen ventajas claras en cuanto a la seguridad de los subprocesos, pueden pasarse de una a otra de forma segura y son m\u00e1s f\u00e1ciles de razonar. Aunque Java ofrece algunas funcionalidades de inmutabilidad para las colecciones, su uso es peligroso, porque provoca f\u00e1cilmente excepciones durante la ejecuci\u00f3n:<\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">List.of(1,2,3).add(4); \u274cunsafe \ud83d\ude2c! .add(...) compiles, but throws UnsupportedOperationException<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/The default collections in Kotlin are immutable (read-only)\nlistOf(1,2,3).add(4); \u00a0\/\/\u2705safe: does not compile\n\nval l0 = listOf(1,2,3)\u00a0\nval l1 = l0 + 4 \/\/\u2705safe: it will return a new list containing the added element\nl1 shouldBe listOf(1,2,3,4) \/\/\u2705<\/pre>\n<p>Lo mismo se aplica al uso de <code>Collections.<em>unmodifiableList(...)<\/em><\/code><em>, <\/em>que no solo no es seguro, sino que adem\u00e1s requiere una asignaci\u00f3n adicional:<\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class PersonRepo {\n\u00a0\u00a0\u00a0private final List cache = new ArrayList();\n\u00a0\u00a0\u00a0\/\/ Java \u2013 must clone or wrap every call\n\u00a0\u00a0\u00a0public List getItems() {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return Collections.unmodifiableList(cache); \u00a0 \/\/\u26a0\ufe0fextra alloc\n\u00a0\u00a0\u00a0}\n}\n\n\/\/Usage\npersonRepo.getItems().add(joe) \u274cunsafe \ud83d\ude2c! .add(...) can be called but throws UnsupportedOperationException<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class PersonRepo {\n\n\/\/The need to type \u2018mutable\u2019 for mutable collections is intentional: Kotlin wants you to use immutable ones by default. But sometimes you need them:\n\n\u00a0\u00a0\u00a0private val cache: MutableList = mutableListOf()\n\n\u00a0\u00a0\u00a0fun items(): List = cache \/\/\u2705safe: though the underlying collection is mutable, by returning it as its superclass List, it only exposes the read-only interface\n\n}\n\n\/\/Usage\npersonRepo.items().add(joe) \/\/\u2705safe:\ud83d\ude2c! Does not compile<\/pre>\n<p>Cuando se trata de concurrencia, deben preferirse las estructuras de datos inmutables, incluidas las colecciones. En Java, se requiere un mayor esfuerzo con las colecciones especiales que ofrecen una API diferente o limitada, como <code>CopyOnWriteArrayList<\/code>. En Kotlin, por otro lado, la lista de solo lectura <code>List<\/code> es aplicable a casi todos los casos de uso.\u00a0<\/p>\n<p>Si necesita colecciones mutables y a prueba de hilos, Kotlin ofrece colecciones persistentes (<code>persistentListOf(...)<\/code>, <code>persistentMapOf(...)<\/code>), que comparten la misma potente interfaz.<\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ConcurrentHashMap persons = new ConcurrentHashMap();\npersons.put(\"Alice\", 23);\npersons.put(\"Bob\", \u00a0 21);\n\n\/\/not fluent and data copying going on\nMap incPersons = new HashMap(persons.size());\npersons.forEach((k, v) -&gt; incPersons.put(k, v + 1));\n\n\/\/wordy and data copying going on\npersons\n\u00a0\u00a0\u00a0.entrySet()\n\u00a0\u00a0\u00a0.stream()\n\u00a0\u00a0\u00a0.forEach(entry -&gt;\u00a0\n      entry.setValue(entry.getValue() + 1));<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">persistentMapOf(\"Alice\" to 23, \"Bob\" to 21)\n\u00a0\u00a0\u00a0      .mapValues { (key, value) -&gt; value + 1 } \/\/\u2705same rich API like any other Kotlin Map type and not data copying going on<\/pre>\n<h4 class=\"wp-block-heading\"><em>Error 7: seguir utilizando constructores (o peor a\u00fan: intentar utilizar Lombok).\u00a0<\/em><\/h4>\n<p>Consejo: <strong>utilice argumentos con nombre.<\/strong><\/p>\n<p>Los constructores son muy comunes en Java. Aunque son pr\u00e1cticos, a\u00f1aden c\u00f3digo adicional, no son seguros y aumentan la complejidad. En Kotlin, no sirven de nada, ya que una simple funcionalidad del lenguaje los hace obsoletos: los argumentos con nombre.\u00a0<\/p>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public record Person(String name, int age) {\n\n\u00a0\u00a0\u00a0\/\/ Builder for Person\n\u00a0\u00a0\u00a0public static class Builder {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0private String name;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0private int age;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0public Builder() {}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0public Builder name(String name) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.name = name;\n\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return this;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0public Builder age(int age) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.age = age;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return this;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0public Person build() {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return new Person(name, age);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0}\n}\n\n\/\/Usage\nnew JPerson.Builder().name(\"Jack\").age(36).build(); \/\/compiles and succeeds at runtime\n\nnew JPerson.Builder().age(36).build(); \/\/\u274cunsafe \ud83d\ude2c: compiles but fails at runtime.<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">data class Person(val name: String, val age: Int = 0)\n\n\/\/Usage - no builder, only named arguments.\nPerson(name = \"Jack\") \/\/\u2705safe: if it compiles, it always succeeds at runtime\nPerson(name = \"Jack\", age = 36) \/\/\u2705<\/pre>\n<h3 id=\"2.-extend\/convert-an-existing-java-application\" class=\"wp-block-heading\"><strong>2. Ampliar\/convertir una aplicaci\u00f3n Java existente<\/strong><\/h3>\n<p>Si no tiene ninguna opci\u00f3n de nueva creaci\u00f3n para probar Kotlin, <a href=\"https:\/\/www.jetbrains.com\/help\/idea\/get-started-with-kotlin.html#add-kotlin-dependency\" target=\"_blank\" rel=\"noreferrer noopener\">a\u00f1ada nuevas funcionalidades de Kotlin o m\u00f3dulos enteros de Kotlin a una base de c\u00f3digo Java existente <\/a>. Gracias a la interoperabilidad fluida de Kotlin con Java, puede escribir c\u00f3digo Kotlin que parece Java para los llamadores desde Java. Este enfoque permite lo siguiente:<\/p>\n<ul>\n<li>Migraci\u00f3n gradual sin grandes reescrituras<\/li>\n<li>Pruebas reales de Kotlin en su contexto espec\u00edfico<\/li>\n<li>Crear confianza en el equipo con el c\u00f3digo Kotlin de producci\u00f3n<\/li>\n<\/ul>\n<p>En lugar de empezar <em>por donde sea<\/em>, considere estas diferentes opciones:<\/p>\n<p><strong>Desde fuera hacia dentro:<\/strong><\/p>\n<p>Comience en la secci\u00f3n \u00abhoja\u00bb de su aplicaci\u00f3n, por ejemplo, el controlador, el trabajo por lotes, etc., y luego dir\u00edjase hacia el dominio central. Esto le proporcionar\u00e1 las siguientes ventajas:\u00a0<\/p>\n<ul>\n<li><strong>Aislamiento en tiempo de compilaci\u00f3n<\/strong>: las clases hoja rara vez tienen algo que dependa <em>de ellas<\/em>, por lo que puede pasarlas a Kotlin y seguir construyendo el resto del sistema sin cambios.<\/li>\n<li><strong>Menos ediciones en cascada.<\/strong> Una IU\/controlador convertido puede llamar al c\u00f3digo de dominio Java existente sin apenas cambios gracias a la interoperabilidad fluida.<\/li>\n<li><strong>Solicitudes de incorporaci\u00f3n de cambios m\u00e1s reducidas, revisiones m\u00e1s f\u00e1ciles.<\/strong> Puede migrar archivo por archivo o funcionalidad por funcionalidad.<\/li>\n<\/ul>\n<p><strong>De dentro hacia fuera:<\/strong><\/p>\n<p>Empezar por el n\u00facleo y pasar despu\u00e9s a las capas exteriores suele ser m\u00e1s arriesgado, ya que compromete las ventajas del enfoque de fuera a dentro mencionado anteriormente. Sin embargo, es una opci\u00f3n viable en los siguientes casos:<\/p>\n<ul>\n<li><strong>N\u00facleo muy peque\u00f1o o aut\u00f3nomo.<\/strong> Si su capa de dominio son solo unos pocos POJO y servicios, darle la vuelta antes puede ser barato y desbloquear inmediatamente construcciones idiom\u00e1ticas (clase de datos, clases de valores, jerarqu\u00edas selladas).<\/li>\n<li><strong>Rearquitectura de cualquier modo.<\/strong> Si planea refactorizar invariantes o introducir patrones DDD (objetos de valor, agregados) mientras migra, a veces es m\u00e1s limpio redise\u00f1ar primero el dominio en Kotlin.<\/li>\n<li><strong>Contratos de seguridad nula estrictos.<\/strong> Poner Kotlin en el centro convierte el dominio en una \u00abfortaleza a prueba de nulos\u00bb; las capas externas de Java pueden seguir enviando valores nulos, pero los l\u00edmites se hacen expl\u00edcitos y m\u00e1s f\u00e1ciles de vigilar.<\/li>\n<\/ul>\n<p><strong>M\u00f3dulo por m\u00f3dulo<\/strong><\/p>\n<ul>\n<li>Si su arquitectura est\u00e1 organizada por funcionalidad y no por capas, y los m\u00f3dulos tienen un tama\u00f1o manejable, convertirlos uno a uno es una buena estrategia.<\/li>\n<\/ul>\n<p><strong>Funcionalidades del lenguaje para convertir Java a Kotlin<\/strong><\/p>\n<p>Kotlin ofrece una serie de funcionalidades, principalmente anotaciones, que permiten que su c\u00f3digo Kotlin se comporte como Java nativo. Esto resulta especialmente valioso en entornos h\u00edbridos en los que Kotlin y Java coexisten dentro de la misma base de c\u00f3digo.<br \/><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class Person @JvmOverloads constructor(val name: String,\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 var age: Int = 0) {\n\u00a0 companion object {\n\n\u00a0 @JvmStatic\n\u00a0 @Throws(InvalidNameException::class)\n\u00a0 fun newBorn(name: String): Person = if (name.isEmpty())\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0throw InvalidNameException(\"name not set\")\n\u00a0\u00a0\u00a0\u00a0\u00a0else Person(name, 0)\n\n\u00a0\u00a0\u00a0@JvmField\n\u00a0\u00a0\u00a0val LOG = LoggerFactory.getLogger(KPerson.javaClass)\n\u00a0\u00a0}\n}<\/pre>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/thanks to @JvmOverloads an additional constructor is created, propagating Kotlin\u2019s default arguments to Java\nvar john =\u00a0 new Person(\"John\");\n\n\/\/Kotlin automatically generates getters (val) and setters (var) for Java\njohn.setAge(23);\nvar name = ken.getName();\n\n\/\/@JvmStatic and @JvmField all accessing (companion) object fields and methods as statics in Java\n\n\/\/Without @JvmStatic it would be: Person.Companion.newBorn(...)\nvar ken =\u00a0 Person.newBorn(\"Ken\");\u00a0\n\n\/\/Without @JvmField it would be: Person.Companion.LOG\nPerson.LOG.info(\"Hello World, Ken ;-)\");\n\n\/\/@Throws(...) will put the checked Exception in the method signature \ntry {\n\u00a0 Person ken =\u00a0 Person.newBorn(\"Ken\");\n} catch (InvalidNameException e) {\n\u00a0 \/\/\u2026\n}<\/pre>\n<p><strong>Kotlin<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@file:JvmName(\"Persons\")\npackage org.abc\n\n@JvmName(\"prettyPrint\")\n\nfun Person.pretty() =\n\u00a0 \u00a0 \u00a0 Person.LOG.info(\"$name is $age old\")<\/pre>\n<p><strong>Java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/@JvmName for files and methods makes accessing static fields look like Java: without it would be: PersonKt.pretty(...)\nPersons.prettyPrint(ken)<\/pre>\n<p><strong>Conversor de Java a Kotlin de IntelliJ IDEA<\/strong><\/p>\n<p>IntelliJ IDEA ofrece un conversor de Java a Kotlin, por lo que, en teor\u00eda, la herramienta puede hacerlo por usted. Sin embargo, el c\u00f3digo resultante dista mucho de ser perfecto, as\u00ed que util\u00edcelo solo como punto de partida. A partir de ah\u00ed, convi\u00e9rtalo en una representaci\u00f3n m\u00e1s propia de Kotlin. En la \u00faltima secci\u00f3n de esta serie de art\u00edculos del blog se tratar\u00e1 m\u00e1s sobre este tema:<em> Factores de \u00e9xito para la adopci\u00f3n de Kotlin a gran escala<\/em>.<\/p>\n<p>Si toma Java como punto de partida, lo m\u00e1s probable es que escriba Kotlin al estilo Java, lo que le ofrecer\u00e1 algunas ventajas, pero no desatar\u00e1 todo el potencial de Kotlin. Por lo tanto, mi opci\u00f3n preferida es escribir una nueva aplicaci\u00f3n.\u00a0<\/p>\n<h3 class=\"wp-block-heading\">Siguiente de la serie<\/h3>\n<p>Esta entrega de nuestra serie de art\u00edculos de blog <em>La gu\u00eda definitiva para adoptar Kotlin en un entorno dominado por Java<\/em> demostr\u00f3 c\u00f3mo los experimentos con Kotlin pueden evolucionar hasta convertirse en c\u00f3digo de producci\u00f3n. Nuestro pr\u00f3ximo art\u00edculo se centra en el lado humano de la adopci\u00f3n: convencer a sus compa\u00f1eros. Se explica c\u00f3mo presentar argumentos claros y basados en el c\u00f3digo, guiar a los nuevos desarrolladores y crear una comunidad de Kotlin peque\u00f1a pero duradera dentro de su equipo.<\/p>\n<p><em>Art\u00edculo original en ingl\u00e9s de:<\/em><\/p>\n<div class=\"about-author \">\n<div class=\"about-author__box\">\n<div class=\"row\">\n<div class=\"about-author__box-img\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/10\/a3f7-400o400o1-QYUavRYyARnAVJLCpM1m8M.webp\" alt=\"\" \/><\/div>\n<div class=\"about-author__box-text\">\n<h4>Urs Peter<\/h4>\n<p>Urs es un experimentado ingeniero de software, arquitecto de soluciones, conferenciante y formador con m\u00e1s de 20 a\u00f1os de experiencia en la creaci\u00f3n de sistemas resistentes, escalables y cr\u00edticos, principalmente con Kotlin y Scala.<\/p>\n<p>Adem\u00e1s de su trabajo como consultor, tambi\u00e9n es un formador apasionado y autor de una gran variedad de cursos que van desde cursos de los lenguajes Kotlin y Scala hasta formaciones de arquitectura como los microservicios y las arquitecturas orientadas a eventos.<\/p>\n<p>Como persona sociable por naturaleza, le encanta compartir conocimientos e inspirar y dejarse inspirar por sus compa\u00f1eros en quedadas y conferencias. Urs es un formador de Kotlin certificado por JetBrains.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n\n    <div class=\"about-author \">\n        <div class=\"about-author__box\">\n            <div class=\"row\">\n                <div class=\"about-author__box-img\">\n                    <img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2021\/08\/photo_2021-08-03_15-27-43-200x200.jpg\" width=\"200\" height=\"200\" alt=\"Alyona Chernyaeva\" loading=\"lazy\"  class=\"avatar avatar-200 wp-user-avatar wp-user-avatar-200 photo avatar-default\">\n                <\/div>\n                <div class=\"about-author__box-text\">\n                                            <h4>Alyona Chernyaeva<\/h4>\n                                                        <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n","protected":false},"author":1086,"featured_media":671802,"comment_status":"closed","ping_status":"closed","template":"","categories":[],"tags":[],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/kotlin\/671776"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/kotlin"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/types\/kotlin"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/users\/1086"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/comments?post=671776"}],"version-history":[{"count":3,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/kotlin\/671776\/revisions"}],"predecessor-version":[{"id":671826,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/kotlin\/671776\/revisions\/671826"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/media\/671802"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/media?parent=671776"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/categories?post=671776"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/tags?post=671776"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/es\/wp-json\/wp\/v2\/cross-post-tag?post=671776"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}