Minborg

Minborg
Minborg
Showing posts with label interface. Show all posts
Showing posts with label interface. Show all posts

Thursday, December 8, 2016

Day 8, Java Holiday Calendar 2016, Use Traits

8. Use Traits in Java


Today's tips is about defining your classes and interfaces using Traits. This allows you to simplify your code and reuse components efficiently without creating unnecessary coupling or inheritance between your classes.

Suppose we have two district interfaces Table and Column that both have names, then we would introduce a name trait HasName that both interfaces extends. A Table and Column can also have an alias so because of that, we would introduce the trait HasAlias:

    interface Table extends HasName, HasAlias, HasOtherTableProperties {}
    interface Column extends HasName, HasAlias, HasOtherColumnProperties {}
    
    interface HasName {
        void setName(String name);
        String getName();
    }
    
    interface HasAlias {
        void setAlias(String alias);
        String getAlias();
    }
    
    public <T extends HasName & HasAlias> void printNameAndAlias(T item) {
        System.out.format("Name=%2, Alias=%s%n", item.getName(), item.getAlias());
    }

Notice in the last method, printNameAndAlias(), how we define a dynamic compound type T that must be both HasName and HasAlias but does not care about other traits or defined methods. The nice thing now is that once we define a Schema interface like this:

    interface Table extends HasName, HasAlias, HasOtherSchemaProperties {}

Then we can re-use the existing trait methods (e.g. printNameAndAlias()) with no extra effort or coupling. So this would work out-of-the-box:

    Schema schema = ...;
    printNameAndAlias(schema);

The trait scheme is used heavily in the open-source project Speedment that is a stream based ORM tool and runtime. Check out the actual Table and Column code here.

Do this:

    
    interface Table extends HasName, HasAlias, HasOtherTableProperties {}
    interface Column extends HasName, HasAlias, HasOtherColumnProperties {}
    
    interface HasName {
        void setName(String name);
        String getName();
    }
    
    interface HasAlias {
        void setAlias(String name);
        String getAlias();
    }

    public <T extends HasName & HasAlias> void printNameAndAlias(T item) {
        System.out.format("Name=%2, Alias=%s%n", item.getName(), item.getAlias());
    }

Don't do this:

    interface Table {
        void setName(String name);
        String getName();
        void setAlias(String name);
        String getAlias();
        ... other table specific methods
    }
    interface Column {
        void setName(String name);
        String getName();
        void setAlias(String name);
        String getAlias();
        ... other column specific methods
    }

    public void printNameAndAlias(Table item) {
        System.out.format("Name=%2, Alias=%s%n", item.getName(), item.getAlias());
    }

    public void printNameAndAlias(Column item) {
        System.out.format("Name=%2, Alias=%s%n", item.getName(), item.getAlias());
    }

    public void printNameAndAlias(Schema item) {
        System.out.format("Name=%2, Alias=%s%n", item.getName(), item.getAlias());
    }



Read more in the original DZone article at https://dzone.com/articles/using-traits-in-java-8

Follow the Java Holiday Calendar 2016 with small tips and tricks all the way through the winter holiday season.

Wednesday, March 9, 2016

Java 8: Declare Private and Protected Methods in Interfaces

Learn How to Declare Private and Protected Methods in Java 8 Interfaces


When Java 8 was introduced, we could use default methods in interfaces. The main driver for this feature was to allow expansion of an interface while retaining backward compatibility for older interface versions. One example is the introduction of the stream() method in the existing Collection classes.

Sometimes, when we want to introduce several default methods, they may share some common code base and then it would be nice if we could use private methods in the interface. This way, we can reuse our code and also prevent it from being exposed to classes that are using or are implementing the interface.

But there is a problem. Private and protected access in interfaces were postponed to Java 9. So how can we use private interface methods in Java 8 today?

A Simple Solution


Suppose that we have an interface Foo with two methods; bar() and bazz() that both are to return some hard-to-calculate result emanating form some shared code like this:

public interface Foo {

    default int bar() {
        return complicatedMethodWithManyLinesOfCode();
    }

    default int bazz() {
        return complicatedMethodWithManyLinesOfCode() + 1;
    }

    
    // Will not work in Java 8 because interface methods cannot be private!
    private int complicatedMethodWithManyLinesOfCode() {
        // Actual code not shown...
        return 0;
    }

}
By introducing a class that holds the private method, we can "hide" the method from outside access and almost get away with private methods in Java 8 interface. It can be done like this:
public interface Foo {

    default int bar() {
        return Hidden.complicatedMethodWithManyLinesOfCode();
    }

    default int bazz() {
        return Hidden.complicatedMethodWithManyLinesOfCode() + 1;
    }

    class Hidden {

        private static int complicatedMethodWithManyLinesOfCode() {
            // Actual code not shown...
            return 0;
        }
    }

}
The method Foo:complicatedMethodWithManyLinesOfCode is not visible from outside classes or interfaces but the Hidden class itself can be seen. However, methods and fields in Hidden cannot be seen if they are private.

This scheme can also be applied for protected interface method access. Technically, we could extend the Hidden class in an interface that also extends the original interface Foo. Remember that protected methods are also package visible, so if we extend or use the interface from the same package, the protected methods are visible (as they always are).

One drawback is that the hidden methods cannot access other methods in the interface. This latter drawback can easily be fixed by letting the hidden static method take a parameter of the interface type. Suppose that the complicatedMethodWithManyLinesOfCode method needs another value from the Foo interface that can be obtained via some interface method named buzz(), then it could look something like this:
public interface Foo {

    default int bar() {
        return Hidden.complicatedMethodWithManyLinesOfCode(this);
    }

    default int bazz() {
        return Hidden.complicatedMethodWithManyLinesOfCode(this) + 1;
    }

    int buzz();

    class Hidden {

        private static int complicatedMethodWithManyLinesOfCode(Foo foo) {
            // Actual code not shown...
            return 0 + foo.buzz();
        }
    }

}

Saturday, March 5, 2016

Java 8: A Type Safe Map Builder Using Alternating Interface Exposure

Expose Your Classes Dynamically

Duke and Spire exposing another look... 
When I was a Java newbie, I remember thinking that there should be a way of removing or hiding methods in my classes that I did not want to expose. Like overriding a public method with a private or something like that (which of corse cannot and should not be possible). Obviously today, we all know  that we could achieve the same goal by exposing an interface instead.

By using a scheme named Alternating Interface Exposure, we could view a class' methods dynamically and type safe, so that the same class can enforce a pattern in which it is supposed to to be used.

Let me take an example. Let's say we have a Map builder that can be called by successively adding keys and values before the actual Map can be built. The Alternating Interface Exposure scheme allows us to ensure that we call the key() method and the value() exactly the same number of times and that the build() method is only callable (and seen, for example in the IDE) when there are just as many keys as there are values.

The Alternating Interface Exposure scheme is used in the open-source project Speedment that I am contributing to. In Speedment, the scheme is for example used when building type-safe Tuples that subsequently will be built after adding elements to a TupleBuilder. This way, we can get a typed Tuple2<String, Integer> = {"Meaning of Life", 42}, if we write TupleBuilder.builder().add("Meaning of Life).add(42).build().

Using a Dynamic Map Builder

I have written about the Builder Pattern several times in some of my previous posts (e.g. here) and I encourage you to revisit an article on this issue, should you not be familiar with the concept, before reading on.

The task at hand is to produce a Map builder that dynamically exposes a number of implementing methods using a number of context dependent interfaces. Furthermore, the builder shall "learn" its key/value types the first time they are used and then enforce the same type of keys and values for the remaining entries.

Here is an example of how we could use the builder in our code once it is developed:
    public static void main(String[] args) {

        // Use the type safe builder
        Map<Integer, String> map = Maps.builder()
                .key(1)                 // The key type is decided here for all following keys
                .value("One")           // The value type is decided here for all following values
                .key(2)                 // Must be the same or extend the first key type
                .value("Two")           // Must be the same type or extend the first value type
                .key(10).value("Zehn'") // And so on...
                .build();               // Creates the map!

        // Create an empty map
        Map<String, Integer> map2 = Maps.builder()
                .build();
        
        
    }

}

In the code above, once we start using an Integer using the call key(1), the builder only accepts additional keys that are instances of Integer. The same is true for the values. Once we call value("one"), only objects that are instances of String can be used. If we try to write value(42) instead of value("two") for example, we would immediately see the error in our IDE. Also, most IDE:s would automatically be able to select good candidates when we use code completion.

Let me elaborate on the meaning of this:

Initial Usage

The builder is created using the method Maps.builder() and the initial view returned allows us to call:
  1. build() that builds an empty Map (like in the second "empty map" example above)
  2. key(K key) that adds a key to the builder and decides the type (=K) for all subsequent keys (like key(1) above)

Once the initial key(K key) is called, another view of the builder appears exposing only:
  1. value(V value) that adds a value to the builder and decides the type (=V) for all subsequent values (like value("one"))

Note that the build() method is not exposed in this state, because the number of keys and values differ. Writing Map.builder().key(1).build(); is simply illegal, because there is no value associated with key 1.

Subsequent Usage

Now that the key and value types are decided, the builder would just alternate between two alternating interfaces being exposed depending on if key() or value() is being called. If key() is called, we expose value() and if value() is called, we expose both key() and build().

The Builder

Here are the two alternating interfaces that the builder is using once the types are decided upon:
public interface KeyBuilder<K, V> {

        ValueBuilder<K, V> key(K k);
        
