Wednesday, December 21, 2022

Where to find .gitattributes on Windows 10/11 using Git, IntelliJ 2022 and WSL Ubuntu to fix CRLF (\r\n) command not found: convert to LF line endings on checkout

Introduction

Problem: when checking out a project in IntelliJ in Windows, all files are checked out with Window's newline CRLF (\r\n).

But if you then open a terminal in IntelliJ which runs WSL (Ubuntu) and you want to run a bash script like this shell script, you'll get this error:

#!/bin/sh

set -e

[unrelated stuff deleted]

It will fail with: 

./deploy.sh: line 2: $'\r': command not found

: invalid optione 3: set: -

set: usage: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...]

Those errors are caused by the shell script having CRLF instead of just LF, which Ubuntu/Mac OS expects.

Sidenote: I made softlink from /bin/sh to bash in my system because dash does not support several features, see here


So I tried setting IntelliJ's Code Style to use \n for newlines and line endings and line separator, also for new projects like mentioned here.

But still after creating a new project from Git, all files including the above script was set to CRLF. You can see this at the bottom of the screen in this screenshot:


I also realised that I don't want all files to have just LF, because I noticed doing that manually, on my commit of those files, I had to commit these too, as the newline changed. But I didn't want to bother other teammembers on Mac Books with this unnecessary change. Similar to what happens when you do a dos2unix on the file.
So the next to try was to get Git on my machine to handle it correctly. And thus hopefully IntelliJ too.
The .gitattributes file seemed a very good candidate: a suggestion was to change the git config 'core.autocrlf'. But that meant all the local files were getting changed still if I understood correctly, which I don't want.

Solution

It cost a lot of effort to find out where the gitattributes file is currently; and I wanted to change it for all my Git commands in the future, not just for one project.
What I wanted to change it to is mentioned here:

# Convert to LF line endings on checkout.
*.sh text eol=lf
**/*.sh eol=lf

Those lines specify to change all end of lines (newlines) of files ending with .sh to be checked out with a LF (\n).
I also added the 3rd line myself, to specify subdirectories, but maybe that was not needed.

It was hard to find the correct location for the gitattributes (or .gitattributes file which made it all more confusing), this official Git text wasn't super-clear on it: 

Finally I found my gitattributes file on Windows here:

C:\git\etc\gitattributes

C:\git is actually where I installed the Git client. And it is the one IntelliJ uses (but not the one the terminal shell in IntelliJ uses!) 

And there were already quite a few settings in there like:

*.RTF diff=astextplain
*.doc diff=astextplain
*.DOC diff=astextplain

I just appended these:

**/*.sh eol=lf
*.sh eol=lf

Restarted IntelliJ to be sure. And yes, after a complete full new checkout of my Git project, Java and Kotlin files were still having CRLF line endings, and my shell script had the LF ending.

Sunday, December 18, 2022

Docker build with Git command running in CircleCI failing with: Fatal: No names found, cannot describe anything, invalid argument, for "-t, --tag" flag: invalid reference format

Introduction

Context: Docker, CircleCI, Github.

The Docker build command 

docker build -f .circleci/Dockerfile -t $AWS_ACCOUNT_ID.ecr.$AWS_DEFAULT_REGION.amazonaws.com/${CIRCLE_PROJECT_REPONAME}:`git describe --tags` -t $AWS_ACCOUNT_ID.ecr.$AWS_DEFAULT_REGION.amazonaws.com/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_BUILD_NUM} -t $AWS_ACCOUNT_ID.ecr.$AWS_DEFAULT_REGION.amazonaws.com/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_SHA1} .

was failing with this message:

fatal: No names found, cannot describe anything.

invalid argument "********************************************/my-project:" for "-t, --tag" flag: invalid reference format

See 'docker build --help'.

Exited with code exit status 125

Solution

You'd expect the Docker command maybe being syntactically incorrect. But the error message is referring to something else: it turns out the git describe --tags command gave the fatal message.
The cause was that there was no git-tag set at all on the Github project yet.   After manually creating a release (including a tag) on Github and running the above build command again, the docker build command succeeded.

Wednesday, November 30, 2022

Flyway FlywayValidateException Validate failed: Migrations have failed validation. Detected failed migration to version 1 solution, please remove any half-completed changes then run repair to fix the schema history

Introduction

Setup:
  • Spring boot 2.7.4
  • Kotlin 1.7.20

Flyway dependencies:

plugins {
    id "org.flywaydb.flyway" version "9.6.0"
}

project(":application") {
  dependencies {

      dependencies {

          implementation "org.flywaydb:flyway-core:9.6.0"
          implementation "org.flywaydb:flyway-mysql:9.6.0"
  }
}

With only this Flyway script V1__initial_script present in the Spring Boot application:

create table order
(
    id                 varchar(255) primary key,
    orderId        bigint(20)    not null
);

create index order_id_idx on order(orderId);

create table order_entry
(
    id              varchar(255) primary key,
    orderid     bigint(20) not null,
    desc              varchar(255) not null,
    UNIQUE (oderId),
    constraint fk_orderId foreign key (orderId) references order (id)
);

gave this error:

Invocation of init method failed; nested exception is org.flywaydb.core.api.exception.FlywayValidateException: Validate failed: Migrations have failed validation
Detected failed migration to version 1 (initialize schema).
Please remove any half-completed changes then run repair to fix the schema history.
Need more flexibility with validation rules? Learn more: https://rd.gt/3AbJUZE

See also the below screenshot:


So the error is very vague. I also knew for sure that the schema script was the very first Flyway script to be run for that database, so it could not be the case that I edited the script after it already had run before succesfully.

Solution

The error code documentation doesn't tell much either. No details at all. 

But when I ran the contents of the above script on the SQL command line in DBeaver you get the exact details of the problem:

org.jkiss.dbeaver.model.sql.DBSQLException: SQL Error [3780] [HY000]: Referencing column 'orderId' and referenced column 'id' in foreign key constraint 'fk_orderId' are incompatible.
at org.jkiss.dbeaver.model.impl.jdbc.exec.JDBCStatementImpl.executeStatement(JDBCStatementImpl.java:133)
...

So Flyway just doesn't log the details of the error at all. I also tried the Flyway validate in a bean, to see if I could inspect the exception hopefully returned in the validateWithResult variable:

  @Bean
  fun myFlyway(dataSource: DataSource): String {
    val flyway = Flyway.configure().dataSource(dataSource).load()
    val validateWithResult = flyway.validateWithResult()
    logger.info("Flyway validate = ${validateWithResult}");
  }

But that only showed again the high level error:

Migrations have failed validation
Error code: FAILED_VERSIONED_MIGRATION
Error message: Detected failed migration to version 1 (initialize schema). Please remove any half-completed changes then run repair to fix the schema history.

I found out that older Flyway versions used to have a bug not showing the whole exception,
But even in this most version 9.6.0 the full exception is not logged.

I also tried configuring Flyway with outputting JSON as mentioned here and here:

flyway {
    outputType="json"
}

But outputType can't be used here. Didn't further investigate how to specify that parameter, the @Bean has no option for it. Maybe it is possible via application.yml...

Though it seems it should be possible via configuration files and environment variables.

Thus in the end the solution for me was to run the script manually against the database to see what the exact error is. Running repair is also not the correct suggestion, because the script just contained some syntax/semantic errors. It looks like a Flyway bug that the exact error details aren't logged by default, and I couldn't get it to do that either.










Wednesday, November 16, 2022

Spring JDBC and MySql using UUIDs in Java and VARCHAR(36) in database incorrect string value solution

Introduction

Using H2 as initial embedded database for a Spring Boot application worked fine. H2 is very forgiving in many situations and of course only tries to emulate the real target database, in this case MySql 8.0.
So after connecting my Spring Boot application to MySql, this error started to appear when inserting a row in a table with a java.util.UUID property as 'id' field:  

    java.sql.SQLException: Incorrect string value: '\xAC\xED\x00\x05sr...' for column 'id' at row 1

A quick internet search showed that potentially my character set and collate setting for the tables were using 3 bytes instead of 4 for UTF-8 storage.
But the database, tables and columns all had utf8mb4 specified as CHARACTER SET and COLLATE, since I'm using MySql 8.0. So that 3 vs 4 bytes for UTF-8 issue did not apply for me. 

Solution

Then I found this blog https://petrepopescu.tech/2021/01/how-to-use-string-uuid-in-hibernate-with-mysql/ that at least when using Hibernate, it doesn't know how to convert UUIDs to strings (varchars), and you need to specify a Hibernate-provided converter. But the Hibernate annotation of course did not work for Spring Data JDBC.

Luckily there is a way to write your own converters for Spring JDBC datatypes.

Implementing that fixed that initial error message. Note the example in the above post is missing the @Configuration annotation on the MyJdbcConfiguration class.

But then the error happened again during this custom JdbcTemplate select query:

    String query = "SELECT DISTINCT r.id, user_id FROM recipe r WHERE user_id = ?";
    List<Object> parameterValues = new ArrayList<>();
    parameterValues.add(userId);
    Object[] parameterValuesArray = parameterValues.toArray();
    jdbcTemplate.query(query, parameterValuesArray, new JdbcRecipeRowMapper());


This was the output of that query, including the parameters used in the query:

Executing prepared SQL statement [SELECT DISTINCT r.id, user_id, r.name, vegetarian, number_of_servings, instructions, r.created_at, r.updated_at FROM recipe r INNER JOIN ingredient i ON r.id = i.recipe_id WHERE user_id = ? AND vegetarian = ? AND number_of_servings = ? AND instructions LIKE ? AND  i.name IN (?) ]
2022-09-14 15:46:38.062 TRACE 16148 --- [nio-7000-exec-2] o.s.jdbc.core.StatementCreatorUtils      : Setting SQL statement parameter value: column index 1, parameter value [e26b2a0a-3d2c-442f-8fa1-f26336d5a9d3], value class [java.util.UUID], SQL type unknown
2022-09-14 15:46:38.068 TRACE 16148 --- [nio-7000-exec-2] o.s.jdbc.core.StatementCreatorUtils      : Setting SQL statement parameter value: column index 2, parameter value [true], value class [java.lang.Boolean], SQL type unknown
2022-09-14 15:46:38.068 TRACE 16148 --- [nio-7000-exec-2] o.s.jdbc.core.StatementCreatorUtils      : Setting SQL statement parameter value: column index 3, parameter value [3], value class [java.lang.String], SQL type unknown
2022-09-14 15:46:38.068 TRACE 16148 --- [nio-7000-exec-2] o.s.jdbc.core.StatementCreatorUtils      : Setting SQL statement parameter value: column index 4, parameter value [%Step%], value class [java.lang.String], SQL type unknown
2022-09-14 15:46:38.068 TRACE 16148 --- [nio-7000-exec-2] o.s.jdbc.core.StatementCreatorUtils      : Setting SQL statement parameter value: column index 5, parameter value [spinach], value class [java.lang.String], SQL type unknown
2022-09-14 16:06:37.952 DEBUG 19748 --- [nio-7000-exec-2] o.s.jdbc.core.JdbcTemplate               : SQLWarning ignored: SQL state 'HY000', error code '1366', message [Incorrect string value: '\xAC\xED\x00\x05sr...' for column 'user_id' at row 1]

So that only shows a warning message at DEBUG level, not even as an error, so at first I missed it completely! The query just returned 0 results.

    SQLWarning ignored: SQL state 'HY000', error code '1366', message [Incorrect string value: '\xAC\xED\x00\x05sr...' for column 'user_id' at row 1]

Makes sense though that this custom query has the same issue, since my string-based query of course does not use the converters that I configured earlier.
So I had to change the third line to explicitly convert the value to a string, so Spring JDBC will pass it on as a string:

        parameterValues.add(userId.toString());

Note: maybe implementing a placeholder interface for each repository like this would also have fixed it for the Spring generated methods/queries like save(), remove() etc, e.g: interface RecipeRepository extends Repository<Recipe, UUID>. Did not try that out.

Other related links used to get to the above solution:


Thursday, October 13, 2022

Migrating Java 17 Spring Boot 2.7.3 application to Kotlin 1.7.20

Introduction

This blogpost describes the challenges encountered when migrating a Java 17 Spring Boot 2.7.3 application to Kotlin 1.7.20. 

Other libraries/tools used in the project:
- Swagger (OpenAPI 3.0.3)
- Spring boot 2.7.3
- Liquibase
- H2 in mem + file based
- JUnit5 with Mockito and Mockito-Kotlin
- MySql 8.0
- Actuator
- Maven 3
Tip: after migration of most of the .java files manually, I found this Spring tutorial which helps to avoid having to add 'open' to @Component, @Service etc annotated classes.
It includes the kotlin-spring-plugin and Kotlin JPA plugin to support Kotlin features better, including support for JSR-305 annotations + Spring nullability annotations. Usually you'd also want to include jackson-module-kotlin for serialization and deserialization of Kotlin classes.
Note that my application uses generated Java classes from an OpenAPI 3 Swagger yaml file, which are returned in the REST API, so therefore not needed.

Total code reduction after migration:
Java: 4718
Kotlin: 2860

So about 44% reduction in code. Not bad.

Steps

Convert POJOs

As first I converted a simple POJO which in my case had @Data and @Builder Lombok annotations.
Open that POJO Java file. Hit ctrl-alt-shift-k. Or, find it in the IntelliJ 'actions' search panel:




Then make sure to rebuild to project by enforcing Maven to rebuild if it didn't do that.
Then I had to redo it on the POJO class.

I was a bit surprised what came out:

    @Builder
    @Data
    class Ingredient {
        private val name: String = null
        private val recipeId: UUID = null
        private val createdAt: LocalDateTime = null
        private val updatedAt: LocalDateTime = null
    }

I would have expected the Lombok annotations to be gone. But on the other hand IntelliJ can't know how to fix them I guess (see below for more on Lombok migration).
And maybe because of the @Data it made all fields private...

I did do expect more like this:

    @Builder
    @Data
    class Ingredient(val name: String, recipeId: UUID, createdAt: LocalDateTime, 
                        updatedAt: LocalDateTime)

But even when I remove the Lombok annotations, still the fields are created as private fields, not as part of the primary constructor... Maybe because of the other annotations on some of the fields, like @Id and @Version?

I manually converted it some more, into this:

    data class Ingredient(val name: String, val recipeId: UUID, val createdAt: LocalDateTime, val         
                        updatedAt: LocalDateTime)

Then I converted all uses of the Builder in the Java class to the regular (primary) constructor of the Kotlin data class. E.g:

    new Ingredient(ingredient, recipeId, createdAt, createdAt)

Doing this for all POJOs would be quite some work. And later on, you want to convert those Java instance creations to Kotlin constructors anyway, with named parameters.
So I didn't do this for all classes, I started to skip this step of replacing builders with constructors.

Now first let's try to rebuild the project with 'mvn clean install' for example.

That gave an error: the newly created Kotlin Ingredient class (symbol) could not be found.   Note that IntelliJ was able to find all dependencies just fine.
The answer to that can be found here
I applied the solution where you move your .kt file into its new src/main/kotlin/x/y/z package. Make sure to mark that src/main/kotlin directory as a source directory in IntelliJ.

But still an error: 

    Cannot find symbol (Ingredient)

So that didn't fix it, so applied the accepted solution, so to make sure the compilation order is Kotlin then Java.
After this change in the pom.xml, IntelliJ couldn't find the Spring Boot application class anymore. 'mvn clean install' ran up to the tests, but many failed due to:

    java.lang.NoClassDefFoundError: kotlin/reflect/full/KClasses 

See next section below for how those were fixed.
I changed also in the Kotlin plugin in the pom.xml the JVM target to: <jvmTarget>1.17</jvmTarget>
After that, IntelliJ compiled fine again.

Then trying to run the application with 'mvn spring-boot:run' gave this error:

    Compilation failure
    Unknown JVM target version: 1.17
    Supported versions: 1.6, 1.8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18

So the <jvmTarget> needs to be 17 (not 1.17) apparently. And then it almost started, but I got the same error as when running the tests:

    java.lang.ClassNotFoundException: kotlin.reflect.full.KClasses

Note also this warning showed up during the build, that needs to be checked and fixed, because it refers to Kotlin JDK8 and we use Java 17:

    [INFO] Scanning for projects...
    [WARNING] 
    [WARNING] Some problems were encountered while building the effective model for com.project:kotlinrecipes:jar:1.0.0
    [WARNING] 'dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique: org.jetbrains.kotlin:kotlin-stdlib-jdk8:jar -> duplicate declaration of version ${kotlin.version} @ line 159, column 15
    [WARNING] 
    [WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.


Fixing the Java tests Part 1

Fixing the tests with the error java.lang.ClassNotFoundException: kotlin.reflect.full.KClasses

Adding this dependency, as also mentioned here, fixed it:

<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>${kotlin.version}</version>
</dependency>

Outstanding question for this stdlib dependency for me was, why is Kotlin stdlib using Java 8?

<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>   // Can't this be Java 17?
<version>${kotlin.version}</version>
</dependency>

And: the Kotlin standard library kotlin-stdlib targets Java 6 and above. There are extended versions of the standard library that add support for some of the features of JDK 7 and JDK 8.
And when you include kotlin-stdlib-jdk8, it will pull kotlin-stdlib-jdk7 and kotlin-stdlib
And also note: https://stackoverflow.com/questions/65731542/why-is-there-no-kotlin-stdlib-jdk11. So basically, all fine since Kotlin just doesn't use any API from Java's JDK higher than 8.

After this, the application started fine, connected to the MySql Docker instance and the REST endpoints worked all fine.

Migrating the Spring Boot application class

The IntelliJ converter worked fine. The @Bean annotation in that class was migrated correctly too. Constants were correctly put in a companion object {} block.
But when starting the application this message showed up:

    org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: @Configuration class 'RecipesApplication' may not be final. Remove the final modifier to continue.

Strange: it says the class might not be final, remove the final modifier... Sounds contradicting!
I made the class 'open' (because the default is public final) and then it worked.

Then for the @Bean I got:

    org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: @Bean method 'encoder' must not be private or final; change the method's modifiers to continue

I made the method 'encoder' also 'open' and then it worked.

If you have other issues, check this post for more tips. 

Migrating a Spring Boot @RestController

I applied the IntelliJ converter. Its result was pretty good. Inheritance from the OpenAPI 3 generated Java code was correctly applied. 
I had to add the Kotlin logging to replace the @Slf4j static logger, see here.
Note the default static 'log' field generated by @Slf4j is after applying that named private val logger = KotlinLogging.logger {}. But you can name it as you want of course.

When running the application, again the controller also had to be made open, to allow it to be subclassed (as can be seen in the tip in the introduction section, Spring and some other frameworks require classes to be open (extendable)).
After this change, the controller worked fine.

I also modified the generated code a bit by adding a @NotNull annotation (for something which had already a '?' so was incorrect anyway I realized afterwards), but then at runtime only I sadly got this error:

    javax.validation.ConstraintDeclarationException: HV000151: A method overriding another method must not redefine the parameter constraint configuration, but method UserController#loginUser(JwtRequest) redefines the configuration of UsersApi#loginUser(JwtRequest).

Removing the incorrectly added @NotNull fixed that problem; and it is unnecessary in combination with the '?' too anyway.

Migrating @Component service

Got this error after adding the logger:

    java.lang.NullPointerException: Cannot invoke "mu.KLogger.info(String)" because "this.logger" is null

Strange, the @RestController did not have that issue. Though that one is overriding a (generated OpenAPI 3) class.
This question triggered me, so I added the 'open' keyword to the methods in the @Component class, to make it non-final so Spring has access to it with its proxies too.

That worked.

Converting Spring @Repository

For interfaces, the question was: a findByUsername(username) can return null when it doesn't find the user. How to best define that in Kotlin? Allow null to be returned (but at least the caller then has to handle the null possibility)? Or use Optional in that case? Or is there another better solution?
No clear best answer to me, e.g: https://discuss.kotlinlang.org/t/how-to-deal-with-database-null-return-according-kotlin-null-safety-feature/2546
I went for having the repo return '?'. But another repo already was using Optional, so left that there too. Will have to decide on consistency here...
See this post on how null can be seen positively and also the String.toIntOrNull() function extension built into Kotlin! :) Based on that, I went for allowing repository functions to return 0.
 

Converting @MappedCollection(idColumn = "RECIPE_ID")

Only had to make sure using the arrayOf() and using a MutableSet because you usually want to add elements to the child (collection):

        @OneToMany(cascade = arrayOf(CascadeType.ALL), orphanRemoval = true, 
                    targetEntity = Ingredient::class)
        @JoinColumn(name = "recipe_id")
        var ingredients: MutableSet<Ingredient> = HashSet<Ingredient>(),

Use @NotNull or not

Is it useful to have the @NotNull annotation, while it is only done at runtime? And doesn't Kotlin also throw an exception when you pass in a null value to a parameter that is already non-nullable by default (i.e it doesn't have the '?' appended to its type)?
Seems like double because Kotlin inserts the @NotNull into the code
Maybe you'd put it in when Java code is calling your Kotlin code, to make it more explicit - and the Java code can then validate on it?

