Java Deep

Pure Java, what else

How not to use Java 8 default methods

Warning: you can not make this unseen once you have read

I was talking about the multiple inheritance of default methods in the last blog article and how they behave during compilation and run time. This week I look at how to use default methods to do real inheritance, which actually, default methods were not designed for. For this very reason, please read these lines at your own risk, and do not imply that this is a pattern to be followed, just as well do not imply the opposite. What I write here are some coding technics that can be made using Java 8 but their usability is questionable at least for me. I am also a bit afraid to let some ifrit out of the bottle , but on the other hands those ifrits just do not stay there anyway. Some day somebody would let it out. At least I attach the warning sign.

Sample problem

A few years ago I worked on an application that used a lot of different types of objects that each had a name. After many classes started to contain

public String getName(){...}
public void setName(String name){...}

methods that were just setters and getters the copy paste code smell just filled the room unbearable. Therefore we created a class

class HasName {
  public String getName(){...}
  public void setName(String name){...}
}

and each of the classes that had name were just extending this class. Actually it was not working for a long time. There were classes that extended already other classes. In that case we just tried to move the HasName upward in the inheritance line, but in some cases it just did not work. As we went up the line reaching for the top we realized that those classes and their some other descendant do not have a name, why to force them? To be honest, in real life it was bit more complex than just having name. If it were only names, we could live with it having other classes having names. It was something more complex that would just make the topic even more complicated and believe me: it is going to be complex enough.

Summary: we could not implement having the name for some of the objects implemented in some spare classes. But now we could do that using default methods.

HasName interface with default implementation

Default methods just deliver default functionality. A default method can access the this variable, which is always the object that is implementing the interface and on which behalf the method was invoked. If there is an interface I and class C implements the interface, when a method on a C c object is invoked the variable this is actually the object c. How would you implement getName() and setName()?

These are setters and getters that accessing a String variable that is in the object. You can not access that from the interface. But it is not absolutely necessary that the value is stored IN the object. The only requirement is that whatever is set for an object the same is get. We can store the value somewhere else, one for each object instance. So we need some value that can be paired to an object and the lifetime of the value has to be the same as the lifetime of the object. Does it ring the bell?

It is a weak hash map! Yes, it is. And using that you can easily implement the HasName interface.

public interface HasName {
    class Extensions {
        private static final WeakHashMap<HasName, String> map = new WeakHashMap<>();
    }
    default void setName(String name) {
        Extensions.map.put(this, name);
    }
    default String getName() {
        return Extensions.map.get(this);
    }
}

All you have to do is write at the end of the list of interfaces the class implements: ,HasName and it magically has.

In this example the only value stored is a String. However you can have instead of String any class and you can implement not only setters and getters but any methods that do something with that class. Presumably these implementations will be implemented in the class and the default methods will only delegate. You can have the class somewhere else, or as an inner class inside the interface. Matter of taste and style.

Conclusion

Interfaces can not have instance fields. Why? Because in that case they were not interfaces but classes. Java does not have multiple implementation inheritance. Perhaps it has but “please don’t use it” kind of. The default method is a technological mistake. You can call it compromise. Something that was needed to retain backward compatibility of JDK libraries when extended with functional methods. Still you can mimic the fields in interfaces using weak hash maps to get access the inherited class “vtable” of fields and methods to delegate to. With this you can do real multiple inheritance. The type that your mother always warned you about. I told you mate!

Another warning: the above implementation is NOT thread safe. If you try to use it in multithread environment you may get ConcurrentModificationException or it may even happen that calling get() on a weak hash map gets into infinite loop and never returns. I do not tell how to fix the usage of weak hash maps in this scenario. Or, well, I changed my mind, and I do: use default methods only the they were designed for.

About these ads

11 responses to “How not to use Java 8 default methods

  1. Ivan Sopov (@moradan228) April 2, 2014 at 10:41 am

    Use IdentityHashMap or Collections.synchronizedMap(new IdentityHashMap())…

    Like

    • Josh Hyde April 2, 2014 at 11:48 am

      And now you have a memory leak since you’re retaining references to every single object that implements this interface.

      Like

      • John Finalizer April 2, 2014 at 1:32 pm

        Can interfaces now implement finalize?

        Like

      • Peter Verhas April 2, 2014 at 5:47 pm

        Actually not. The Java Language Specification version 8 says:

        It is a compile-time error if a default method is override-equivalent with a nonprivate method of the class Object, because any class implementing the interface will inherit its own implementation of the method.

        Since the method finalize() is implemented in the class Object and it is protected it can not be implemented as a default method.

        While playing around with Java 8 version 1.8.0-b132 on my osx I could have an interface defining the method finalize() as a default method. The class implementing the interface had to implement the method and could not inherit that from the interface. The compiler actually did not care the rule cited above, but it was concerned by the fact that the implementation is inherited from Object (inheritance from a class extended is stronger than the inheritance from an interface) and the method finalize() in Object is protected, which is less visibility than public as it is defined in the interface. Perhaps this is a bug in the ORACLE java implementation.

        Like

      • Peter Verhas April 2, 2014 at 5:48 pm

        You actually did only ask if you can implement and I answered that in my previous reply, however I take the liberty to say that you should not even if you could.

        Like

  2. Saurabh J April 5, 2014 at 4:36 am

    If i understood correctly , in your example you are trying to solve a wrong problem here , default methods are not meant for that and more specifically interfaces are not meant to solve this.Java spec very clearly says that “A particular state of any object should be bind to that class or to its same parent class in the hierarchy.” Interface in plain terms basically controls the role a particular class can play , it is not supposed to be meant for dealing with the state of an object.

    feel free to correct me if my understanding is not correct.

    Cheers

    Like

    • Peter Verhas April 7, 2014 at 10:28 am

      Your understanding my intentions are absolutely correct. I wanted to show a bad example detailing why it is bad. The comments underlined and emphasized the “why” part. I am hoping that showing a bad example with explanation is better than letting a “junior” discover the possibility without noticing that it is bad practice.

      Like

  3. Joe Wolf April 7, 2014 at 4:40 pm

    You can use Groovy traits to solve this problem, which are now available as of Groovy 2.1.3-beta.

    Like

    • Peter Verhas April 7, 2014 at 6:01 pm

      You can solve some problems using Groovy traits, but you have to use Groovy. There is a big difference between the philosophy Groovy and Java. Some may say that Groovy is Java on steroids. I rather would say that Groovy is Java feral. And actually both are carnivore.

      The “this problem” I detailed in this article is NOT that interfaces are limited to default methods and can not have state. That is a limitation but that is not a problem. Like it is a limitation that you should not drive faster than speed limit (say 50km/h in Europe) using your scooter in cities, but that is not a problem.

      The problem is that someone may think that the limitation is there because the scooter does not run faster. When they can access some real bike or a car they start to speed and kill someone. Most of us drive these more powerful programming languages for many years and we know that the limitation is not because of the engine but because of the safety. The engine is limited on the other hand, because there is no point to go faster.

      Your comment points out very well that the problem I pointed out exists and is real.

      And the answer is no. You can not solve this problem using Groovy traits.

      P.S.: I suspect that the groovy version in your comment is wrong, it could be 2.3.1-beta.

      Like

  4. JackZ April 29, 2014 at 11:30 pm

    I had hoped for a more flexible solution similar to C#’s extension methods or Gosu’s enhancements. With Java’s default methods only the owner of the interface can add new methods and, worse, they are not optional.

    Like

  5. javinpaul (@javinpaul) June 7, 2014 at 4:47 am

    Nice tutorial. By the way, I have also shared few examples on Java 8 lambda expressions. Your reader may find that useful.

    Regards
    Javin

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 946 other followers

%d bloggers like this: