On using static methods in Java

This is a bit of a departure for this blog – an article about coding rather than politics/current affairs. This article assumes you’re reasonably familiar with software development, with http requests, with concepts such as object oriented programming (OOP) and unit testing and ideally also with Java.

Consider the following code snippet:

import java.io.IOException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;

public class LanguageDetector {

    public static String detect(HttpClient client,
            String languageDetectionUrl, String document) {
        try {
            HttpPost httpPost = new HttpPost(languageDetectionUrl);
            HttpEntity entity = new StringEntity(document);
            httpPost.setEntity(entity);
            
            HttpResponse response = client.execute(httpPost);
            return Utils.getStringFromStream(response.getEntity().getContent());
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }
}

Unsurprisingly the code is used to call a third party service which detects the language of the text you send to it.

It is a simplified version of some code I was reviewing where I suggested altering the method so that it wasn’t static, primarily in order to aid unit testing of the code that was calling it, but also because of a general suspicion of static methods being used outside of certain contexts (see below).

The issue with unit testing is that because static methods can’t be overridden, they can’t be mocked (at least not using conventional mocking frameworks such as Mockito or jMock – see below) and thus you can’t isolate the callers from the static method when testing.  This may complicate the tests for the callers and results in those tests failing because of a problem with the static method rather than the caller, which may take time to figure out especially if the developer isn’t familiar with the code. However if the static method is trivial with no side effects this may not be much of an issue in practice.

My suspicion of this usage of a static method was also heightened by the fact it calls a third party service via http, which is quite a big deal compared to typical utility methods or static factory methods the two main usages of static methods that I have no quarrel with. There’s a lot can go wrong with an http call that’s outside the control of the code whilst invoking a third party service over the network is a notable side effect!

The author of the code queried my suggestion and asked if it was suggested purely for testing and also whether static methods should simply be avoided all the time. Subsequent discussion included mention of Powermock which enables static methods to be mocked thus addressing the testability issue I raised, albeit by doing weird things via byte code manipulation. This may be why the creators of Powermock warn against putting it into the hands of junior developers!

So this led me to ask myself: putting testability aside, just exactly when should we use static methods?

My instinct was basically that static methods were fine for utility classes, such as Math.max(), Collections.sort(), etc and for static factory methods. I might also tolerate them outside of these contexts if they didn’t have major side effects and were doing relatively simple things.

It’s worth noting that when you create a utility method, it will normally operate solely on the arguments supplied to it and carry out a single, well defined operation, e.g.  find the maximum of two numbers or even sort a list. In these circumstances, creating an object in order to call such a method gets in the way of what you’re trying to do, so it’s convenient to have the option of creating the static method. You can also treat these methods as if they were simply extra operators added to the language.

With static factory methods, you’re really just defining constructors. Object creation has to occur somewhere, and using a static factory method can allow you to exert more control over the process. The difficulties of unit testing in this context are no different from the difficulties constructors normally cause for unit testing, so the use of the static factory method isn’t actually making testability any worse here, whilst the extra control of a static factory method vs a constructor is a valid reason to use them, regardless of testability issues.

But why not use static methods all the time? Well if you do that, you’re no longer doing OOP!

Static methods are procedural with the limitations inherent in that approach to programming. Thus choosing static methods vs creating objects is really about choosing procedural programming vs OOP.

One might favour OOP most of the time, but not necessarily all the time. OOP is very powerful as a way of organising your code, but if you’re doing something dead simple then going to the hassle of creating an object in order to invoke a method that does your dead simple thing may be overkill.

So does it make sense to call the language detector service procedurally as happens above? The code is reasonably clear to read and we can isolate it from its callers’ unit tests if we’re willing to invoke the byte coding tricks of Powermock, so one might say why not?

Well, aside from what it’s doing not being “dead simple” (see my comments above), the above code requires its callers to supply both a url and an http client, whether or not they need them for their own purposes. It would make for better organisation of the code for LanguageDetector to hold the url and http client so that the callers don’t need these – the only thing they really need to supply is the document. Since the purpose of language detector is to detect what language the document is written in, that’s a reasonable ask.

Now one could address the above point, whilst sticking to using a static method, by defining the url and http client as static variables on LanguageDetector. One could also make them private static variables that only LanguageDetector methods can access, so as to avoid creating Java’s equivalent of global variables.  But what you’ve then got is effectively just a singleton object with two fields and a method, just with everything implemented statically. If you really need the detector as a singleton, Java has better ways of achieving that. If you don’t need a singleton then why not just create the class normally in the first place?

As for Powermock – it looks like a useful tool you might invoke for dealing with static methods in legacy code (until you get around to refactoring it) or third party libraries that you cannot alter. However the enabling of the mocking of static methods is not an excuse to use static methods when they’re not appropriate.

 

 

Advertisements
Posted in All Articles. Tags: , , . Comments Off on On using static methods in Java
%d bloggers like this: