Extension methods are not OOP

Extension method is a special language feature introduced by Kotlin, Swift and other programming languages, which allows developers to add new methods to otherwise unchangeable classes. While at first it seems like a great idea, in reality it contradicts the core OOP concepts and ideas.

William Shatner with Nick Cravat as the gremlin
© The Twilight Zone (Nightmare at 20,000 Feet)

First, let’s see how it can be used in Kotlin.

fun String.escapeXml() : String {
  return this
    .replace("&", "&")
    .replace("<", "&lt;")
    .replace(">", "&gt;")
}

String, as you may know, is a final class in Java. As a result, whenever additional functionality is required, developers usually place it in utility classes. With extension methods, new instance methods can be seamlessly injected into any class.

// Java
StringUtils.escapeXml("&<>");

// Kotlin
"&<>".escapeXml("&<>")

This seems to be a great feature, right? No doubts, calling an instance method is more intuitive compared to a static one. In addition, no new classes have to be created. Although extension methods might seem like a viable alternative to utility classes, in reality it is a terrible anti-pattern which violates OOP practices in a number of ways.

  1. The main idea of OOP is to create new classes for new functionality.

    The core idea of OOP is to improve code maintainability by reducing scope as much as possible. Therefore, whenever a new kind functionality is needed, it should be placed in a new class, which implements one or more interfaces.

    An apparent OOP solution to the aforementioned problem of escaping a XML string, would be a simple decorator class.

     public class StringEscapedXml implements Supplier<String> {
            
         private final String origin;
            
         public StringEscapedXml(final String origin) {
             this.origin = origin;
         }
            
         public String get() {
             return origin
                 .replace("&", "&amp;")
                 .replace("<", "&lt;")
                 .replace(">", "&gt;");
         }
            
     }
    
  2. Extension methods violate encapsulation by injecting new properties into objects.

    OOP resides on an idea that entities must be fully encapsulated and that encapsulation must never be broken. However, some language features violate encapsulation. For instance, reflection allows us to peek inside an object, see its private properties and their values. Similar to reflection, extension methods break encapsulation by injecting new properties (in the form of methods) into otherwise independent and unchangeable objects.

  3. Extension methods split class declaration apart.

    After reading documentation on extension methods in Kotlin, your first question would probably be: “Where the heck do I write them?”. The answer is - anywhere! That’s right, Kotlin (as well as Swift) allows you to place static functions and extension methods in any code file that belongs to the project classpath (just like in C or C++). And, yes, you are right again - now you cannot treat class declaration as the only source of instance or static methods.