DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Languages
  4. A Kotlin Wishlist for Java

A Kotlin Wishlist for Java

One dev walks through some features he enjoys in Kotlin and would like to see brought to the Java language.

Sarthak Makhija user avatar by
Sarthak Makhija
·
Mar. 20, 18 · Opinion
Like (31)
Save
Tweet
Share
18.43K Views

Join the DZone community and get the full member experience.

Join For Free

There is no doubt that Java has enjoyed a superior position when it comes to programming languages and is considered as one of the most important languages for development. However, there have been a number of languages developed on top of the JVM, like Kotlin.

Kotlin is a statically typed programming language for modern multi-platform applications. While I have been a Java developer for quite a long while, working on a project data-anonymization made me feel that there are things that Java should consider importing from Kotlin. 

These are some of the Kotlin features that I would love to see making a place in Java.

Promote Immutability

Java 9 promotes immutability by introducing factory methods to create collections. It would be great to see immutable collections embedded in the language, rather than relying on wrappers to generate immutable collections. existingDepartments() is a function that returns an immutable list of Strings in Kotlin.

//Kotlin
fun existingDepartments(): List<String> = 
   listOf("Human Resources", "Learning & Development", "Research")


Java 9 comes closest to returning an immutable list by throwing an UnsupportedOperationException when an attempt is made to add or remove an element from the list. It would be great to have a separate hierarchy of mutable and immutable collections and avoid exposing add/remove or any other mutating methods from immutable collections.

//pre Java 8
public List<String> existingDepartments() {
   return new ArrayList<String>(){{
            add("Human Resources");
            add("Learning & Development");
            add("Research");
        }};
}

//Java 8
public List<String> existingDepartments() {
   return Stream.of("Human Resources", "Learning & Development", "Research")
               .collect(Collectors.toList());
}

//Java 9
public List<String> existingDepartments() {
   return List.of("Human Resources", "Learning & Development", "Research");
}


Being more explicit about immutable collections and letting immutable collections speak out loud for themselves should be given preference over exposing methods and throwing UnsupportedOperationExceptions

Method Parameters Should Be Final by Default

With an intent to promote immutability and avoid errors because of mutation, it might be worth to at least giving a thought to making method parameters final by default.

//Kotlin
fun add (augend: Int, addend: Int) = augend + addend


Parameters for the add() function are val by default cannot be changed, which means as a client of any function, I can rest assured that the function is not changing the arguments (not to be confused with object mutation) that are passed to it.

Making method parameters final by default might and will most likely break existing code bases on Java upgrades but is worth giving a thought

Handle NULL at Compile Time

Every Java developer is bound to know the infamous NullPointerException. Kotlin took a major step by handling NULLs at compile time. Everything is non-null be default until it is explicitly stated.

Did Java 8 not introduce Optional for the very same reason ? Let's see with an example:

//Kotlin
class Employee(private val id: Int, private val department: Department?) {
  fun departmentName() = department?.name ?: "Unassigned"
}

class Department(val name: String)
/**
    Employee needs a non-nullable "id" and an optional department to be constructed.
    val employee = Employee(null, null) => <b> Compile Time Error </b>
**/


The Employee class has a primary constructor with a non-nullable id and an optional (nullable) department. Passing null for the id will result in a compile time error.

The departmentName() function accesses the name property of Department using the optional operator ? on the nullable field. If department is null, name will not be accessed and the expression on the left-hand side [department?.name] will return null. The Elvis operator ?: will return the right hand side ("Unassigned") if the left-hand side of the expression is null.

//Java 8
class Employee {
    private Integer id;
    private Optional<Department> department

    Employee(Integer id, Optional<Department> department){
        this.id = id;
        this.department = department;
    }

    public String departmentName() {
        return department.orElse("Unassigned");
    }
}
/**
    Employee needs a non-nullable "id" and an optional department to be constructed.
    Employee employee = new Employee(null, null); <b>NPE !!!</b>
**/


Optional will not protect the code from NPE, but Optional has its advantages:

  • It makes the domain model clear. The Employee class has an optional department, which is good enough to conclude that every employee may not be assigned a department

  • It promotes composability as in the departmentName() method

Handling NULLs at compile time should result in cleaner code by removing unnecessary NULL checks in the form of an if statement, Objects.requireNonNull, Preconditions.checkNotNull, any other form

To keep things simple, department was passed in to the constructor even though this is an optional attribute.

Improve Lambdas

