In classic Java, it is a common idiom to set up a temp variable to hold some value, populate it in a for loop (often after checking some condition for values to add), and then subsequently return the temp variable:

public List<Person> getFilteredUsers(List<Person> personsToFilter) {  
    List<Person> result = new ArrayList<>();
    for(person : personsToFilter) {
        if(person.getAge() >= 15 && person.getJobTitle().equals("Developer")) {
            result.add(person);
        }
    }
    return result;
}

When migrating to Kotlin, it's understandable then that this "java-flavoured" idiom would stick for developers picking up Kotlin's syntax:

fun getFilteredUsers(personsToFilter: List<Person>): List<Person> {  
    val result = mutableListOf<Person>()
    for(person in personsToFilter) {
       if(person.age >= 15 && person.jobTitle == "Developer") {
           result += person
       }
    }
    return result
}

Java-flavoured Kotlin code is all too common, and reasonably so. It's part of adopting any new language to work in the style that you are most recently familiar with, and what you know comfortably.

A couple of guiding ideas can help to adopt a Kotlin style when moving from Java:

  • Favor val over var
  • Favor read-only over mutable

With these ideas in mind, you can ask the question: "can i drop the mutable list?". Fortunately, Kotlin has many niceties built into the language to help with exactly these sort of situations, without requiring adding mutable state:

fun getFilteredUsers(personsToFilter: List<Person>): List<Person> {  
    return personsToFilter.filter { it.age >= 15 && it.jobTitle == "Developer" }
}

Ah! Much better. The Kotlin filter operator accepts the left-hand collection, creating a new collection with any element that matches the condition you specify in the {}'s. Now you've accomplished the same work, without requiring declaring a temp. If you find yourself declaring a temp variable in Kotlin, chances are a function on iterable exists to handle exactly the problem you're facing.

Now, you might be thinking "yes, this is cool - but, what if this were a more complicated situation - like a map for example?" Fortunately Kotlin's collection api has many goodies baked in with these further complicated situations in mind. For example, what if getFilteredUsers should return a map of user's age associated with user instead? Let's see that in the Old Java style:

public Map<Integer, Person> getFilteredUsers(List<Person> personsToFilter) {  
    Map<Integer, Person> result = new HashMap<>();
    for (Person person : personsToFilter) {
        if(person.getAge() >= 15 && person.getJobTitle().equals("Developer")) {
            result.put(person.getAge(), person);
        }
    }
    return result;
}

Fortunately, Kotlin has you covered for these (and far more complicated) cases as well!

fun getFilteredUsers(personsToFilter: List<Person>): Map<Int, Person> {  
    return personsToFilter.filter { it.age >= 15 && it.jobTitle == "Developer" }
            .associateBy { it.age }
}

By the way, now that your statement is written as expression, you can flex an extra benefit of the Kotlin language and convert that function to a single-expression function:

fun getFilteredUsers(personsToFilter: List<Person>) =  
    personsToFilter.filter { it.age >= 15 && it.jobTitle == "Developer" }.associateBy { it.age }

Whenever you declare a temp variable in Kotlin, let it trigger the thought: can I use the collections API instead to solve this without a temp or mutable state? Chances are, the answer is "yes!"