Skip to Content

<p>

From time to time one recognizes methods like

<pre>

String getProperty(String)

void setProperty(String, String)

Map<String, String> getProperties()

</pre>

for some factory provided by an API. Eh, what…? , one will say, especially when not familiar to that API. In very many of the cases there is only a restricted set of allowed or supported parameters for these methods. If one is lucky these are defined as static constants in the factory or are at least documented in the javadoc. Especially in the former case it can be annoying when there are more constants defined (for other purposes) than are keys accepted.

</p>

<p>

Another prominent example I struggle over very often is something like:

<pre>

Foo doSomething(String path)

</pre>

In many cases this doesn’t make problems, but what if the environment one is using the API in doesn’t allow direct file system access via paths? One of the problems with it is that normally one does not know its installation location but the platform allows easy access to resources in the installation. If the platform is flexible in its packaging, this might be plain files as well as packed entities, so the natural thing a platform returns is an URL. How to convert it to a file system path? </p>

<p>

What do these examples have in common? They are using String for entities which have a semantic meaning. What is the natural way to express something with a semantic meaning in a typed language? Yes, you choose an appropriate type for it. In many cases java.lang.String is not worth much more than java.lang.Object.

</p>

<p>

In the first example an enum (in 1.5) or the typesafe enum pattern (below 1.5) will do the job. Even if you are reading a properties file, nobody prevents one from putting the recognized keys into a typesafe construct. One has to hardcode the recognized keys anyway. This will be an easy place to remember the supported keys when you are in doubt half a year later or when you have to hand over the code.

</p>

<p>

In the second example: What will doSomething normally do? It will construct a stream to read the contents.

Than the API should take an InputStream if only sequential reading is required. If an URL suffices (eg. for an external database driver which can be bound via an URLClassloader) this should be the parameter. In the rare cases where really file system access is required the parameter should be exactly that: java.io.File.

</p>

<p>

Another prominent example of this category is javax.xml.parsers.DocumentBuilderFactory which allows one to set properties for specific implementations. What can this be good for? If I require properties of a specific implementation than I will use that directly. In xml parsing I did not find so far any use case where one requires a property optionally (but I may be wrong since I do not use that often). If you are bound to a specific implementation call it or cast to it. This requires you to refer it directly and to think about how to package it with your solution.

</p>

<p>

As a conclusion I would ask any provider of an API: be specific to the level your API requires and tell the client with your interface what you are expecting. This will increase the usability of an API a lot AND give the compiler a chance to check your requirements.

</p>

To report this post you need to login first.

4 Comments

You must be Logged on to comment or reply to a post.

  1. Valery Silaev
    Richard,

    Fully agree with you about requirement to declare API explicitly. The only case when I can forgive String parameter is when method is overloaded with several input parameter types (say, String URL, and URL as java.net.URL) for convenience. In this case API is still explicit by “common sence and code completion capabilities of IDE” 😉

    VS

    (0) 
    1. Richard Birenheide Post author
      Valery,

      yes, for overloaded and/or convenience API I agree with you (java.io.File is a good axample as well).

      This reminds me of another nasty which I did not mention but falls into the same category: methods which take several (problem domain unrelated) equal parameters such as doIt(String arg1, String arg2, String arg3). I came across a case where two of the parameter names where interchanged in some API I was using, which obviously broke my code at runtime. It took me some time to find the error…;-)

      (0) 
      1. Valery Silaev
        Richard,

        Your second example reminds me two thing:
        1. IntelliJ IDEA is really smart IDE. Check this:
        http://weblogs.java.net/blog/javaben/archive/2007/01/i_love_intellig.html
        2. Up to certain extent the problem can be avoided with Java 5 enums (if parameters are keys), but in general case there is no simple workaround due to missing support for named parameters in Java. The only thing that comes to my mind is to transform:

        createEmployee(String firstName, String lastName, String title);

        to

        createEmployee(CreateEmployeeParams params);
        public class CreateEmployeeParams {
          public String firstName, lastName, title;
        }

        Not very useful in practice due to absurd amount of additional classes and characters to type 🙂

        VS

        (0) 
        1. Richard Birenheide Post author
          Valery,

          I saw that blog earlier ;-). Unfortunately, afaik this is not ralized in Eclipse so far (I am devloping for NWDS).

          Yes, you are right with enums (I used the Bloch typesafe enum pattern with <5.0), I use them wherever I can (eg. the keys of a properties file).

          Your last example asks for some discussion. For the first two parameters I agree with you since names are (from the programmers point of view) semantically meaningless array of characters at first. For the parameter “title” I doubt that one should use a string. I’d rather prefer a more strictly typed construct. If you cannot support a fixed number of titles (this would call for an enum) because you are designing a program for various customers which have different title one could use a more dynamic approach: I would make use of an abstract class as the parameter and leave definition of concrete instances to subclasses (who could make use of dynamic means to construct adequate instances).

          Regards
          Richard

          (0) 

Leave a Reply