        Map<K, V> build();
    
}

public interface ValueBuilder<K, V> {

    KeyBuilder<K, V> value(V v);

}

Note how one interface is returning the other, thereby creating an indefinite flow of alternating interfaces being exposed. Here is the actual builder that make use of the alternating interfaces:
public class Maps<K, V> implements KeyBuilder<K, V>, ValueBuilder<K, V> {

    private final List<Entry<K, V>> entries;
    private K lastKey;

    public Maps() {
        this.entries = new ArrayList<>();
    }

    @Override
    public ValueBuilder<K, V> key(K k) {
        lastKey = k;
        return (ValueBuilder<K, V>) this;
    }

    @Override
    public KeyBuilder<K, V> value(V v) {
        entries.add(new AbstractMap.SimpleEntry<>(lastKey, v));
        return (KeyBuilder<K, V>) this;
    }

    @Override
    public Map<K, V> build() {
        return entries.stream()
                .collect(toMap(Entry::getKey, Entry::getValue));
    }

    public static InitialKeyBuilder builder() {
        return new InitialKeyBuilder();
    }

}

We see that the implementing class implements both of the alternating interfaces but only return one of them depending on if key() or value() is called. I have "cheated" a bit by created two initial help classes that take care about the initial phase where the key and value types are not yet decided. For the sake of completeness, the two "cheat" classes are also shown hereunder:
public class InitialKeyBuilder {

    public <K> InitialValueBuilder<K> key(K k) {
        return new InitialValueBuilder<>(k);
    }
    
    public <K, V> Map<K, V> build() {
        return new HashMap<>();
    }

}

public class InitialValueBuilder<K> {
    
    private final K k;

    public InitialValueBuilder(K k) {
        this.k = k;
    }
    
    public <V> KeyBuilder<K, V> value(V v) {
        return new Maps<K, V>().key(k).value(v);
    }

}

These latter classes work in a similar fashion as the main builder in the way that the InitialKeyBuilder returns a InitialValueBuilder that in turn, creates a typed builder that would be used indefinitely by alternately returning either a KeyBuilder or a ValueBuilder.

Conclusions

The Alternating Interface Exposure scheme is useful when you want a type safe and context aware model of your classes. You can develop and enforce a number of rules for your classes using this scheme. These classes will be much more intuitive to use, since the context sensitive model and its types propagate all the way out to the IDE. The schema also gives more robust code, because potential errors are seen very early in the design phase. We will see potential errors as we are coding and not as failed tests or application errors.

Wednesday, February 3, 2016

Plugin Your Own Components with Java 8

Components are Good!


Spire and Duke is plugging in...
A couple of years ago when I worked in the telecom industry, I spent time cleaning up Java code that mixed the concept of abstraction (interfaces) and implementation (concrete classes implementing one or several interfaces).

Another related problem I have come across several times, is the use of Singletons, used to hold various implementations. To be fair, I have created more than a couple of those singletons myself, but nowadays, I almost never use them.

In this post, I will show why and how to avoid them and at the same time gain some additional advantages like separation of interfaces and implementations using Components and a ComponentHandler.

The ComponentHandler that is shown in the examples a bit further down is derived from the open-source project Speedment that I am contributing to. Speedment is great if you want to write database applications in Java 8 using standard Java 8 Streams. Read more about Speedment here.

Why Are Singletons Bad?

The purpose of a singleton is to guarantee that just a single instance of a class or interface is present at any single time. This is of course not a bad thing in it self. On the contrary, there are many situations when we want this property, for example for logging or for other system components like queues or factories. However, the way people chose to enforce the singleton property is more often than not, simply bad.

In my previous post Enforcing Java Singletons is Very Hard, I presented a number of Singleton Idioms that can be used to enforce the singleton property and in the same post, I showed that it is hard to really enforce the singleton patterns (albeit "cheating" with reflections) and also talked about some other disadvantages of singletons.

Here is one of the idioms from the post, called the Enum Singleton Idiom which I first learned about in Joshua Bloch's book "Effective Java" many years ago.

public enum FooEnumSingleton {

    INSTANCE;

    public static FooEnumSingleton getInstance() { return INSTANCE; }

    public void bar() { }

}

This provides a relatively solid and simple singleton, but there are some drawbacks. Because all enums inherit from the built-in Java class Enum, we can not inherit from other base classes. Furthermore, we can not override methods when we want to test our singleton in JUnit tests, for example if we want to "harnes" our class, call some methods and then inspect its state or functionality. Additionally, there is no support or control over the singletons Lifecycle Management. When we first reference the singleton above, the class loader will create and initialize it, pretty much outside our control. If another programmer calls our singleton, it will load no matter what. Now, if our singleton depends on other singletons, which each depends on yet another singleton and so on, our code will likely break. Perhaps not in the first version where things were easy, but it is just a matter of time until it undoubtedly will fail.

I will talk more about lifecycles in the next section.

Component Lifecycles

The Lifecycle of a component or class can be certain stages the component will pass during the time it exists. For example, a component may be created, added, started, stopped and and then eventually, removed from a system. Using the right tools, a component can easily be setup to react to its own lifecycle events.

The Component Handler

Often, it is very convenient to setup some kind of Component Handler that keeps track of all our singleton components. This way, we can have them all collected at one place and treat them in a consistent way. The Component Handler can also be made to automatically call the component's lifecycle methods whenever they are added, changed or removed from the Component Handler.

Now, do not fall for the temptation to make the Component Handler a singleton under the excuse that "now it is just one singleton". Admittedly, it is better to have just one singleton than having 100 singletons, but it is generally even better to just let our Component Handler be just a normal object and keep track of the Component Handler instance in our code. Especially if we are deploying our application in a web server, where there might be many instances of the "Singletons" under different class loaders.

The Class Mapper

The classical (apologies for the bad word-joke) "Class Mapper" is a good starting point when we want to implement a Component Handler. Take a look at this example:

public interface ClassMapper {

    /**
     * Puts a new mapping and returns the previous mapping for the given class,
     * or null if no mapping existed.
     *
     * @param <K> key class
     * @param <V> item type
     * @param keyClass to use
     * @param item to add
     * @return the previous mapping that existed, or null 
     *         if no mapping existed
     */
    <K, V extends K> K put(Class<K> keyClass, V item);

    /**
     * Returns the mapping for the given class, or null if no mapping exists
     *
     * @param <K> The class type
     * @param clazz the class to use
     * @return the mapping for the given class, or null if no mapping exists
     */
    public <K> K get(Class<K> clazz);

}

and a corresponding (almost trivial) implementation:

public class ClassMapperImpl implements ClassMapper {

    private final Map<Class<?>, Object> map;

    public ClassMapperImpl() {
        this.map = new ConcurrentHashMap<>();
    }

    @Override
    public <K, V extends K> K put(Class<K> keyClass, V item) {
        requireNonNull(keyClass);
        requireNonNull(item);
        @SuppressWarnings("unchecked")
        // We know that it is a safe cast
        // because V extends K
        final K result = (K) map.put(keyClass, item);
        return result;
    }

    @Override
    public <K> K get(Class<K> clazz) {
        @SuppressWarnings("unchecked")
        // We know that it is a safe cast
        // because the previous put()
        // guarantees this
        final K result = (K) map.get(clazz);
        return result;
    }

}

The good thing with this is that now we have a small rudimentary Component Handler. Suppose that we have an interface like this:

public interface JokeComponent {

    String nextJoke();

}
The nextJoke() method is supposed to return a funny joke, and we want a pluggable concept so we are planning to implement several versions of the JokeComponent. We start by constructing a very simple JokeComponent that just returns a single joke regardless how many times we call the nextJoke() method:

public class StaticJokeComponent implements JokeComponent {

    @Override
    public String nextJoke() {
        return "I went to buy some camouflage trousers the other day,"
                + " but I couldn’t find any.";
    }

}
Now we can test our Component Handler as shown in the following program:

public class Main {

    public static void main(String[] args) {
        final ClassMapper componentHandler = new ClassMapperImpl();
        setupComponents(componentHandler);

        crankOutTenJokes(componentHandler);
    }

    private static void setupComponents(ClassMapper componentHandler) {
        // Plug in the selected basic JokeGenerator
        componentHandler.put(JokeComponent.class, new StaticJokeComponent());
    }

    private static void crankOutTenJokes(ClassMapper componentHandler) {
        // Get the current JokeGenerator that is plugged into the componentHandler
        JokeComponent jokeGenerator = componentHandler.get(JokeComponent.class);
        // Tell the world who is making the jokes
        System.out.println(jokeGenerator.getClass().getSimpleName() + " says:");
        // print ten of its joke
        IntStream.rangeClosed(1, 10)
                .mapToObj(i -> jokeGenerator.nextJoke())
                .forEach(System.out::println);
    }

}

This will produce the following jokes:

StaticJokeComponent says:
I went to buy some camouflage trousers the other day, but I couldn’t find any.
I went to buy some camouflage trousers the other day, but I couldn’t find any.
I went to buy some camouflage trousers the other day, but I couldn’t find any.
I went to buy some camouflage trousers the other day, but I couldn’t find any.
I went to buy some camouflage trousers the other day, but I couldn’t find any.
I went to buy some camouflage trousers the other day, but I couldn’t find any.
I went to buy some camouflage trousers the other day, but I couldn’t find any.
I went to buy some camouflage trousers the other day, but I couldn’t find any.
I went to buy some camouflage trousers the other day, but I couldn’t find any.
I went to buy some camouflage trousers the other day, but I couldn’t find any.
Even though the joke might be a bit fun the first or second time, we start to lose interest a bit further down the list... Time for improvement by writing a new JokeComponent that doesn't tell the same joke all the time.

public class RandomJokeComponent implements JokeComponent {

    private final Random random = new Random();
    private final List<String> jokes = Arrays.asList(
            "What's blue and doesn't weigh much? Light blue.",
            "What do you get when you cross a joke with a rhetorical question?",
            "What do you call a fish with no eyes? A fsh.",
            "Two men walk into a bar... the third one ducks.",
            "A farmer rounded up her 196 cows and got 200 of them."
    );

    @Override
    public String nextJoke() {
        return jokes.get(random.nextInt(jokes.size()));
    }

}
Now we can plug in this JokeComponent instead and see what happens. Modify the code so it looks like this instead:

    private static void setupComponents(ClassMapper componentHandler) {
        // Plug in the selected basic JokeGenerator
        // Use the RandomJokeGenerator this time instead ---+
        //                                                  V
        componentHandler.put(JokeComponent.class, new RandomJokeComponent());
    }
Now we get another output:
What's blue and doesn't weigh much? Light blue.
Two men walk into a bar... the third one ducks.
A farmer rounded up her 196 cows and got 200 of them.
What do you call a fish with no eyes? A fsh.
What do you get when you cross a joke with a rhetorical question?
A farmer rounded up her 196 cows and got 200 of them.
What do you get when you cross a joke with a rhetorical question?
What do you call a fish with no eyes? A fsh.
What's blue and doesn't weigh much? Light blue.
What do you call a fish with no eyes? A fsh.
This is a small step forward for the humor society, but we could as well write yet another implementation of the JokeComponent that, whenever nextJoke() is called, goes out on the internet and retrieves a funny story from some site. The user code outside the component would remain exactly the same.

Note that the Component Handler can handle any number of interfaces and the interfaces can look completely different and contain different methods, so you could set it up to also handle RidleComponent and perhaps also BlooperComponent that could, for instance, return URL:s to funny films on the net.

The ComponentHandler with Lifecycles


It is fairly easy to expand the Component Handler so it can handle Component Lifecycles. Let us define a simple Lifecycle Component like this.

public interface Component {

    void added();

    void removed();
}
So, every time we add a Component to the Component Handler, we want the Component Handler to automatically invoke the Component::add method and every time it is removed we want it to call the Component::removed method. That way, we know that all component in the Component Handler will be initialized (by the added() method) properly and also that they will clean up stuff (if needed by the removed() method) when they are removed from the Component Handler. Evidently, every component that we want to add to the Component Handler must implement the Component interface. First, we improve the ClassMapper interface so it takes a generic parameter for the values to extend.

public interface ClassMapper<T> {

    <K extends T, V extends K> K put(Class<K> keyClass, V item);

    public <K extends T> K get(Class<K> clazz);

}

Then we could write a ComponentHandler as depicted here under:

public class ComponentHandler implements ClassMapper<Component> {

    private final Map<Class<? extends Component>, Component> map;

    public ComponentHandler() {
        this.map = new ConcurrentHashMap<>();
    }

    @Override
    public <K extends Component, V extends K> K put(Class<K> keyClass, V item) {
        requireNonNull(keyClass);
        requireNonNull(item);
        item.added();
        @SuppressWarnings("unchecked")
        // We know that it is a safe cast
        // because V extends K
        final K result = (K) map.put(keyClass, item);
        if (result != null) {
            result.removed();
        }
        return result;
    }

    @Override
    public <K extends Component> K get(Class<K> clazz) {
        @SuppressWarnings("unchecked")
        // We know that it is a safe cast
        // because the previous put()
        // guarantees this
        final K result = (K) map.get(clazz);
        return result;
    }

}

I have made some simplifications in the code, but the general outline becomes obvious. Whenever we add a Component, its added() method will be called and whenever we replace a component with a new one, the old's removed() method is called.

In the example below, we are to make a Queue for Strings that is a pluggable Component in the new ComponentHandler we just wrote. We could define a StringQueueComponent as a Component like this:

public interface StringQueueComponent extends Component {

    boolean offer(String msg);

    String poll();
}
We could start with a very straightforward implementation using one of the existing Queue implementation already in the Java framework.

public class MemoryStringQueueComponent implements StringQueueComponent {

    private final Queue<String> queue = new ConcurrentLinkedQueue<>();

    @Override
    public boolean offer(String msg) {
        return queue.offer(msg);
    }

    @Override
    public String poll() {
        return queue.poll();
    }

    @Override
    public void added() {
        System.out.println(getClass().getSimpleName() + " added");
    }

    @Override
    public void removed() {
        queue.clear();
        System.out.println(getClass().getSimpleName() + " removed");
    }

}
and then we may come up with an alternative Queue that places the strings on a file as shown in this outline (I have not shown the actual code for file handling because it is quit bulky, but the principle is obvious).

public class FileStringQueueComponent implements StringQueueComponent {

    // File variable goes here

    @Override
    public boolean offer(String msg) {
        // Append the string to the file
    }

    @Override
    public String poll() {
        // Check if the file has grown
        // if yes, read the next line and return it
        // if no, return null
    }

    @Override
    public void added() {
        // Open the file and trunkate it if contains an old queue.
        // Set a pointer to the current file location
    }

    @Override
    public void removed() {
        // Close the file and remove it from the file system
    }

}
So now, when we put the new ComponentHandler to use as depicted below:

public class Main {

    public static void main(String[] args) {
        ComponentHandler componentHandler = new ComponentHandler();
        setupComponents(componentHandler);
        ///
        putStuffInTheStingQueue(componentHandler);
        printStuffInTheStringQueue(componentHandler);

        // Select a new queue component (content of the old queue is lost)
        componentHandler.put(StringQueueComponent.class, new FileStringQueueComponent());

        putStuffInTheStingQueue(componentHandler);
        printStuffInTheStringQueue(componentHandler);

    }

    private static void putStuffInTheStingQueue(ComponentHandler componentHandler) {
        StringQueueComponent queue = componentHandler.get(StringQueueComponent.class);
        queue.offer("A");
        queue.offer("B");
        queue.offer("C");
    }

    private static void printStuffInTheStringQueue(ComponentHandler componentHandler) {
        StringQueueComponent queue = componentHandler.get(StringQueueComponent.class);
        System.out.println("Stuff in " + queue.getClass().getSimpleName());
        String s;
        while ((s = queue.poll()) != null) {
            System.out.println(s);
        }
    }

    private static void setupComponents(ComponentHandler componentHandler) {
        componentHandler.put(StringQueueComponent.class, new MemoryStringQueueComponent());
    }

}
it will produce the following output (given a real implementation of the FileStringQueueComponent):

MemoryStringQueueComponent added
Stuff in MemoryStringQueueComponent
A
B
C
FileStringQueueComponent added
MemoryStringQueueComponent removed
Stuff in FileStringQueueComponent
A
B
C

Conclusion


By using a Component Framework, we gain many advantages over singletons :

  • We can control the lifecycle of our components.
  • The ability to test our components becomes much better.
  • We get a clear separation between interfaces an implementations.
  • We collect all our component in one place and access them in a standardized way.
  • We can ensure that our component is not called before it is initialized.
  • Our code will be less likely to fail in a multi-classloader scenario like a web server.


If you want to take a look at a real existing component handler, have a look at Speedment's  component handler on GitHub. For performance reasons, this component handler is more optimized so that we can obtain components without the overhead of looking them up in a Map.


Wednesday, October 15, 2014

New Java 8 "Object Support Mixin Pattern" for overriding Object.equals(), hashCode(), toString() and compareTo()

Preface

You have, with a probability infinitely close to 1, made one or several errors when overriding basic Object methods like equals() and hashCode()! When I discovered a new class off error in one of my client's classes, I started to look in the entire module and discovered that not a single class was correct. Then I checked the entire project and discovered that every class had the same type of error. Panic! Things went from bad to worse when I checked my own Java code written over the last decade. Just one single class was correct in a strict sense. The errors did not surface, but still, they were there, lurking to appear in the future. So far I have seen 49513 faulty classes and one correct making 99.998% of the classes faulty... Read this post to see how you can avoid these errors!

Overview

In this post, I will disclose a new pattern that I have called the "Object Support Mixin Pattern" and that can be used for automatically overriding the Object methods equals(), hashCode() and toString() in a correct way. I will also show how the related Comparable.compareTo() can be implemented in a similar way.

Using this pattern, that relies on Java 8's new default interface methods, it is possible to automatically mix in support methods without scarifying the ability to inherit from another super class. I will show several variants of the pattern that, depending on the circumstances, can be used directly in your own code. This is a long and sometimes complicated post but please read it thoroughly and you will end up being a better programmer!

When you have read this post to the very end, you will understand why the following class will automatically have correct equals(), hashCode() and toString() methods that will consider the bean properties name, email and born:

public class Person extends ReflectionObjectSupport<Person> {

    private final String name;
    private final String email;
    private final int born;

    public Person(String name, String email, int born) {
        this.name = name;
        this.email = email;
        this.born = born;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }

    public int getBorn() {
        return born;
    }

 }
The superclass ReflectionObjectSupport will override the equals(), hashCode(), toString() and compareTo() methods and, based on reflection, provide fully automatic methods.

If you have classes that already have inherited from another super class (as you probably know, Java can, for good reasons, only inherit from one super class) you can use the Object Support Mixin Pattern. Below, the same Person class is depicted using the Object Support Mixin Pattern. This class also implements the Comparable.compareTo() method.
public class Person implements Comparable<Person>, ReflectionObjectMixin<Person> {

    private final String name;
    private final String email;
    private final int born;

    public Person(String name, String email, int born) {
        this.name = name;
        this.email = email;
        this.born = born;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }

    public int getBorn() {
        return born;
    }

    @Override
    public Comparable[] compareToMembers() {
        return mkComparableArray(getName());
    }

    @Override
    public int hashCode() {
        return _hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return _equals(obj);
    }

    @Override
    public String toString() {
        return _toString();
    }

    @Override
    public int compareTo(Person o) {
        return _compareTo(o);
    }

}
It is difficult to imagine shorter override methods for the Object methods and no support methods are needed in the class except for compareTo() where we need to make a human decision on how we shall order the object. Note that the class is free to inherit from another class, since it is only implementing interfaces and does not extend any class right now.

The problem

While working for some clients, I have often noticed how here are errors in the fundamental Object methods equals() and hashCode(). More suprisingly on my own part, I have also discovered major errors related to the same methods in code that I have written myself. This seams to be a ubiquitous problem and I have not seen a really good pattern to eliminate this plague. Most solution relies on hand coded methods.

There are several articles on the net describing how to write the equals() and hashCode() methods and both methods are related to each other. In short, it is imperative that these methods fulfill their contract or else the class will fail! Failures can occur, for example, when a faulty object is put into a Map or in other types of Collections that are so commonly used in Java code.

The equals() contract

Let us start with the equals() method. The contract requires the following properties for any non-null object "a":

A) It is reflexive, meaning that a.equals(a)
B) It is symmetric, meaning that if a.equals(b) then b.equals(a)
C) It is transient, meaning that if a.equals(b) and b.equals(c) then a.equals(c)
D) It is consistent, meaning that if a.equals(b) this must always be true unless a and/or b change.
E) a.equals(null) shall always be false

What does this mean in pure English? Let me use an analogy with a "dime" that is a ten-cent coin, one tenth of a United States dollar.

A) unsurprisingly says "a dime is equal to a dime". It is an axiomatic definition that seams reasonable. Otherwise equals() would really not make much sense, would it?

B) says "if a dime is 10 cent, then 10 cent is a dime" which seams reasonable. If a thing is equal to something else, the latter thing must also be equal to the first thing.

C) says "if a dime is 10 cent and 10 cent is 0.1 buck, then a dime is 0.1 buck". This rule allows us to infer equality between several objects. It really says that if there are bunch of equal objects and another object is equal to one of these objects, then that object is also equal to all objects in the bunch.

D) says "if a dime is 10 cent now, it must always remain so (unless we change a dime to say 8 cent or so)". Perhaps politicians can navigate around this rule, but we programmers must not!

E) says that "a dime is never equal to nothing". Let us hope that even politicians will adhere to this rule!

The hashCode() contract

If we look at the contract of the hashCode() we conclude that it must:

  A) be consistent, meaning that a.hashCode() should always return the same integer unless a is changed. This is similar to item D for equals().
  B) if a.equals(b) then a.hashCode() and b.hashCode() must return the same integer value.

A is easy to understand but B is a somewhat confusing statement: if a.equals(b) is false, then a.hashCode() and b.hashCode() may or may not return the same integer. If this was not the case, then the number of integers would quickly run out. So, new Point(100,234).hashCode() might produce the same result as new Color(240, 240, 240).hashCode() for example, even though they certainly are not equal.

The default equals() method inherited from the Object class relies on comparing the references to the objects. If they refer to the same object, then the objects are considered to be equal, otherwise they are not equal. This is how it looks:

public boolean equals(Object obj) {
    return (this == obj);
}

The simplest imaginable hashCode() method would be:
 // Legal but do not use this method!!
public int hashCode() {
    return 0;
}
Albeit legal, this method would lead to very poor hash performance. For a Map, all keys would hash to the same bucket, effectively turning the Map into a List. 0 fulfills A because it is consistently 0 and it also fulfills B because all objects would have the hashCode of 0, regardless if they are equal or not. So those objects that are in fact equal, would undoubtedly have the same hashCode.

The hashCode() inherited from the Object class is better than this and will compute an integer based on the numerical value of the object's reference. This is actually a good starting point. It fulfills the contract of the methods (check this for youself!) and for classes that are generally not comparable, such as Thread and Random, they work like a charm. For a typical beans however, where we want to compare the bean properties to asses equality, the situation is not so good.

Common error types

I have notices four distinct errors commonly made in this area:

i) One common mistake is that a programmer overrides one of the methods and not the other. This will, in almost any situation, break the contract of rule B for the hashCode() method! If the programmer overrides equals() and not hashCode() then a.equals(b) will behave differently than before but hashCode() will behave the same. If the programmer on the other hand overrides hashCode() and not equals() then a.equals(b) will behave differently but the hashCode() will remain the same. Always override hashCode() and equals() at the same time!

ii) Another common error is that you overide both methods but regard different bean properties in the methods. Suppose that, if you have a class similar to the Person class above, you use all the properties: getName(), getEmail() and getAge() but in the hashCode() you only use getName() and getEmail() because you added age later and forgot to add it in the hashCode().

iii) A third more subtile error is that you create a (perhaps anonymous) class that overrides one of the getters. In your equals() and hashCode() you use the member variables directly to compare classes and not the corresponding getters. Now your bean will expose the overridden property for the getter but equals() and hashCode() will use the member variable that is not exposed any more.

iv) Class types are compared using instanceof instead of comparing class objects directly, resulting in an asymmetry for overridden classes visa vi their parent class. This will break B and/or C. For example, consider two classes Person and FemalePerson where the latter extends the former. If Person is using (female instanceof Person) and FemalePerson is using (person instanceof FemalePerson) in their equals() methods, (female instanceof Person) is true while (person instanceof FemalePerson) certainly is false.

although strictly not an error but more of an inconvenience, I would like to add another problem commonly appearing in classes:

v) In an attempt to avoid iv) errors, class types are compared using the getClass() method effectively prohibiting derived classes (such as anonymous or inherited classes) to be equal to their base classes.

Asymmetry

I will elaborate more on type iv) errors because they are a bit more difficult to explain. Suppose that we have a bean class named "A" with a bean property named "value" like this:

public class A {

    private final int val;

    public A(int val) {
        this.val = val;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 13 * hash + this.getVal();
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof A)) {
            return false;
        }
        final A other = (A) obj;
        if (this.getVal() != other.getVal()) {
            return false;
        }
        return true;
    }

    public int getVal() {
        return val;
    }

}

Then we extend class A with another class with the somewhat expected name "B", where B introduces an additional bean property named "anotherValue" like this:
public class B extends A {

    private final int anotherValue;

    public B(int val, int anotherValue) {
        super(val);
        this.anotherValue = anotherValue;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 67 * hash + this.getValue();
        hash = 67 * hash + this.getAnotherValue();
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof B)) {
            return false;
        }
        final B other = (B) obj;
        if (this.getAnotherValue() != other.getAnotherValue()) {
            return false;
        }
        if (this.getValue() != other.getValue()) {
            return false;
        }
        return true;
    }

    public int getAnotherValue() {
        return anotherValue;
    }

}

We also create a test class to test the A and B classes:

public class Main {

    public static void main(String[] args) {
        A a = new A(1);
        B b = new B(1, 2);

        System.out.println("a.equals(b) is " + a.equals(b));  // true
        System.out.println("b.equals(a) is " + b.equals(a));  // false
    }

}

As can be seen, we get the disappointing result that "a.equals(b) is true" whereas "b.equals(a) is false". A clear violation of the symmetry rule B!


Preface and Overall Solution Strategy

Before we start with the solution, I want to draw to the attention that equals() and hashCode() basically has the same skeleton. They should both iterate over the (same) bean properties and produce a result. equals() shall return a boolean value if all the bean properties are equal whereas hashCode() shall return an integer that depends on all the bean properties. It would appear rational if both methods got their input properties from the same source using their getters. This way, we avoid mistake i), ii) and iii).

So, imagine that we have an interface with the method Object[] members() that the class has to implement and that it is used both in the equals() and hashCode() method.

Before we lay out a solution, we start with looking at what most IDE:s will generate if you request that they shall generate equals() and hashCode() methods. This allows us to understand the problem a bit more.


The IDE Bean Pattern

Most IDEs have built-in functions for generating the equals() and hashCode(). My IDE makes mistake iii) but otherwise it looks reasonable. After fixing this and some other minor flaws it looks something like this:

public class Person implements Comparable&ltPerson&gt {

    private final String name;
    private final String email;
    private final int born;

    public Person(String name, String email, int born) {
        this.name = Objects.requireNonNull(name);
        this.email = email;
        this.born = born;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }

    public int getBorn() {
        return born;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 61 * hash + Objects.hashCode(getName());
        hash = 61 * hash + Objects.hashCode(getEmail());
        hash = 61 * hash + getBorn();
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Person other = (Person) obj;
        if (!Objects.equals(this.getName(), other.getName())) {
            return false;
        }
        if (!Objects.equals(this.getEmail(), other.getEmail())) {
            return false;
        }
        if (this.getBorn() != other.getBorn()) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "Person{" + "Name=" + getName() + ", Email=" + getEmail() + ", Born=" + getBorn() + '}';
    }

    @Override
    public int compareTo(Person that) {
        int nameCompareTo = this.getName().compareTo(that.getName());
        if (nameCompareTo != 0) {
            return nameCompareTo;
        }
        return Integer.valueOf(this.getBorn()).compareTo(that.getBorn());
    }

}

The science field for computing hash codes is quit broad and falls outside the scope of this post. I will perhaps come back on this subject in a later post. Here, one apparently starts with a prime number (7) and then iteratively multiplies the interim result with another prime (61) and adds the hash of the bean property. This progresses until all bean properties has been used.

The equals() is similar in the way that it iterates over the properties, but it progressively checks if they are equal. As soon as one property is determined to be un-equal, the method aborts and returns false. If all bean properties are equal, then the beans are also equal which seams reasonable. Again, note the mistake of using if (!(obj instanceof Person)) instead of if (getClass() != obj.getClass()). Can you see why this will lead to problems when Person is overriden? If we override the Person class with FemalePerson and we let FemalePerson do if (!(obj instanceof FemalePerson)) while the Person still will retain its if (!(obj instanceof Person)) then we will violate contract items B and C! Why is that? A FemalePerson is an instance of both Person and FemalePerson while a Person is an instance of only Person and not FemalePerson. So female.equals(person) might be true while person.equals(female) is false at the same time! The symmetry becomes broken.
The solution depicted above further has the disadvantage that the getClass():es must return exactly the same class which is not good when we, for instance, create anonymous classes. We will see how to fix this problem later on in this post.

I have also shown the toString() method here. It also iterates over the bean values and produces a string which contains all the bean properties.

At the end, there is also a compareTo() method that is similar to the equals() method. However, It iterates over a subset of the bean properties and compares them. If two properties are not equal, the compareTo value of them are returned, otherwise the method progresses over the properties until the last properties and the compareTo of them are returned.

The observant reader is now seeing a pattern here. We only need two "picker" methods that will select the bean properties for equals(), hashCode(), toString() on one hand and for compareTo() on the other hand. If we want to implement the toString() method, we also need a supplementary third picker method that returns the name of each bean property.

Before we continue, I would like to show one way of extending the Person class:

public class FemalePerson extends Person {

    private final String handbagBrand;

    public FemalePerson(String handbagBrand, String name, String email, int born) {
        super(name, email, born);
        this.handbagBrand = handbagBrand;
    }

    public String getHandbagBrand() {
        return handbagBrand;
    }

    @Override
    public int hashCode() {
        return 61 * super.hashCode() + Objects.hashCode(getHandbagBrand());
    }

    @Override
    public boolean equals(Object obj) {
        if (!super.equals(obj)) {
            return false;
        }
        final FemalePerson other = (FemalePerson) obj;
        if (!Objects.equals(this.getHandbagBrand(), other.getHandbagBrand())) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "FemalePerson{" + "Name=" + getName() + ", Email=" + getEmail() + ", Born=" + getBorn() + ", HandbagBrand=" + getHandbagBrand() + '}';
    }

}
We have added an important property of females, namely the "handbagBrand". Both the hashCode() and the equals() method calls their respective super methods after which the new bean property is added. The toString() method is rewritten from scratch.

Testing the Beans

Now we can test our new bean. We will use the same test for all the implementation variants of Person and FemalePerson throughout this post. Here it is:

public class Test {

    public static void main(String[] args) {
        final Person adam = new Person("Adam", "adam@mail.com", 1966);
        final Person adam2 = new Person("Adam", "adam@mail.com", 1966);
        final Person adamYoung = new Person("Adam", "adam_88@mail.com", 1988);
        final Person bert = new Person("Bert", "bert@mail.com", 1979);
        final Person bert2 = new Person("Bert", "bert@mail.com", 1979) {

            @Override
            public String toString() {
                return "Strange:" + super.toString();
            }

        };
        final Person cecelia = new FemalePerson("Guchi", "Cecelia", "cecelia@mail.com", 1981);
        final Person ceceliaPro = new FemalePerson("Guchi Pro", "Cecelia", "cecelia@mail.com", 1981);
        final Person cecelia2 = new Person("Cecelia", "cecelia@mail.com", 1981);

        printEquals(adam, adam2);
        printEquals(adam2, adam);
        printEquals(bert, bert2);
        printEquals(bert2, bert);
        printEquals(cecelia, cecelia2);
        printEquals(cecelia2, cecelia);

        final List l = Arrays.asList(cecelia, adamYoung, ceceliaPro, cecelia2, adam, bert, adam2, bert2);

        System.out.println("*** Initial order");
        l.forEach(System.out::println);
        System.out.println("*** Sorted order");
        Collections.sort(l);
        l.forEach(System.out::println);

    }

    private static void printEquals(Person p1, Person p2) {
        System.out.println("It is " + p1.equals(p2) + " that " + p1 + " equals " + p2
                + ". hashCode()s are " + ((p1.hashCode() == p2.hashCode()) ? "equals" : "are different"));
    }

}

We first create two adams with identical bean properties (adam and adam2) followed by a younger version of adam called youngAdam. Then we create two berts with the same bean properties but with different toString() methods, just to illustrated what happens with anonymous class overrides. Then we have three incarnations of cecilias: two FemalePersons with different HandbagBrands, then a cecilia that is just a Person. The objects are rigged to demonstrate different behavior of the implementations depicted in this post.

The test program then prints out how some of the objects that are related in terms of equality and then a list with the person in a mixed order is created. This list is printed, then the list is sorted (to test the compareTo() method) and is then printed again.

When run, the following result will show:

It is true that Person{Name=Adam, Email=adam@mail.com, Born=1966} equals Person{Name=Adam, Email=adam@mail.com, Born=1966}. hashCode()s are equals
It is true that Person{Name=Adam, Email=adam@mail.com, Born=1966} equals Person{Name=Adam, Email=adam@mail.com, Born=1966}. hashCode()s are equals
It is false that Person{Name=Bert, Email=bert@mail.com, Born=1979} equals Strange:Person{Name=Bert, Email=bert@mail.com, Born=1979}. hashCode()s are equals
It is false that Strange:Person{Name=Bert, Email=bert@mail.com, Born=1979} equals Person{Name=Bert, Email=bert@mail.com, Born=1979}. hashCode()s are equals
It is false that FemalePerson{Name=Cecelia, Email=cecelia@mail.com, Born=1981, HandbagBrand=Guchi} equals Person{Name=Cecelia, Email=cecelia@mail.com, Born=1981}. hashCode()s are are different
It is false that Person{Name=Cecelia, Email=cecelia@mail.com, Born=1981} equals FemalePerson{Name=Cecelia, Email=cecelia@mail.com, Born=1981, HandbagBrand=Guchi}. hashCode()s are are different
*** Initial order
FemalePerson{Name=Cecelia, Email=cecelia@mail.com, Born=1981, HandbagBrand=Guchi}
Person{Name=Adam, Email=adam_88@mail.com, Born=1988}
FemalePerson{Name=Cecelia, Email=cecelia@mail.com, Born=1981, HandbagBrand=Guchi Pro}
Person{Name=Cecelia, Email=cecelia@mail.com, Born=1981}
Person{Name=Adam, Email=adam@mail.com, Born=1966}
Person{Name=Bert, Email=bert@mail.com, Born=1979}
Person{Name=Adam, Email=adam@mail.com, Born=1966}
Strange:Person{Name=Bert, Email=bert@mail.com, Born=1979}
*** Sorted order
Person{Name=Adam, Email=adam@mail.com, Born=1966}
Person{Name=Adam, Email=adam@mail.com, Born=1966}
Person{Name=Adam, Email=adam_88@mail.com, Born=1988}
Person{Name=Bert, Email=bert@mail.com, Born=1979}
Strange:Person{Name=Bert, Email=bert@mail.com, Born=1979}
FemalePerson{Name=Cecelia, Email=cecelia@mail.com, Born=1981, HandbagBrand=Guchi}
FemalePerson{Name=Cecelia, Email=cecelia@mail.com, Born=1981, HandbagBrand=Guchi Pro}
Person{Name=Cecelia, Email=cecelia@mail.com, Born=1981}  


As can be seen, it works as expected. As we allredy are aware of, bert2 will not equal bert even though their bean properties are the same because they are not of the same class. There are no occurrences of two objects being equal but at the same time having different hashCodes. The mixed list is sorted in correct order.

Abstract Object Support Class

Consider the following abstract object support class:
public abstract class AbstractObjectSupport<T extends AbstractObjectSupport<T>> implements Comparable<T> {
    protected abstract Object[] members();
    protected abstract Object[] names();
    protected abstract Comparable<?>[] compareToMembers();
    protected Object[] mkArray(final Object... members) {
        return members;
    }

    protected Comparable<?>[] mkComparableArray(final Comparable<?>... members) {
        return members;
    }

    protected Object[] exArray(final Object[] originalMembers, final Object... newMembers) {
        final Object[] result = Arrays.copyOf(originalMembers, originalMembers.length + newMembers.length);
        for (int i = originalMembers.length, n = 0; i < result.length; i++, n++) {
            result[i] = newMembers[n];
        }
        return result;
    }

    protected Comparable<?>[] exComparableArray(final Comparable<?>[] originalMembers, final Comparable<?>... newMembers) {
        final Comparable<?>[] result = (Comparable<?>[]) exArray(originalMembers, (Object[]) newMembers);
        return result;
    }

    @Override
    public int hashCode() {
        return Objects.hash(members());

    }
    @Override
    public boolean equals(final Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        @SuppressWarnings("rawtypes")
        // Must be an AbstractObjectSupport since the class is the same as this class
        final AbstractObjectSupport thatAbstractObjectSupport = (AbstractObjectSupport) obj; 
        return Arrays.equals(members(), thatAbstractObjectSupport.members());
    }

    @Override
    public String toString() {
        final String className = getClass().getSimpleName().isEmpty() ? getClass().getName() : getClass().getSimpleName();
        final StringJoiner sj = new StringJoiner(", ", className + "{", "}");
        final Object[] members = members();
        final Object[] names = names();
        final int n = Math.min(members.length, names.length);
        for (int i = 0; i < n; i++) {
            final StringJoiner msj = new StringJoiner("=");
            msj.add(Objects.toString(names[i]));
            msj.add(Objects.toString(members[i]));
            sj.merge(msj);
        }
        return sj.toString();
    }

    @Override
    public int compareTo(T that) {
        @SuppressWarnings("rawtypes")
        final Comparable[] thisComparables = this.compareToMembers();
        @SuppressWarnings("rawtypes")
        final Comparable[] thatComparables = that.compareToMembers();

        final int n = Math.min(thisComparables.length, thatComparables.length);
        for (int i = 0; i < n; i++) {
            @SuppressWarnings("unchecked")
            final int result = thisComparables[i].compareTo(thatComparables[i]);
            if (result != 0) {
                return result;
            }
        }
        return 0; // They are equal
    }

}

When subclassing from this class, a new concrete class must implement the three support methods:

- members() that will return an ordered array with all the bean properties.
- names() that will return an ordered array of the corresponding bean propoerty names. These names are used in the toString() function only.
- compareToMembers() that will return an ordered array with all the (Comparable) bean properties that shall be used in the compareTo() method.

Then the class implements the equals(), hashCode(), toString() and compareTo() methods by first using the corresponding support methods and then performing some logic on the results. Note how simple equals() and hashCode() are implemented using the Objects and Arrays classes. Note also the use of StringJoiner in the toString() method.

Now we can create our Person and FemalePerson classes very easily like this:
public class Person extends AbstractObjectSupport<Person> {

    private final String name;
    private final String email;
    private final int born;

    public Person(String name, String email, int born) {
        this.name = name;
        this.email = email;
        this.born = born;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }

    public int getBorn() {
        return born;
    }

    @Override
    public Object[] members() {
        return mkArray(getName(), getEmail(), getBorn());
    }

    @Override
    public Object[] names() {
        return mkArray("Name", "Email", "Born");
    }

    @Override
    public Comparable<?>[] compareToMembers() {
        return mkComparableArray(getName());
    }

}


and

public class FemalePerson extends Person {

    private final String handbagBrand;

    public FemalePerson(String handbagBrand, String name, String email, int born) {
        super(name, email, born);
        this.handbagBrand = handbagBrand;
    }

    @Override
    public Object[] members() {
        return exArray(super.members(), getHandbagBrand());
    }

    @Override
    public Object[] names() {
        return exArray(super.names(), "handbagBrand");
    }

    public String getHandbagBrand() {
        return handbagBrand;
    }

}

We are now certain that the equals() and hashCode() methods are using the same bean properties and thus we know that they will fulfill their contracts. If we run out test program, we will get the same result as before which is encouraging.

This pattern can be used if you have not inherited from a super class before, but since Java objects can only have one super class, you can not use this pattern when you want to inherit from another class. In the next chapters, we will learn how we can mix in these methods while still being able to inherit from another class.

The Object Support Mixin Pattern

Java 8 provides default methods in interfaces. This functionality was needed to extend existing classes (such as the Collection classes) while retaining compatibility with old code. New methods can be added to interface without the need to implement these methods in the implementing classes. This feature can also be used for other purposes. Now is a good time to mention that some people are against the use of interfaces as carrying any form of logic. According to them, interfaces shall only describe what can be done, not how! I will not engage in this philosophic discussion now. As a marker that this is not just any interface, I have chosen to name the interface to ObjectMixin where the suffix "Mixin" is intended to indicate that it is more than just an interface: methods will be mixed in (not inherited) from the interface. The ObjectMixin looks very similar to the AbstractObjectSupport class:

public interface ObjectMixin<T extends ObjectMixin<T>> {

    Object[] members();

    Object[] names();

    Comparable<?>[] compareToMembers();

    default Object[] mkArray(final Object... members) {
        return members;
    }

    default Comparable<?>[] mkComparableArray(final Comparable<?>... members) {
        return members;
    }

    default Object[] exArray(final Object[] originalMembers, final Object... newMembers) {
        final Object[] result = Arrays.copyOf(originalMembers, originalMembers.length + newMembers.length);
        for (int i = originalMembers.length, n = 0; i < result.length; i++, n++) {
            result[i] = newMembers[n];
        }
        return result;
    }

    default Comparable<?>[] exComparableArray(final Comparable<?>[] originalMembers, final Comparable<?>... newMembers) {
     
        final Comparable<?>[] result = (Comparable<?>[])exArray(originalMembers, (Object[])newMembers);
        return result;
    }

    default int _hashCode() {
        return Objects.hash(members());
    }

    default boolean _equals(final Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        @SuppressWarnings("rawtypes")
        // Must be an AbstractObjectSupport since the class is the same as this class
        final AbstractObjectSupport thatAbstractObjectSupport = (AbstractObjectSupport) obj; 
        return Arrays.equals(members(), thatAbstractObjectSupport.members());
    }

    default String _toString() {
        final String className = getClass().getSimpleName().isEmpty() ? getClass().getName() : getClass().getSimpleName();
        final StringJoiner sj = new StringJoiner(", ", className + "{", "}");
        final Object[] members = members();
        final Object[] names = names();
        final int n = Math.min(members.length, names.length);
        for (int i = 0; i < n; i++) {
            final StringJoiner msj = new StringJoiner("=");
            msj.add(Objects.toString(names[i]));
            msj.add(Objects.toString(members[i]));
            sj.merge(msj);
        }
        return sj.toString();
    }

    default int _compareTo(T obj) {
        @SuppressWarnings("rawtypes")
        final Comparable[] thisComparables = compareToMembers();
        @SuppressWarnings("rawtypes")
        final Comparable[] thatComparables = obj.compareToMembers();

        final int n = Math.min(thisComparables.length, thatComparables.length);
        for (int i = 0; i < n; i++) {
            @SuppressWarnings("unchecked")
            final int result = thisComparables[i].compareTo(thatComparables[i]);
            if (result != 0) {
                return result;
            }
        }
        return 0; // They are equal
    }

}


If we let our Person class implement this interface, it can look like this:

public class Person implements Comparable<Person>, ObjectMixin<Person> {

    private final String name;
    private final String email;
    private final int born;

    public Person(String name, String email, int born) {
        this.name = name;
        this.email = email;
        this.born = born;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }

    public int getBorn() {
        return born;
    }

    @Override
    public Object[] members() {
        return mkArray(getName(), getEmail(), getBorn());
    }

    @Override
    public Object[] names() {
        return mkArray("Name", "Email", "Born");
    }

    @Override
    public Comparable<?>[] compareToMembers() {
        return mkComparableArray(getName());
    }

    @Override
    public int hashCode() {
        return _hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return _equals(obj);
    }

    @Override
    public String toString() {
        return _toString();
    }

    @Override
    public int compareTo(Person o) {
        return _compareTo(o);
    }

}

This way, we do not need to scarify the inheritance and but can still gain all the benefits that the AbstractObjectSupport gave us. The only disadvantage is that we need to explicitly override the equals(), hashCode(), toString() and compareTo() methods and delegate to the ObjectMixin methods. As you might be aware of, an interface can neither introduce new bean properties nor can it override existing methods. When we run the test program, we still get the same output as before.

Classes can easily be extended just as in the previous chapter where we saw FemalePerson being declared.

The Standard Object Support Mixin

We still have the nuisance that overridden classes like anonymous classes are not equal to seemingly equal classes. For example, bert and bert2 are not equal even though they have the same bean properties. Remember that only the toString() differs and this should not make them different. By introducing a new method called compareClass() we can use this class instead of the getClass() and compare them. Now we are in charge what class we elect to return and can set a new "watermark" whenever we think that an inherited class shall never be equal to its super class. The neat thing with the solution below is that we will also have a default compareClass() that automatically will determine the highest class that also is an ObjectMixin. So, you get the initial base class compareClass() for free. Note how the defaultBaseCompareObjectMixinClass() is hid in the inner class MethodUtil so it will not be exposed to the implementing class. The  defaultBaseCompareObjectMixinClass() recursively inspects super classes and when a super class does not implement ObjectMixin, it returns.

public interface ObjectMixin<T extends ObjectMixin<T>> {

    Object[] members();

    Object[] names();

    Comparable<?>[] compareToMembers();

    default Class<? extends ObjectMixin<T>> compareClass() {
        return MethodUtil.defaultBaseCompareObjectMixinClass((Class<T>) getClass());
    }

    default Object[] mkArray(final Object... members) {
        return members;
    }

    default Comparable<?>[] mkComparableArray(final Comparable<?>... members) {
        return members;
    }

    default Object[] exArray(final Object[] originalMembers, final Object... newMembers) {
        final Object[] result = Arrays.copyOf(originalMembers, originalMembers.length + newMembers.length);
        for (int i = originalMembers.length, n = 0; i < result.length; i++, n++) {
            result[i] = newMembers[n];
        }
        return result;
    }

    default Comparable<?>[] exComparableArray(final Comparable<?>[] originalMembers, final Comparable<?>... newMembers) {

        final Comparable<?>[] result = (Comparable<?>[]) exArray(originalMembers, (Object[]) newMembers);
        return result;
    }

    default int _hashCode() {
        return Objects.hash(members());
    }

    default boolean _equals(final Object obj) {
        if (!(obj instanceof ObjectMixin)) {
            return false;
        }
        @SuppressWarnings("rawtypes")
        final ObjectMixin thatObjectMixin = (ObjectMixin) obj;
        if (this.compareClass() != thatObjectMixin.compareClass()) {
            return false;
        }
        return Arrays.equals(members(), thatObjectMixin.members());
    }

    default String _toString() {
        final String className = getClass().getSimpleName().isEmpty() ? getClass().getName() : getClass().getSimpleName();
        final StringJoiner sj = new StringJoiner(", ", className + "{", "}");
        final Object[] members = members();
        final Object[] names = names();
        final int n = Math.min(members.length, names.length);
        for (int i = 0; i < n; i++) {
            final StringJoiner msj = new StringJoiner("=");
            msj.add(Objects.toString(names[i]));
            msj.add(Objects.toString(members[i]));
            sj.merge(msj);
        }
        return sj.toString();
    }

    default int _compareTo(T obj) {
        @SuppressWarnings("rawtypes")
        final Comparable[] thisComparables = compareToMembers();
        @SuppressWarnings("rawtypes")
        final Comparable[] thatComparables = obj.compareToMembers();

        final int n = Math.min(thisComparables.length, thatComparables.length);
        for (int i = 0; i < n; i++) {
            @SuppressWarnings("unchecked")
            final int result = thisComparables[i].compareTo(thatComparables[i]);
            if (result != 0) {
                return result;
            }
        }
        return 0; // They are equal
    }

    static abstract class MethodUtil {

        public static <T extends ObjectMixin> Class<T> defaultBaseCompareObjectMixinClass(Class<T> clazz) {
            final Class<? super T> superClazz = clazz.getSuperclass();
            if (!ObjectMixin.class.isAssignableFrom(superClazz)) {
                return clazz;
            }
            @SuppressWarnings("unchecked")
            final Class<T> objectMixinSuperClazz = (Class<T>) superClazz;
            return defaultBaseCompareObjectMixinClass(objectMixinSuperClazz);
        }

    }

}

Please note that the equals() method now considers the compareClass() instead of just the getClass() and that we have full control of the compareClass() method as opposed to the getClass() method.

When we now run the test program we get the following result (shortened listing):

It is true that Person{Name=Adam, Email=adam@mail.com, Born=1966} equals Person{Name=Adam, Email=adam@mail.com, Born=1966}. hashCode()s are equals

It is true that Person{Name=Adam, Email=adam@mail.com, Born=1966} equals Person{Name=Adam, Email=adam@mail.com, Born=1966}. hashCode()s are equals

It is true that Person{Name=Bert, Email=bert@mail.com, Born=1979} equals Strange:com.blogspot.minborgsjavapot.objectmixin._4interface_class.Test$1{Name=Bert, Email=bert@mail.com, Born=1979}. hashCode()s are equals

It is true that Strange:com.blogspot.minborgsjavapot.objectmixin._4interface_class.Test$1{Name=Bert, Email=bert@mail.com, Born=1979} equals Person{Name=Bert, Email=bert@mail.com, Born=1979}. hashCode()s are equals

It is false that FemalePerson{Name=Cecelia, Email=cecelia@mail.com, Born=1981, handbagBrand=Guchi} equals Person{Name=Cecelia, Email=cecelia@mail.com, Born=1981}. hashCode()s are are different

It is false that Person{Name=Cecelia, Email=cecelia@mail.com, Born=1981} equals FemalePerson{Name=Cecelia, Email=cecelia@mail.com, Born=1981, handbagBrand=Guchi}. hashCode()s are are different


Now, bert and bert2 are equal just as we would expect! Great progress!

When FemalePerson inherit from Person, we also set a new watermark to ensure that FemalePerson are never equal to Person() as shown in this class:

public class FemalePerson extends Person {

    private final String handbagBrand;

    public FemalePerson(String handbagBrand, String name, String email, int born) {
        super(name, email, born);
        this.handbagBrand = handbagBrand;
    }

    @Override
    public Object[] members() {
        return exArray(super.members(), getHandbagBrand());
    }

    @Override
    public Object[] names() {
        return exArray(super.names(), "handbagBrand");
    }

    public String getHandbagBrand() {
        return handbagBrand;
    }

    @Override
    public Class<FemalePerson> compareClass() {
        return FemalePerson.class;
    }

}

The Reflection Object Support Mixin

The pattern can be simplified even more for the implementing classes. It is possible for the interface to provide default support methods for the menbers() and names() methods, eliminating the need for implementing these method by hand. This can be done by extending the previous ObjectMixin inteface as shown hereunder:

public interface ReflectionObjectMixin<T extends ReflectionObjectMixin<T>> extends ObjectMixin<T> {

    @Override
    default Object[] members() {
        return new MethodUtil(getClass()) {

            @Override
            protected Object onMethod(final Method method) {
                try {
                    return method.invoke(ReflectionObjectMixin.this, (Object[]) null);
                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                    throw new IllegalStateException("Unexpected invocation error", e);
                }
            }
        }.toObjects();

    }

    @Override
    default Object[] names() {

        return new MethodUtil(getClass()) {

            @Override
            protected Object onMethod(final Method method) {
                return method.getName().substring(MethodUtil.INGRESS.length());
            }
        }.toObjects();

    }

    static abstract class MethodUtil {

        public static final String INGRESS = "get";
        public static final Set<String> EXCLUDED_METHODS = new HashSet<>(Arrays.asList("getClass"));
        private final Class<?> clazz;

        private MethodUtil(Class<?> clazz) {
            this.clazz = clazz;
        }

        private static List<Method> obtainGetMethods(Class<?> clazz) {
            final List<Method> result = new ArrayList<>();
            final Method[] methods = clazz.getMethods();
            for (final Method method : methods) {
                final String methodName = method.getName();
                if (methodName.startsWith(INGRESS) && method.getParameterCount() == 0 && !EXCLUDED_METHODS.contains(methodName)) {
                    result.add(method);
                }
            }
            Collections.sort(result, METHOD_COMPARATOR);
            return result;
        }

        protected abstract Object onMethod(Method method);

        public Object[] toObjects() {
            final List<Object> result = new ArrayList<>();
            for (final Method method : MethodUtil.obtainGetMethods(clazz)) {
                result.add(onMethod(method));
            }
            return result.toArray();
        }

        private final static MethodComparator METHOD_COMPARATOR = new MethodComparator();

        private static class MethodComparator implements Comparator<Method> {

            @Override
            public int compare(Method o1, Method o2) {
                int classCompare = o1.getDeclaringClass().getName().compareTo(o2.getDeclaringClass().getName());
                if (classCompare != 0) {
                    return classCompare;
                }
                return o1.getName().compareTo(o2.getName());
            }
        }
    }
}

Both the new default methods members() and names() will iterate over all methods that starts with "get" (except the getClass()) and that does not take any parameters. These are all assumed to be bean properties as dictated by the Bean Pattern. For the names() method, we will just cut out the name of the bean property as the name of the getter excluding the "get" prefix (e.g. "getName" becomes "Name"). For the members() method, we will iterate over the same methods but instead we will invoke the method for the bean and save the resulting result in the result array. The clazz.getMethods() will return the classes methods in any order, so we will sort the methods in class declaration order (name of the class it is declared in) and then in alphabetic order of the method name.

The implementing class is now shorter since we got rid of the members() and names() method declaration:

public class Person implements Comparable<Person>, ReflectionObjectMixin<Person> {

    private final String name;
    private final String email;
    private final int born;

    public Person(String name, String email, int born) {
        this.name = name;
        this.email = email;
        this.born = born;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }

    public int getBorn() {
        return born;
    }

    @Override
    public Comparable<?>[] compareToMembers() {
        return mkComparableArray(getName());
    }

    @Override
    public int hashCode() {
        return _hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return _equals(obj);
    }

    @Override
    public String toString() {
        return _toString();
    }

    @Override
    public int compareTo(Person o) {
        return _compareTo(o);
    }

}

When we run the test program we get the following output:

It is true that Person{Born=1966, Email=adam@mail.com, Name=Adam} equals Person{Born=1966, Email=adam@mail.com, Name=Adam}. hashCode()s are equals
It is true that Person{Born=1966, Email=adam@mail.com, Name=Adam} equals Person{Born=1966, Email=adam@mail.com, Name=Adam}. hashCode()s are equals
It is true that Person{Born=1979, Email=bert@mail.com, Name=Bert} equals Strange:javapot.objectmixin._5interface_reflection.Test$1{Born=1979, Email=bert@mail.com, Name=Bert}. hashCode()s are equals
It is true that Strange:javapot.objectmixin._5interface_reflection.Test$1{Born=1979, Email=bert@mail.com, Name=Bert} equals Person{Born=1979, Email=bert@mail.com, Name=Bert}. hashCode()s are equals
It is false that FemalePerson{HandbagBrand=Guchi, Born=1981, Email=cecelia@mail.com, Name=Cecelia} equals Person{Born=1981, Email=cecelia@mail.com, Name=Cecelia}. hashCode()s are are different
It is false that Person{Born=1981, Email=cecelia@mail.com, Name=Cecelia} equals FemalePerson{HandbagBrand=Guchi, Born=1981, Email=cecelia@mail.com, Name=Cecelia}. hashCode()s are are different
*** Initial order
FemalePerson{HandbagBrand=Guchi, Born=1981, Email=cecelia@mail.com, Name=Cecelia}
Person{Born=1988, Email=adam_88@mail.com, Name=Adam}
FemalePerson{HandbagBrand=Guchi Pro, Born=1981, Email=cecelia@mail.com, Name=Cecelia}
Person{Born=1981, Email=cecelia@mail.com, Name=Cecelia}
Person{Born=1966, Email=adam@mail.com, Name=Adam}
Person{Born=1979, Email=bert@mail.com, Name=Bert}
Person{Born=1966, Email=adam@mail.com, Name=Adam}
Strange:javapot.objectmixin._5interface_reflection.Test$1{Born=1979, Email=bert@mail.com, Name=Bert}
*** Sorted order
Person{Born=1988, Email=adam_88@mail.com, Name=Adam}
Person{Born=1966, Email=adam@mail.com, Name=Adam}
Person{Born=1966, Email=adam@mail.com, Name=Adam}
Person{Born=1979, Email=bert@mail.com, Name=Bert}
Strange:javapot.objectmixin._5interface_reflection.Test$1{Born=1979, Email=bert@mail.com, Name=Bert}
FemalePerson{HandbagBrand=Guchi, Born=1981, Email=cecelia@mail.com, Name=Cecelia}
FemalePerson{HandbagBrand=Guchi Pro, Born=1981, Email=cecelia@mail.com, Name=Cecelia}
Person{Born=1981, Email=cecelia@mail.com, Name=Cecelia}

Nice! The only thing that differs is the order of the bean properties in the toString() method.

It becomes even better when we are considering the class FemalePerson, which now looks like this:

public class FemalePerson extends Person {

    private final String handbagBrand;

    public FemalePerson(String handbagBrand, String name, String email, int born) {
        super(name, email, born);
        this.handbagBrand = handbagBrand;
    }

    public String getHandbagBrand() {
        return handbagBrand;
    }

}

It is almost magical, you now get everything for free! The equals(), compareTo() and toString() automatically adjusts to the newly introduced bean property.


The Annotated Object Support Mixin

We can also decide what methods shall be used in the members() and names() method by using annotations. We start by creating our own annotation class named EqualsAndHashCode:

@Retention(value = RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface EqualsAndHashCode {

}

The intention now is that we should simply be able to annotate our methods that we want to "mark" as being in the members() and names() function using this annotation. To allow this we create yet another variant of the ObjectMixin as follows:

public interface AnnotationObjectMixin<T extends AnnotationObjectMixin<T>> extends ObjectMixin<T> {

    @Override
    default Object[] members() {
        return new MethodUtil(getClass(), EqualsAndHashCode.class) {

            @Override
            protected Object onMethod(final Method method) {
                try {
                    return method.invoke(AnnotationObjectMixin.this, (Object[]) null);
                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                    throw new IllegalStateException("Unexpected invocation error", e);
                }
            }
        }.toObjects();

    }

    @Override
    default Object[] names() {

        return new MethodUtil(getClass(), EqualsAndHashCode.class) {

            @Override
            protected Object onMethod(final Method method) {
                final String methodName = method.getName();
                if (methodName.startsWith(INGRESS)) {
                    return methodName.substring(ReflectionObjectMixin.MethodUtil.INGRESS.length());
                } else {
                    return methodName;
                }
            }
        }.toObjects();

    }

    static abstract class MethodUtil {

        public static final String INGRESS = "get";
        private final Class<?> clazz;
        private final Class annotationClass;

        private MethodUtil(Class<?> clazz, Class annotationClass) {
            this.clazz = clazz;
            this.annotationClass = annotationClass;
        }

        private static List<Method> obtainGetMethods(Class<?> clazz, Class annotationClass) {
            final List<Method> result = new ArrayList<>();
            final Method[] methods = clazz.getMethods();
            for (final Method method : methods) {
                if (method.getParameterCount() == 0 && (method.getAnnotation(annotationClass) != null)) {
                    result.add(method);
                }
            }
            Collections.sort(result, METHOD_COMPARATOR);
            return result;
        }

        protected abstract Object onMethod(Method method);

        public Object[] toObjects() {
            final List<Object> result = new ArrayList<>();
            for (final Method method : MethodUtil.obtainGetMethods(clazz, annotationClass)) {
                result.add(onMethod(method));
            }
            return result.toArray();
        }

        private final static MethodComparator METHOD_COMPARATOR = new MethodComparator();

        private static class MethodComparator implements Comparator<Method> {

            @Override
            public int compare(Method o1, Method o2) {
                int classCompare = o1.getDeclaringClass().getName().compareTo(o2.getDeclaringClass().getName());
                if (classCompare != 0) {
                    return classCompare;
                }
                return o1.getName().compareTo(o2.getName());
            }
        }
    }
}

Now we are able just to "mark" our implementing class methods with @EqualsAndHashCode as shown here:

public class Person implements Comparable<Person>, AnnotationObjectMixin<Person> {

    private final String name;
    private final String email;
    private final int born;

    public Person(String name, String email, int born) {
        this.name = name;
        this.email = email;
        this.born = born;
    }

    @EqualsAndHashCode
    public String getName() {
        return name;
    }

    @EqualsAndHashCode
    public String getEmail() {
        return email;
    }

    @EqualsAndHashCode
    public int getBorn() {
        return born;
    }

    @Override
    public Comparable<?>[] compareToMembers() {
        return mkComparableArray(getName());
    }

    @Override
    public int hashCode() {
        return _hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return _equals(obj);
    }

    @Override
    public String toString() {
        return _toString();
    }

    @Override
    public int compareTo(Person o) {
        return _compareTo(o);
    }

}

The extending FemalePerson class can now look like this:

public class FemalePerson extends Person {

    private final String handbagBrand;

    public FemalePerson(String handbagBrand, String name, String email, int born) {
        super(name, email, born);
        this.handbagBrand = handbagBrand;
    }

    @EqualsAndHashCode
    public String getHandbagBrand() {
        return handbagBrand;
    }

}

Performance

The equals() and hashCode() methods call the members() method which converts any primitive bean properties (such as int) to their corresponding wrapper classes (e.g. Integer) by means of auto-boxing. This leads to unnecisary creation of short lived objecs compare to hand coded equals() and hashCode() methods where the primitives can be used directly for comparison.

The performance of reflection is relatively poor, so if you use the ReflectionObjectMixin or the AnnotationObjectMixin you will notice reduced performance. A large part of this performance drop can be regained by caching the reflection calls using a ConcurrentHashMap as shown in the following snippet, form a performance optimized ReflectionObjectMixin class:

        private static final Map<Class<?>, List<Method>> methodCache = new ConcurrentHashMap<>();

        private static List<Method> obtainGetMethods(Class<?> clazz) {

            List<Method> cacheResult = methodCache.get(clazz);
            if (cacheResult != null) {
                return cacheResult;
            } else {
                final List<Method> result = new ArrayList<>();
                final Method[] methods = clazz.getMethods();
                for (final Method method : methods) {
                    final String methodName = method.getName();
                    if (methodName.startsWith(INGRESS) && method.getParameterCount() == 0 && !EXCLUDED_METHODS.contains(methodName)) {
                        result.add(method);
                    }
                }
                Collections.sort(result, METHOD_COMPARATOR);
                methodCache.put(clazz, result);
                return result;
            }
        }
The performance of the compareClass() can also be improved in the same way using a static lookup Map.


Interface Wrapper Class

If you can use inheritance, you can create a small interface wrapper class that you can inherit from to save the work of overriding the Object methods like this:

public class ReflectionObjectSupport<T extends ReflectionObjectMixin<T>> implements ReflectionObjectMixin<T>, Comparable<T> {

    @Override
    public Comparable<?>[] compareToMembers() {
        throw new UnsupportedOperationException("Override this method in your class to implement comapreTo() support.");
    }

    @Override
    public boolean equals(Object obj) {
        return _equals(obj);
    }

    @Override
    public int hashCode() {
        return _hashCode();
    }

    @Override
    public String toString() {
        return _toString();
    }

    @Override
    public int compareTo(T o) {
        return _compareTo(o);
    }

}


Now your Person class can look just as promised at the top of this post!

Conclusions

You should develop a strategy on how to override the equals() and hashCode() methods that ensures that you will use the same bean properties for them both. You should also make sure that, when you override classes, their equals() and hashCode() should work as expected.

The Object Support Mixin Pattern ensures that the contract of the equals() and hashCode() are fulfilled. Furthermore, it makes coding of these method much easier and less error prone. The Object Support Mixin Pattern allows you to extend a different super class and just mix in the functionality you need without scarifying the single class inheritance. The Object Support Mixin Pattern also allows easy subclassing, both with normal classes and anonymous classes. One drawback with the pattern is that its performance is less than their hand coded counter parts.

Future Improvements

All the mixin methods and support methods are exposed as public methods. Perhaps it is possible to move the methods to an inner class so that they are not seen directly in the implementing class.

Bean properties stored using primitive classes (such as ints and longs) can perhaps be handled by separate member() methods to eliminate auto-boxing overhead. Perhaps, these primitive bean properties shall be compared before the wrapper class bean properties since, presumably, they are faster to compare.

Good luck with improving your basic Object methods!