DZone
Java Zone
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
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Java Zone > Beautify Third-Party APIs With Kotlin

Beautify Third-Party APIs With Kotlin

Let's pretty some things up

Nicolas Fränkel user avatar by
Nicolas Fränkel
CORE ·
Dec. 19, 21 · Java Zone · Tutorial
Like (7)
Save
Tweet
6.77K Views

Join the DZone community and get the full member experience.

Join For Free

Scala has popularized the "Pimp my library" pattern:

This is just a fancy expression to refer to the ability to supplement a library using implicit conversions.

-- Pimp My Library Pattern in Scala

Kotlin does provide the same capability. However, it achieves it via extension functions. While the generated bytecode is similar to Java's static methods, the developer experience is the same as adding functions to existing types.

This approach has limitations, though. One cannot update the type hierarchy.

Imagine a library that offers a component with an open/close lifecycle. The component opens when you instantiate it, but you need to make sure to close it after usage, e.g., a file, a stream, etc.

Before Java 7, you had actually to close the component explicitly:

Kotlin
 
Component component;
try {
    component = new Component();
    // Use component
} finally {
    component.close();
}

Java 7 introduced the try-with-resource statement so that you can write something like this:

Kotlin
 
try (Component component = new Component()) {
    // Use component
}                                                // 1
  1. Component is closed here in a generated finally block

However, Component must implement AutoCloseable.

Kotlin provides the use() extension function on Closeable.
Hence, one can replace the try-with-resource statement with a simple function call:

Kotlin
 
Component().use {
  // Use component as it
}                                                // 1
  1. Component is closed here in a finally block

That being said, imagine that the library above implements neither Closeable nor AutoCloseable. We cannot use use(). Kotlin delegation to the rescue!

The Delegation pattern is widespread in Object-Oriented languages:

In delegation, an object handles a request by delegating to a second object (the delegate). The delegate is a helper object, but with the original context. With language-level support for delegation, this is done implicitly by having self in the delegate refer to the original (sending) object, not the delegate (receiving object). In the delegate pattern, this is instead accomplished by explicitly passing the original object to the delegate, as an argument to a method.

-- Wikipedia

Implementing the Delegation pattern in Java requires writing a lot of boilerplate code. The more methods the original class has, the more boring it is:

Kotlin
 
interface class Component {
    void a();
    void b();
    void c();
}

public class CloseableComponent extends Component implements Closeable {

    private final Component component;

    public CloseableComponent(Component component) {
        this.component = component;
    }

    void a() { component.a(); }
    void b() { component.b(); }
    void c() { component.c(); }
    public void close() {}
}

Kotlin supports the Delegation pattern out-of-the-box via the by keyword. One can rewrite the above code as:

Kotlin
 
interface Component {
    fun a() {}
    fun b() {}
    fun c() {}
}

class CloseableComponent(component: Component) : Component by component,
                                                 Closeable {                  // 1
    override fun close() {}
}
  1. Delegate all calls of a(), b(), and c() to the underlying component

We can finally write the desired code:

Kotlin
 
CloseableComponent(RealComponent()).use {
    // Use component as it
}

Even better, it works with third-party code to improve an external library with this approach.

The icing on the cake, one can also call the code from a try-with-resource Java statement:

Kotlin
 
try (CloseableComponent component = new CloseableComponent(new RealComponent())) {
    // Use component
}

As I wrote above, one can do it in Java also. In general, however, the sheer amount of boilerplate code that one needs to write to implement delegation is a significant impediment. Kotlin makes it a breeze.

We miss one last step to make our code easier to write. How do we get the CloseableComponent? Let's create an extension function on Component:

Kotlin
 
fun Component.toCloseable() = CloseableComponent(this)

And now, usage is fluent:

Kotlin
 
RealComponent().toCloseable().use {
    // Use component
}

In this post, we have seen how to improve the API provided by third-party libraries. We achieved it by combining Kotlin extension functions and delegation.

To go further:

  • Extending third-party APIs in different languages
  • Extension functions
  • The try-with-resources Statement
  • Kotlin's use
  • Delegation pattern
  • Kotlin's Delegation

Originally published at A Java Geek on December 19th, 2021

Kotlin (programming language)

Published at DZone with permission of Nicolas Fränkel, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • After COVID, Developers Really Are the New Kingmakers
  • Creating a REST Web Service With Java and Spring (Part 1)
  • Why Is Software Integration Important for Business?
  • Adding Authentication to a Web Application with Auth0, React, and JWT

Comments

Java Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • 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:

DZone.com is powered by 

AnswerHub logo