Converting @Configuration class

A class annotated with that has also to be open: 

    @Configuration
    internal open class JdbcConfig : AbstractJdbcConfiguration() {...}

I noticed the Kotlin converter from IntelliJ didn't like always comments at the end of a line of Java code. Sometimes the '()' of a method call were then put on the wrong line.

Fixing the Java tests Part 2

While still as Java code, some failed with this:

    org.mockito.exceptions.base.MockitoException: 
    Cannot mock/spy class com.project.kotlinrecipes.user.infra.JwtUserDetailsServiceImpl
    Mockito cannot mock/spy because :
     - final class

So that was easy, I made those classes 'open'.

I also had to make methods 'open' used in Mockito.when() matchers, because otherwise it complains: 

    org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
    Invalid use of argument matchers!
    0 matchers expected, 1 recorded:

But also verify() started to fail:

    verify(jwtTokenUtil, Mockito.times(0)).validateToken(isA(String.class), isA(UserDetails.class));
    java.lang.NullPointerException: Parameter specified as non-null is null: method
    com.project.kotlinrecipes.infra.security.JwtTokenUtil.validateToken, parameter userDetails

That also meant added the 'open' keyword to that method.

Converting JUnit 5 tests with Mockito to Kotlin

IntelliJ's auto-converter works pretty good. Except that it converts

    private JdbcIngredientRowMapper jdbcIngredientRowMapper;

    @BeforeEach
    public void setUp() {
        jdbcIngredientRowMapper = new JdbcIngredientRowMapper();
    }

to:

    private var jdbcIngredientRowMapper: JdbcIngredientRowMapper? = null

    @BeforeEach
    fun setUp() {
        jdbcIngredientRowMapper = JdbcIngredientRowMapper()
    }

But it can be made nullsafe by changing it to:

    private lateinit var jdbcIngredientRowMapper: JdbcIngredientRowMapper
    
    @BeforeEach
    fun setUp() {
        jdbcIngredientRowMapper = JdbcIngredientRowMapper()
    }