Java 8 introduced lambdas, which are built on top of a functional interface and a functional descriptor, meaning every lambda expression will map to the signature of an abstract method defined in that functional interface. This effectively means it is a mandate to have an interface (Functional Interface) with only one abstract method (Functional Descriptor) to create a lambda expression. 

//Kotlin
val isPositive: (Int) -> Boolean = { it > 0 }
OR,
val isPositive: (Int) -> Boolean = { num > 0 }
OR,
val isPositive: (Int) -> Boolean = { num: Int > 0 }

//Usage
isPositive(10) returns true
isPositive(-1) returns false


Above, the variable isPositive is a function that takes an Int as an argument and returns a Boolean. The value of this variable is a function definition or a lambda defined in braces, which checks that the passed argument is greater than zero.

Whereas, as seen in Java below, Predicate is a functional interface containing an abstract method test() — which takes an argument of type T and returns a boolean.

So, isPositive takes an argument of type Integer and checks that it is greater than zero. In order to use it, we need to invoke the test() method on isPositive.

//Java 8
private Predicate<Integer> isPositive = (Integer arg) -> arg > 0;

//Usage
isPositive.test(10) returns true
isPositive.test(-1) returns false

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
} 


Lambdas should be independent of functional interfaces and their functional descriptors

Support Extension Functions

Kotlin supports extension functions, which provide the ability to extend a class with new functionality without having to inherit from the class or use any type of design pattern, such as Decorator. 

Let's write an extension function to return the last character of a String, meaning "Kotin".lastChar() will return 'n'.

//Kotlin
fun String.lastChar() = this.toCharArray()[this.length - 1]
/**
    Extension functions are of the form -
    fun <ReceiverObject>.function_name() = body
    OR,
    fun <ReceiverObject>.function_name(arg1: Type1, ... argN: TypeN) = body
**/


Here, lastChar() is an extension function defined on String, which is called a receiver object. This function can now be invoked as "Kotlin".lastChar().

Extension functions provide an ability to extend a class with new functionalities without inheritance or any other design pattern

Tail Recursion

Kotlin provides support for Tail-recursion. Tail-recursion is a form of recursion in which the recursive calls are the last instructions in the function (tail). In this way, we don't care about previous values, and one stack frame suffices for all of the recursive calls; tail-recursion is one way of optimizing recursive algorithms.

The other advantage/optimization is that there is an easy way to transform a tail-recursive algorithm to an equivalent one that uses iteration instead of recursion. 

//Kotlin
fun factorialTco(val: Int): Int {
    tailrec fun factorial(n: Int, acc: Int): Int = 
         if ( n == 1 ) acc else factorial(n - 1, acc * n)

  return  factorial(val, acc = 1)
}


When a function is marked with the tailrec modifier and meets the required form, the compiler optimizes out the recursion, leaving behind a fast and efficient loop-based version instead.

Effectively, a tail-recursive function can execute in constant stack space, so it's really just another formulation of an iterative process

Java does not directly support tail-call optimization at the compiler level, but one can use lambda expressions to implement it. It would be nice to see TCO at the compiler level.

Miscellaneous

  • Remove inherent duplication [new, return, semicolon]: Kotlin does not require new to create an instance. It still needs a return if a function is treated as a statement instead of an expression.

//Kotlin
class Employee(private val id: Int, private val department: Department?) {

    //no return
    fun departmentNameWithoutReturn() = department?.name ?: "Unassigned"

    //return is needed if a function is treated as a statmentrather than an expression
    fun departmentNameWithoutReturn() {
        val departmentName = department?.name ?: "Unassigned"
        return departmentName
    }
}


  • Singleton Classes: It would be great to see an easier way to create singleton classes in Java. An equivalent syntax in Kotlin is seen below.

//Kotlin
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
        // ...
    }
}


  • Immutable Classes: It would be good to see something like the readonly/immutable modifier to create an immutable class. The below mentioned code snippet is simply a thought (not available in Kotlin or Java).

//Hypothetical [Not available so far]
immutable class User(private val name: String, private val id: Int)


In conclusion, as developers, we will always make mistakes (skipping NULL checks, mutating a collection, etc.), but providing such features at the language level will make our lives easier and prevent mistakes.

Java (programming language) Kotlin (programming language)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Keep Your Application Secrets Secret
  • Application Architecture Design Principles
  • Kubernetes-Native Development With Quarkus and Eclipse JKube
  • A First Look at Neon

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: