Originally posted on: Big ball of mud
Which is the main problem you have as a developer when you use libraries that you don't own? You can't change them. If something is missing in the public API of a library, there is no chance to extend it. Using some excellent old object-oriented programming, you can overcome this problem by writing much boilerplate code. In the JVM ecosystem, modern programming languages, such as Scala, Kotlin or Groovy, try to give a solution to library extension, the Pimp My Library Pattern. Let's go, and see what I am talking.
The problem
Let's begin with a very extreme example. Imagine you want to add a method Integer
type in Java that allows you to translate an int into an instance of the java.time.Period
class. For whom that don't know this class, a Period
represents a time, using days, months, years.
All that we want to achieve is having something like the following.
final Period days = Integer.valueOf(42).days();
In more evolved languages, such as Scala or Kotlin, the above statement would look like the following.
val days = 42.days;
In Java, you have no many such possibilities to achieve the goal. Since we cannot nor we want to modify directly the Integer
type, and we do not want to use inheritance on a concrete type, the only remaining possibility is to implement a method somewhere that receives in input an Integer
and returns a Period
.
class Integer2Period {
private Integer integer;
Integer2Period(int integer) {
this.integer = integer;
}
Period days() {
return Period.ofDays(integer);
}
}
var days = new Integer2Period(42).days();
Meh. We used a wrapper or some variance of the Object Adapter Pattern, but we are very far from the objective we originally had.
Let's see how modern JVM languages, such as Kotlin, Scala and Groovy answer this problem.
Scala
Scala was the language that first introduced the Pimp My Library pattern. The pattern was introduced by the Scala language's dad, Martin Odersky, in his article Pimp my Library, in the far 2006.
The pattern allows extending a type adding methods to it without using any form of inheritance. Using the pattern, we can add a method to the Int
type without extending from it.
The Scala language implements the pattern through implicit conversions. First of all, we need to declare an implicit
class that allows the compiler to convert our primary type into a new type that adds the method we want to have. In our case, the primary type is the Int
type.
package object extension {
implicit class ExtendedInt(val integer: Int) extends AnyVal {
def days = Period.ofDays(integer)
}
}
Inside the package extension
, or in any package, explicitly importing the package extension
, we can use the method defined in the type ExtendedInt
as a method defined for the Int
type.
val days = 42.days;
The tricks that make the magic are two:
- The declaration of the implicit type inside a package object forces the compiler to automatically import the
ExtendedInt
type in all the files that belong from it. - The class
ExtendedInt
is declared asimplicit
. - The class
ExtendedInt
is a subclass of the typeAnyVal
. From Scala 2.10, extending fromAnyVal
allows the compiler to perform some code optimisations. It's called Custom Value Classes.
If I don't interpret the result of the javap
command wrongly on the .class
files generated by the Scala compiler, Scala adopts a conversion to the bytecode of the implicit class similar to the wrapper approach I gave for Java.
public final class org.rcardin.extension.package$ExtendedInt {
public int integer();
public java.time.Period days();
public int hashCode();
public boolean equals(java.lang.Object);
public org.rcardin.extension.package$ExtendedInt(int);
}
The method int integer()
is a getter of a private attribute and the constructor of the class takes as input a variable of type int
. The decompiled implicit Scala class has the same structure as the Java class Integer2Period
.
The standard library extensively uses the pattern. All the type defined with the suffix Ops
implement the pattern. Have a look at the StringOps
type for an example.
Kotlin
Also, the newbie JVM-based language, Kotlin, has its implementation of the Pimp my library pattern. In Kotlin slang, the pattern implementation it's called Extension functions. The pattern was introduced in the language to contrast the fact that the majority of the libraries a Kotlin developer could use are in Java, and not in Kotlin.
The syntax needed to declare an extension function is less verbose than the syntax used in the Scala language ( :O ).
fun Integer.days(): Period = Period.ofDays(this)
In our example, the Integer
type is also called the receiver type. Whereas, the this
reference on the right of the assignment symbol is called the receiver object. The this
reference refers to the integer instance on which the extension method is called. To preserve encapsulation, you can access only to the public methods of the receiver object.
The compiler does not import the extension methods by default. As any other Kotlin entity, you need to import them before using explicitly.
Under the hood, the compiler translates every extension method in a static
method having the receiver object as its first parameter. The name of the enclosing class is equal to the name of the file that declares the extension function.
Suppose that we declared the Integer.days
function in a file called IntegerUtil.kt, then the Kotlin compiler compile our code into a static method inside a class called IntegerUtilKt
.
class IntegerUtilKt {
public static Period days(Integer receiver) {
return Period.ofDays(receiver);
}
}
It's very similar to the solution we gave for the Java language.
The translation that the Kotlin compiler performs on extension functions allows us to call them also on nullable types. No method is called directly on the receiver object passed as the first parameter to a static method.
So, extension functions and the Kotlin type system allow us to declare something like the following.
fun String?.isNullOrBlank(): Boolean = this == null || this.isBlank()
You can safely use the above method in if-statements, to control if a nullable object contains a null
reference or not.
val possiblyEmptyString: String? = // Obtaining the string reference
if (possiblyEmptyString.isNullOrBlank()) { // No NullPointerException!!!
// Do something smart
}
Awesome. Kotlin continues to surprise me every day.
Conclusions
Sometimes a library contains almost all that you need, but it lacks some feature that you desire. Extension using the regular object-oriented mechanisms is not a possibility in such cases. Many JVM-based languages give you the possibility to achieve the goal to add the methods you need to an existing library without modifying it.
The Pimp my library pattern is the mechanism to make the magic happen. Scala uses implicit objects and conventions to implement such a pattern. In contrast, Kotlin has a more natural approach that integrates very well with the Kotlin type system concerning the handling of null references.
Moreover, let's say that also Groovy implements the pattern, using Extensions and Categories.
Where are you Java? Will you ever join the party?
If you want, download the code of the Scala example from my repository on GitHub: pimp-my-library.
Top comments (0)