I also introduced the `test description` notation, e.g:

    @Test
    fun `Should map row`() {}

I use in some tests:

    @ParameterizedTest
    @MethodSource("filterNullPermutations")

That filterNullPermutations has to be a static method. IntelliJ made it a companion object with that method 'private'. I added @JvmStatic to make it accessible for the @MethodSource.

I had to add mockito-kotlin library for better interoperability for this case:  
ArgumentMatchers.isA(class) for Kotlin methods that don't allow null values be put in (which isA() and any() for example can return).
And by adding this library I could now also use 'whenever' instead of '`when`'.

isA(MyClass.class) in Java had to be converted to:  

    whenever(jwtTokenUtil.generateToken(isA<UserDetails>))).thenReturn(BEARER_TOKEN_VALUE)

Or even shorter:

       whenever(jwtTokenUtil.generateToken(isA())).thenReturn(BEARER_TOKEN_VALUE)

using the mockito-kotlin library, which creates an instance (instead of the regular mockito which can return null). See here for more explanation. 

Replaced all mock() with Kotlin style: val mockBookService : BookService = mock()

The generated code did not work for this TestRestTemplate.exchange() call in Java:

        // Set up find parameters
        Map<String, String> uriVariables = new HashMap<>();
        uriVariables.put("vegetarian", "true");
        uriVariables.put("numberOfServings", "3");
        uriVariables.put("includedIngredients", "onion");
        uriVariables.put("excludedIngredients", "fish");
        uriVariables.put("instructions", "First");

        HttpHeaders headers = new HttpHeaders();
        headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
        HttpEntity<?> entity = new HttpEntity<>(headers);

        // When
        ResponseEntity<List<RecipeResponse>> foundRecipesEntity = testRestTemplate.exchange("/recipes/findByFilter?vegetarian={vegetarian}&numberOfServings={numberOfServings}&includedIngredients={includedIngredients}&excludedIngredients={excludedIngredients}&instructions={instructions}",
                HttpMethod.GET, entity, new ParameterizedTypeReference<>() {}, uriVariables);


Became after IntelliJs converter applied:

        // Set up find parameters
        val uriVariables: MutableMap<String, String?> = HashMap()
        uriVariables["vegetarian"] = "true"
        uriVariables["numberOfServings"] = "3"
        uriVariables["includedIngredients"] = "onion"
        uriVariables["excludedIngredients"] = "fish"
        uriVariables["instructions"] = "First"
        val headers = HttpHeaders()
        headers[HttpHeaders.ACCEPT] = MediaType.APPLICATION_JSON_VALUE
        val entity: HttpEntity<*> = HttpEntity<Any>(headers)

        // When
        val foundRecipesEntity: ResponseEntity<List<RecipeResponse>> =
            testRestTemplate.exchange<List<RecipeResponse>>("/recipes/findByFilter?vegetarian={vegetarian}&numberOfServings={numberOfServings}&includedIngredients={includedIngredients}&excludedIngredients={excludedIngredients}&instructions={instructions}",
                HttpMethod.GET, entity, object : ParameterizedTypeReference<List<RecipeResponse?>?>() {}, uriVariables
            )

But .exchange() was red underlined, no matching method to invoke found. I had to change it into this:

        // Set up find parameters
        val uriVariables: MutableMap<String, String> = HashMap()
        uriVariables["vegetarian"] = "true"
        uriVariables["numberOfServings"] = "3"
        uriVariables["includedIngredients"] = "onion"
        uriVariables["excludedIngredients"] = "fish"
        uriVariables["instructions"] = "First"
        val headers = HttpHeaders()
        headers[HttpHeaders.ACCEPT] = MediaType.APPLICATION_JSON_VALUE

        // When
        val foundRecipesEntity: ResponseEntity<List<RecipeResponse>>? =
            testRestTemplate.exchange(
                "/recipes/findByFilter?vegetarian={vegetarian}&numberOfServings={numberOfServings}&includedIngredients={includedIngredients}&excludedIngredients={excludedIngredients}&instructions={instructions}",
                HttpMethod.GET, HttpEntity("parameters", headers),
                typeReference<List<RecipeResponse>>(), uriVariables
            )

With the typeReference method added (you can also do it inline BTW):

    private inline fun <reified T> typeReference() = object : ParameterizedTypeReference<T>() {}

And after some more cleaning up this worked too (can you spot the differences with the generated Kotlin from IntelliJ?):

        // Set up find parameters
        val uriVariables: MutableMap<String, String> = HashMap()
        uriVariables["vegetarian"] = "true"
        uriVariables["numberOfServings"] = "3"
        uriVariables["includedIngredients"] = "onion"
        uriVariables["excludedIngredients"] = "fish"
        uriVariables["instructions"] = "First"
        val headers = HttpHeaders()
        headers[HttpHeaders.ACCEPT] = MediaType.APPLICATION_JSON_VALUE
        val entity: HttpEntity<*> = HttpEntity<Any>(headers)

        // When
        val foundRecipesEntity: ResponseEntity<List<RecipeResponse>> =
            testRestTemplate.exchange(
                "/recipes/findByFilter?vegetarian={vegetarian}&numberOfServings={numberOfServings}&includedIngredients={includedIngredients}&excludedIngredients={excludedIngredients}&instructions={instructions}",
                HttpMethod.GET, entity,
                typeReference<List<RecipeResponse>>(), uriVariables
            )


Note that MockK can be the next improvements to the Kotlin code, to allow more Kotlin-style of notation.

Method documentation generation

I noticed my IntelliJ 2022.2.1 does not generate @param, @return etc documentation when typing /** above a (private) function definition.

Miscellaneous

I still have a few references to Java classes in the code, like this one:

        httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter::class.java)

Could not find a way to avoid having to reference a Java class in Kotlin this directly.

And at the end of the process I removed all Lombok annotations and its dependency in the pom.xml.

Bonus tip

Spring's Kotlin extensions overview can be found here.


Tuesday, September 27, 2022

Kotlin for Java Developers course summary

Introduction

This is my summary of relevant lessons learned during a Kotlin for Java developers course.


Summary

General

- When running standalone, needs Kotlin runtime
- Toplevel functions: not explicitly defined in a class, e.g fun main() method
- Useful other info: https://kotlinlang.org
- val number: Int and val number = 25    So has type inference.  But if needed: number: Short = 25
- Recommended good practice: delare variables with 'val' where possible
- Properties of a 'val' class instance you can modify
- Via class "constructor" you can also specify whether to set properties is allowed. E.g class Employee(var name: String, val id: Int), then you can do: employee.name = "abc", but not employee.id = 5.  Note that construction of instance needs both parameters anyway.
- Collections: you can use [i] notation to get to an element.  Or for Maps: use the key
- No difference between checked and unchecked exceptions, no need to specify them in method names. You can't even add a 'throws'.
- Kotlin has no static keyword syntactically, e.g see the 'fun main()' declaration. But they do exist.
- No 'new' keyword
- The '==' operator checks for structural equality, so it works the same as .equals()! So .equals() you don't need to use. How to check for referential equality use: '==='
      Annoying, in Javascript '===' means more equality, in Kotlin less (only referential equality).
- Bitoperators like '|' you spell out like: 'or'
- Use 'is' instead of 'instanceof'
- Smart casting: use the 'as' keyword, e.g: val newValue = something as Employee. But if you did an 'is' check before, you don't need the cast anymore.
- Raw strings can be triple quoted, so you don't need to escape special characters, e.g: val path = """c:\somedir\somedir2"""
- trimMargin() for indentation in code of raw strings that cover multiple lines
- Everything is a class, no native types like long or int.
- Kotlin does not automatically widen numbers. E.g: in Java: int a = 55;long b = a; is fine. But not in Kotlin. So all standard datatypes have toLong() and similar conversion methods
- Double is default just like in Java when declaring variable w/o specifying type, e.g: val myDouble = 65.994. Int also, e.g: val myInt = 11
- Any class: similar to root class of Java class hierarchy
- Unit class: similar to void in Java, but different: it is a singleton unit instance
- Nothing class: subclass of any class. E.g when you have a function with an infinite loop, then return 'Nothing'.
- The RPEL can be used as "scratchbook" of executable code, which knows of your existing classes

Arrays

- Initialize array using a lambda, even after declaration: val evenNumbers = Array(16) {i -> i * 2}     So here i is the index of the array
- Mixed array: val mixedArray = arrayOf("Hello", 22, BigDecimal(0.5), 'a').  It is then an array of Array<Any>.  Or just call .toIntArray() on the Kotlin Int array.
- To call a Java method with int[] as parameters, you can't pass in the Kotlin collection Array, so you have to use: intArrayOf(1, 2, 3) instead of arrayOf(1,2,3). Is also a bit of performance boost since directly you create a primitive type array for the JVM (of type int).
- You can't do var someArray = Array<Int>(5)
- You can do for any type: val myData = 'arrayOf(car1, car2, car3)', where the 3 cars are of type Car.
- Convert int[] created via intArrayOf() to a Kotlin typed array: val typedArray = intArrayOf(1,2,3).toTypedArray() 

Null references

- For types that can be nullable, you have to tell it can be null by appending a '?', e.g: 'val str: String? = null'
- If you check for variable being null, Kotlin compiler from then on knows it can't be null, so all methods are available again (similar to smart-casting)
- 'str?.toUpperCase()' performs behind the scenes a 'if (str != null) str.toUpperCase() else null'. The '?' is called the "safe operator".
- So nested also possible, then it stops at the first one that is null. E.g: 'home?.address?.country?.getCountryCode()'
- To set a default value in case it evaluates to null, use the elvis operator: '?:'. E.g: 'val str1: String? = null; val str2 = str1 ?: "this is the default value"'
- Safe cast operator allows any cast, and if it can't do it, it sets the variable to null. E.g: 'val someArray: Any = arrayOf(1,2,3); val str = someArray as? String' results in 'str' having value null.  So '?' really means: this (variable) *might* from now on evaluate to null.
- If you are sure an expression can't evaluate to null, you can tell the compiler via the non-null assertion: str!!.toUpperCase().  After this point you don't have to add the '?' anymore for null safety checks (because you told the compiler it 100% can't be null). Use this e.g when you *do* want a nullpointer exception to be thrown.
- The 'let' function: 'str?.let {println(it)}', which says: if str isn't null, then let this function-call go ahead via a lambda expression.
- Note that the == operator is safe for nulls (note underwater it uses .equals()), e.g: 'val str1 : String? = null; val str2 = "abc"; str1 == str2'
- To have an array of null values, you have to use 'arrayOfNulls()' method, e.g: 'arrayOfNulls<Int?>(5)' creates one of size 5. Or: 'arrayOfNulls<Int>(5)'
- Use !! when you are sure the variable can never ever be null

Access modifiers

- 4 types for toplevel items:
Default = public final (instead of package level in Java). Note classname doesn't have to match filename in kotlin. One file can have multiple public classes.
Private = Everything in the same file can access it. (Java doesn't allow a private class). When specified for a constuctor parameter, no getters/setters are generated! So really only the class itself can have access to that property, no-one else.
Protected = can't be used
Internal = is inside the module (see below) (no Java equivalent)
- Kotlin has the 'module' principle: group of files that are compiled together.
- For class members:
Public, private, protected mean the same as in Java.
Internal = visible in same module, usually outside the file
- In Kotlin, classes can't see private members belonging to inner classes
- Compile time:
private in Kotlin is compiled to package private
internal in Kotlin is compiled to public
That means sometimes Java code can access Kotlin code that from w/in Kotlin (compiler) can't be accessed
Kotlin generates really long strange names for those type of methods/classes defined as internal, so you are kindof warned when using them from Java.
- Create constructor verbose, Java-like: 
class Employee constructor(firstName: String) {
         val firstname: String

     init {
this.firstName = firstName
    }
        }
Note the 'constructor' keyword as primary constructor (declared outside the curly braces). 
And the 'init' block. The init block runs after all fields have been initialized. 
- Create less verbose:
class Employee constructor(firstName: String) {
            val firstName: String = firstName
        }
- Even less (note the 'val' keyword added; can also be 'var'):
class Employee constructor(val firstName: String) {
        }
- Even less:
class Employee (val firstName: String) {
        }
- But if you want to change visibility of the constructor, you will have to add the 'constructor' keyword: class Employee protected constructor(...)
- For more constructors, just add 'constructor's in the code. But you also have to invoke the primary constructor if present. For that do:
constructor(firstname: String, lastName: String): this(firstName) {
xxx
}
Note no 'val' prefix anymore, since the 2nd constructor does not declare variables! Just declare the 2nd parameter as field in the class.
- 'val' and 'var' in the primary constructor generate the matching properties. If not specified, it is just a parameter in that primary constructor.
- You don't *have* to specify a primary constructor
- Kotlin classes only have properties, no fields (what's the difference? Note there's use of something named the "backing field", see below). Default for properties is also 'public'.
- Properties defined as 'var' will have setter generated too. 
- 'private var' properties: can't be accessed at all.  This is because the (generated) getters + setters must have the same visibility as the property.
- If you want your own getters+setters, you can't declare that property in the primary constructor; it has to be declared in the class.
- Backing field (strange name to be just thrown in in the course):
class Employee(val firstName: String, lastName: String = "") {
        var lastName = lastName
                get() {
return field
                }
       }
   Note the get() has to be immediately after the property. And also the special keyword 'field'.
- Similar for the set() method. But note the caller can do: employee.lastName = "abc". No 'setLastName()' call needed.
- You have toplevel constants too just like toplevel functions. So you don't need to declare a constants file for example. val MY_CONSTANT: String = "Text" . Note also the 'const' keyword exists.
- Data class: 'data class Bike(val color: String, val model: String) {}'. You get for free: toString(), equals()+hashCode(), copy() function.
    Requirements: primary constructor has to have at least 1 parameter marked val or var (unlike lastName in previous example). Also: can't be abstract, sealed or inner classes.
- Parameters can be passed in in any order because you can specify the name of the parameter during the call. These are called "named arguments".
- Internal access modifier: private class + internal functions is meaningless, since the private class already restricts it to the file.
- listOf() returns an immutable list of items

Functions

- Simplified function definition: fun myFunction(param1: Int, param2: Int) = "Multiplied = ${param1 * param2}". So the function returns what is after the equals sign. Defining it this way (so not with a {} block body) is called an expression body.
- Returning nothing means specifying Unit (or leaving it out since it is the default)
- Function parameters must have a type specified (even though compiler could derive the type!)
- Variable number of arguments: 'vararg' keyword, e.g: 'fun addUp(vararg amounts: Int)'
- Spread operator: unpacks the array to pass each element separately (used when having to pass to a vararg parameter): addUp(*arrayOfInts). Yes a '*'.
    In Java you could pass in an array of objects to a method with a varargs parameter, but not in Kotlin.
    Another example: 'val newArray = arrayOf(*array1, *array2, anotherElement)'.  If you don't do this, you'll have 2 arrays + one element in 'newArray'!
- Extension functions lets you add functions to classes. Just add the class + a dot (the so-called "receiver type") to a function, e.g: 'fun String.replaceEWithA() {}'. You access the string with 'this' keyword in that method.
    Note it knows since you are putting the extension on String, no need for a String parameter in the new function. 
    Any public field in the class you add the extension function to can be accessed.
    Feels a bit like a "hack" adding random functions to existing classes. People might also get lazy, not coming up with a new class, but just adding it to an existing one because it matched "good enough". Also for a newcomer on the project it is not possible to easily distinct that a method is an
       extension function.
- Function parameters are of type 'val'.
- Inline functions: you can tell the compiler to inline by prefixing function definition with the 'inline' keyword. Usually works best for functions with lambda parameters

Inheritance

- Extend (subclass) a class: 'class RaceBike(): Bike() {}'. Class Bike has to have the 'open' keyword as prefix because default is final (not extendable).
So: 'open class Bike() {}'.  And if you don't want to specify an empty primary constructor: add 'constructor(): super()' to the subclass RaceBike.
- For an abstract class you can remove the 'open' keyword, because 'abstract' implies already that it will be extendable.
- Overriding a method in the subclass requires 'override' in the subclass method and 'open' in the superclass method.
- An 'abstract fun' also needs the 'override' keyword in the subclass. No need for the 'open' keyword here either, it is automatically. Same for the 'override' keyword!
- Subclass (of course) doesn't have to have the same number of parameters for the primary constructor
- Invoking "super.primary constructor" only makes sense/possible if you don't have any primary constructor defined for the parent
- Usually you'll provide a primary constructor and only add secondary ones when absolutely necessary
- In a subclass when creating a secondary constructor, call the constructor (IntelliJ might report it is missing superclass call or similar) like this: 
'constructor(prop1, prop2,..., newParam: Int): this(prop1, prop2,...)'
- Data classes can't be extended (nor be abstract class or inner class)
- Interfaces can be extended with the same syntax: 'interface MyChildInterface: MyParentInterface {}'. Classes can implement multiple interfaces like Java.
- Interfaces can have properties. Which the implementing class has to define too with an override: 'override val number: Int = 30'.
- If you want to set a property's value in an interface, giving it a concrete implementation, you have to define a 'get()' directly below it. Not sure if you'd ever want that in an interface...
- Properties in interfaces don't have the backing field 'field' identifier (which classes do have in the custom get() and set()).
- Making a parameter optional (with default value) in a function in a superclass, doesn't make it an optional parameter in its subclasses.

object keyword

- 'object' keyword is used for: singletons, companion objects, object expressions.
- For singleton there's always only one instance created, very first time you use this class. So I guess not static classes in Java which are created I think at application startup.
     You can access the functions of the singleton, quite normally: MySingleton.someMethod().   Note the singleton class is not constructed first.
- If you want to add e.g functions to a class that are statically accessible (so w/o having to create the class), wrap it with 'companion object {}'.
     The non-private methods in that block are from then on accessible, using special keyword: 'MyClass.Companion.someMethod()'. But then the class is not a singleton but a regular class with a companion object block in it.  You can also give the companion a name. Kotlin also is smart enough you can ommit Companion. This is really much more verbose than the Java 'static' keyword!
- Companion objects can also be used to call private constructors. You can use them to implement factory patterns.
Of course you have to make the primary constructor (if exists) private too then to prevent instantiation outside the factory, e.g: 
class SomeClass private constructor(val input: String) {
companion object: {
fun createBasicSomeClass(val input: String) = SomeClass(input)
}
}
- Object expressions are where in Java you'd use anonymous classes, e.g implementing an interface that needs to be passed as parameter to a function.
Also called anonymous instances.
E.g: 'functionToCall(object: SomeInterface {
override fun functionToImplement(text: String) = "implemented text $text"
}
      '
Note that the object created is not a singleton (confusing, since you use the word 'object' which is also used to create a Singleton)
Also, the object expression code can access local variables in scope (they don't have to be 'val' (or "final" in Java terms), can be 'var' too). So also change that local variable's value!

Enums 

- Use 'enum class' keyword. 
- How annoying: if you add a 'fun' to an enum, you have to add a ';' after the last enum value!! Odd that compiler can't figure it out w/o the ';'...
- Example invoking a function in enum for a given enum value: 'DepartmentEnum.ACCOUNTING.getInfo()'

Importing

- Recommended to (of course) use the same package structure as underlying directory structure. (though it is not a requirement from Kotlin)
- Toplevel functions (in files, not in a class) can be imported like a class
- What if you want to use a function from another module? Then (in IntelliJ) you have to add the dependency to that module first. Will be a regular Maven dependency in Maven I expect.
- Type aliases and extension functions you import the same (they are all top-level anyway)
- You can rename imports too using the 'as' keyword. Useful when 2 third party libraries use the same name for some class.

For loop

- while and do-while is same in Kotlin
- For loop syntax like in Java doesn't exist in Kotlin. 
- Range: values are inclusive. E.g 'val range = 1..5'.  But also: val charRange = 'a' .. 'z'. Because they are comparable. val stringRange = "ABD".."XYZ"
- 'in' keyword for: 'println(3 in range)' is true. But also '"CCCCC" in stringRange' is true! Because the first 'X' is already greater than the first 'C'.
      '"ZZZZZ" in stringRange' is false because the first 'Z' is greater than 'X'
- 'val rangeBackwards = 5..1' is usually not what you want because '5 in rangeBackwards' is false! Because 5 >= 5 but 5 <= 1. So you have to do: '5.downTo(1)'. Not really intuitive!
- You can also provide a step: range.step(2). And '.reversed()'
- To print a range: 'for (i in range) println(i)'.  But not possible for a string-range because it has not iterator. Makes sense of course.
- But for a regular string it is possible: 'for (c in str) println(c)'.
- Loop itself can also step: 'for (i in range step 4) println'. Similar is there a 'downTo' for counting down.
- To not include the last number: use keyword until: 'for (i in 1 until 10) println'.
- Also variables can be used to define the range: 'val range = 1..someStr.length'.
- Negate: 'val notInRange = 33 !in 1..5' evaluates to true. But also: ''e' in "Hello"' is true.
- Index in for loop: 'for (index in myArray.indices) println("Index = $index, value = ${myArray[index]}")'
- Much shorter: 'myArray.forEach { println(it) }'. Or with index: myArray.forEachIndexed {index, value -> println("$value is at array index $index") }
- Loops can given a name: 'myLoopName@ for (i in 1..5)'. When having nested loops, in a subloop of myLoopName, you can say: 'break@myLoopName
    Then you don't further continue running that loop with 'i' anymore either!  Even when having more subloops before hitting the 'break@' statement.
It is almost a goto-statement! Risk of for example spaghetti-code.
- Works similar with 'continue@myLoopName'.

If expression+statement

- 'if' can evaluate to a value! Not possible in Java. You have to put the return value as the last statement in a {} block. In both the if and else block!
- Comparable with the ternary operator in Java: 'val num = if (mycondition) 40 else 60'. You can even write a full 'val num = if () {40} else {60}'.

When expression

- Is the Java 'switch' statement on steroids'
- 'when(condition) {
10 -> println
20 -> println
else -> println("doesnt match")
  '
- No need to add the 'break' statement. So no falling through at all
- More than 1 value possible in the match, e.g: '10,11' it will match on both those values. You can also use ranges. 
- And expressions: 'x + 50 -> println()'. And smart casting: 'when (something) { is String -> println() is BigDecimal -> println()}'
- And also as in the if statement, you can return a value in each branch of the 'when'. Best practice is to *not* return a different type of value in the branches.
- An empty when, looking like an if statement: 'when { 
i < 50 -> println 
i >=50 println 
else println
     }'. Which is much more concise than 3 if statement branches.

Try/Catch

- Reminder: no distinction between checked and non-time exceptions.
- Also the try/catch you can use as expression
- E.g: 'return try { Integer.parseInt(str) } catch (e: NullPointerException) {0} finally { println("hello")}'.
      Note the finally block doesn't return an Int. But that is fine, since the catch block does and the normal block does. So the finally block is not used in the evaluation of a try/catch expression.
- Use case for Nothing return type for functions: as return type for a method that only throws an exception, e.g the NotImplementedYet() exception.

Lambda expressions basics

- You can call them directly using the Kotlin library 'run' function: 'run{println("lambda being run")}'
- 'println(cars.minBy {car: Car -> car.builtYear}'). minBy is a Kotlin collection function built-in. Note you can move the lambda outside () when the last parameter. And if the only parameter, even leave out the (). Note also the 'Car' could be left out, Kotlin can infer the type.
- In this case the compiler can infer even more, so we can use 'it': - 'println(cars.minBy {it.builtYear}'). 
- You can also use a member reference: 'println(cars.minBy(Car::builtYear))'
- And toplevel function calls: 'run(::topLevelFun)'. Where: 'fun topLevelFun(): println("hello")'
- You can access local variables declared before the lambda in the lambda. In Java you can only access final variables in lambdas and anonymous classes.

Lambdas with receivers

- 'with' keyword: 'return with(StringBuilder()) { append("hello") toString() }'.  So can also be written as: 'return with(cars, {println(car)}'.
- So the 'with' turns the parameter you pass it into a receiver. That is how it is called. Inside the lambda you don't have to refer to the receiver object anymore. You can if you want with the 'this' keyword.
- 'apply' keyword is another receiver keyword: 'StringBuilder().apply() { append("hello") }.toString()'.
- A 'return' in an inlined lambda also returns the function it is in, a so called non-local return.
cars.forEach {
if (it.builtYear == 2019) {
                return
}
}
        println("this is not executed when at least 1 car has builtYear 2019")
- You can have a local-return but then you need a label:
cars.forEach returnBlock@ {
if (it.builtYear == 2019) {
                return@returnBlock
}
}
println("this is now also executed even when at least 1 car with builtYear 2019")
- You can also use the label to refer to nested 'apply's and 'with's:
"some text".apply sometext@ {
"another text".aplly {
println(toUpperCase()) // Only converts 'another text'
println(this@sometext.toLowerCase()) // converts the outer 'some text'
}
}
- 'also()' is similar to 'apply()', but you don't need to use 'this'.

Collections: Lists

- All read-only interfaces are covariant: be able to treat a class like its parent (superclass). E.g assign a List of 'Any's to a list of BigDecimals. See next Generics part for more details on this.
- For mutable collections you can't change the collection (of course), like adding elements. Seems usually these are also covariant (because they extend the (immutable) Collection interface which is covariant)
- 'listOf' creates an immutable list. But 'arrayListOf()' creates a mutable list!  See the type of the underlying Java class: first one is Arrays$ArrayList which is inmutable, the 2nd is ArrayList, which is mutable. I guess using 'mutableList' is more informative than using 'arrayListOf'.
- Handy: 'listOfNotNull(a,null,c)' creates an immutable list of only a and c.
- Convert list to array: 'listOf(*arrayOf("a", "b", "c"))'. Note the spread operator '*' there. But you can also do: 'array.toList()'.

Kotlin's added collections functions

- last(), asReversed()
- using the array notation to get an entry at given index: list[5] instead of list.get(5)
- list.getOrNull(): returns null when entry is null. Similar to Optional in Java.
- max(): entry with highest value
- zip(): creates Pair elements.  This is Kotlin Pair so a bit different from Java Pair. For me a bit unintuitive name that 'zip'.
- 'val combinedList = list1 + list2' // So you are really concatenating
- To combine & get no duplicates from 2 lists: 'val noDuplicatesList = list1.union(list2)'. Just like union in SQL.
- To get no duplicates in 1 list: list.distinct()
- Just like in Java, some functions return a new list, others work on the existing list. I expect you get at least an exception when trying the 2nd case on a immutable list.

Maps and destructuring declarations

- 'mapOf<Int, Car>(1 to Car("green", 2012), 2 to Car("yellow", 2013))'. The number is the key of the map. The <Int, Car> is redundant of course.
- And a mutable map: 'mutableMapOf()'.
- In both cases the underlying implementation is LinkedHashMap, so the iteration order is predictable and easy to convert to a Set for example.
- But you can force a hashmap: 'hashMapOf()'.
- Destructuring: 'val (firstValue, secondValue) = Pair(1, "one")'. Kind of "unpacking". And even: 'for ((key, value) : in mutableMap) { println(key) println(value)}'
- To be able to destructure a class, you need to use: component functions
- To add them to your own class use:
class Car(val color: String, val model: String, val year: Int) {
operator fun component1(): color
operator fun component2(): model
operator fun component3(): year
}
    Unclear if these have to have such fixed names componentN().
- Data classes get the component functions already generated (created) for free.

Sets

- To create an immutable set: 'setOf()'. To add an element to a set: 'set.plus(str)'. Duplicates added are ignored. To remove an element: 'set.minus(str)'. (you get a new set when using immutable sets)
- 'drop(nr)': drop the first 3 elements of the set
- Some functions work on the set itself, most return a new set with the function applied.

Other collections functions

- filter(lambda): returns a new instance of the collection
- 'val added10List = myIntsArray.map {it + 10}': adds 10 to each element in myIntsArray and puts that in new list (a java.util.ArrayList)
- You can chain them of course: 'myCarsMap.filter { it.value.builtYear == '2012' }.map { it.value.color = "yellow"}'
- To check for all elements matching a value, use the 'all(lambda)' function, which returns a boolean. And 'any(lambda)' for at least 1 matching.
- count(lambda)
- 'groupBy(lambda)'
- 'sortedBy(lambda)'
- 'toSortedMap()' to sort by key of the map

Sequences

- filter() creates a whole new copy of the collection. Which can be huge. So to prevent too much memory usage, you can use sequences. Similar to Java streams, but re-invented by Kotlin because Android didn't support Java 8 yet back then. To make a map or list a sequence use: 'asSequence().filter()....'
- 2nd case where you need to put a ';': when you have a lambda predicate which you want to put on 1 line, e.g: '.filter { println("$it"); it[0] == 'yellow'}'
- When you have a sequence, the filter(), map() etc return also a sequence, not the list/collection, because they are intermediate operations. So you need a terminal operation in the chain of calls. E.g: 'toList()'. But for example 'find()' stops at the first match. Usually you want to 'map()' late(st) in the chain of calls. Just like Java streams.

Generics

- You always have to specify the generic type in Kotlin in e.g List<String>. So 'List' is not allowed. Also not for 'myList is List'. There you can use: 'myList is List<*>'. That is called the star projection.
- fun <T> myFunction(collection: List<T>) {}
- Or even as extension function: fun <T> List<T>.myFunction() { println(......) }
- fun <T: Number> myFunction(collection: List<T>) to limit to certain subtypes (you specified the Number as upperbound)
- Multiple upperbounds: fun<T> doIt(item1: T, item2: T) where T: CharSequence, T: Apppendable {}
- Note that T accepts the nullable type too!  By default, if no upperbound specified, it is 'Any?'. So nullable! I call this inconsistent with the "try to not support null" goal :)
- You can make it not accept nullable type by specifiying of course: 'T: Any'.
- Just like in Java, at runtime the generic type is erased (not available anymore)
- In Java you can't do 'list instanceof List<String>'. But in Kotlin you can do: 'list is List<String>'. But it can't for a list of type Any.

Reified parameters (generics related)

- Reification: prevents the type from being erased at runtime
- To do that: make the function 'inline' and 'reified T': 
inline fun <reified T> getElements(list: List<Any>): List<T> {
for (element in list) {
if (element is T)  <----------- now compiles!
}
}
   Then call it: val myListOfAny: List<Any> = listOf("string", 1, 20.0f); getElements<BigDecimal(myListOfAny)

Covariance (generics)

- MutableList<Short> is not automatically the same as ImmutableList<Short> at compile time in case of for example function parameters. Immutable collections are covariant, so then the "subtyping" is fine. But if you define the same parameter collection as mutable, the "casting" can't take place (compile error).
- So MutableList is invariant and wants the exact type (you see this also by the 'out' not present at that class's definition; more on that below)
- And so List has the 'out' keyword, so is covariant. So subtyping is preserved. And since it is an immutable interface/class, nothing can change the list anyway. Still some functions have 'T' as parameter while 'out' is there! Like 'contains()'. @UnsafeVariance is then used for such a (in) parameter, telling compiler that the list won't be changed.
- Note that: List is really a class. But List<String> is a type! E.g: Short is a subclass of Number. List<Number> is supertype of List<Short>
A nullable type is a supertype of non-nullable types (a wider type).
- So List<Short> is not by default the same as List<Number>, even though the 2nd is a super-type(!) of the first.
- Mutable collections interface = not covariant. Collections interface = covariant.
- So if you want the sub-typing(!) to be preserved, you have to prefix the type with the 'out' keyword. E.g: 'class Garden<out T: Flower>
- That keyword implies you can use that class only in "out position".  By default parameters are of 'in' position. And usually the out position is the return type. By specifying the T as 'out' in the Garden above, you can *only* use it as return type!
So 'fun getFlower(nr: Int): T {}' is fine.  But 'fun waterFlower(flower: T)' won't compile!
  Because otherwise you can pass in a different type than the original Garden was created with (e.g: rose vs daffodil); the compiler can't tell.
Note that Garden is a type, not a class, due to that generics part.
So in/out is protecting the class from invalid updating.
- So if you have a covariant class ('out' keyword), subtyping is preserved
- Constructors don't have in/out parameters. So you can always pass in a covariant class as constructor parameter, since it is at construction type.
But if you specify a var parameter: can't be covariant class as input, because again you could pass in the wrong type, because it generates also 
a setter. But a 'private var' is ok again because nobody outside the class can access it.
- Covariant type: subtyping preserved so you can pass an instance of the type or the subtype as for example a parameter. But you can't change the instance (thus when having the 'out' keyword).
- In Java it would be like this: List<? extends Car>,  where you now accept anything that extends Car. This you see in Java in method declarations only. (a variable of that type you can't anything to)

Contravariance

- Is the opposite of Covariance. With Covariance you are preserving the subtyping (incorrectly said(?): accept all of its sub"classes". Is it classes or types or both?). With covariance you start at a subclass and you want to accept instances of that subclass, or any of its superclasses.
- So you use the keyword 'in': interface FlowerStuff<in T> { fun doSomething(flower: T) }. So if T is a direct or indirect superclass of a class, then the class will match T. And you can't have in that case the T as return type, so 'fun getFLower(): T' does not compile. Again you won't be guaranteed to get the type you want if that would have been allowed. Or said differently: not all flowers are a rose. But all roses (subclass) are a flower (superclass).
- Again (depends on where you looking at from the inheritance tree; some say it is "flipping the inheritance tree for a class": 
Covariance: you want to accept a class and all its subclasses   ("looking down the inheritance tree")
Contravariance: you want to accept a class and all its superclasses ("looking up the inheritance tree")
You are widening a generic type to include a class and its subclasses (covariance), or a class and its subclasses (contravariance).
"declaration site variance", so used during declaring an interface. In Java you only have "use-site variance".
- In Java it would be: List<? super Car>: accepts Car and any of its superclasses. This you see in Java in method declarations only. (a variable of that type you can't read from)

Use-site variance

- Generic types are invariant. So even if 'Ford: Car', you can't call 'Car copyCars(source: MutableList<Car>, dest: MutableList<Car>)' with 'copyCars(mutableListof(Ford(), Ford()), mutableListOf(Ford())'. You have to change the Car type in the copyCars to 'T'.
- And to have 'copyCars(fords, cars)' compile, you have to add 'out' (covariance!) to the 'source: MutableList<out T>'. Because that parameter is not changed in the code, so we can do that. This is called use-site covariance, because we didn't add it to the Car class or its subclass like Ford. You can also add 'in' to the dest parameter, but is not needed in this case.
- Also called type-projection.
- In Java declaration-site variance doesn't exist, so you'd have to add the in/out to each method of the class. But so in Kotlin you can do it at class level, i.e declaration-site variance.

I/O

- Just extension functions added to the java io classes like java.io.File.
- var lines = File("myfile.txt").reader.readLines()
- Or to have the reader resource close automatically at the end: val lines = File("myfile.txt").reader().use {it.readText}
- reader().forEachLine() to read per line
- instead of try with resources in Java, use the 'use()' function, that closes the resource always, even if exception.
- File(".").walkTopDown() for file tree traversal

Java interoperability

Calling Java from kotlin:

- Primitive arrays in the Java method as parameter like int[]: you have to use toIntArray() or intArrayOf().
- Nullability: @Nullable @NotNull are Jetbrains IntelliJ(!) hints that can be used. So they are NOT Kotlin annotations, but you give in IntelliJ the Kotlin compiler these hints. Quite ugly in your code base of course. It might also recognize Lombok's or javax's.
But, you only get an IllegalArgumentException at runtime, so compiler won't complain if you assign null to a field.
E.g: Java: 'void setColor(@NotNull color) {}' and then in Kotlin: 'var car: Car = null' compiles fine, but IAE at runtime because the Kotlin compiler generates the not-null check.
Default when none of these 2 annotations: it is nullable type for Kotlin
- You can directly access the field of the Java class like car.color when the Java class has matching getter + setter methods. Or make the field public.
- Exceptions from java: no need to add a throws to the Kotlin function
- varargs in Java: you can't pass a Kotlin array, you'll have to spread (unpack, *) the array
- void from java method: in Kotlin you'll see Unit
- Use Java Object methods that are not in Kotlin's Any, like the Object.notify() method: (car.anObject as java.lang.Object).notify().
- Static fields and methods in Java: are converted to companion objects, so just like this: Car.myStaticfield and Car.myStaticMethod().
- Single Abstract Methods in Java, e.g the Runnable interface which only has one method run() which you have to implement: you can pass them a Kotlin lambda (just like the Java lambda)

Calling Kotlin from Java:

- call toplevel Kotlin functions: Kotlin compiler creates a class based on the .kt filename. So: 'Car.kt' then you invoke 'Car.kt.myMethod()'.
But you can change that generated classname using the @file:JvmName("myclassname")
- Extension functions you can call from java by prefixing the classname too
- getX() and setX() getters setters are available in Java for 'var' fields
- To be able to directly access fields in Kotlin classes, you have to add the @JvmField on the field in the Kotlin class. Has a few use constrictions
- Companion object access: usually: Car.Companion.myMethod(). If you want to not have to use the Companion part, annotate the companion method with
@JvmStatic fun myMethod() {}
- 'object MySingleton() { fun myMethod() }' will be callable from java as: MySingleton.INSTANCE.myMethod(). Also here you can annotate the method with @JvmStatic to avoid that INSTANCE part.
- For a 'const val' in Kotlin, you don't need that @JvmStatic, it is directly accessible: MySingleton.myConstant
- When passing null to a function in Kotlin that is expecting a non-null type, at runtime the Kotlin will check for null (generated by the Kotlin compiler)
- To have Kotlin functions tell Java code an exception can be thrown, annotate method with: @Throws(IOException::class)
- When default parameter values for parameters in Kotin functions: to have Kotlin generate all combinations for those optional parameters use: @JvmOverloads

Not covered

  • coroutines
  • DSL
  • Spring boot integration, annotations how?
  • Lombok annotations work with Kotlin?
  • How to do unittests
  • How to do integrationtests

Considerations

  • Why are "methods" called functions in Kotlin? Functions sounds less Object Oriented...