Apr 232012
 

How many times have you created ad-hoc classes, when you need to return multiple values? Would it not be better if your programming language natively would support it, when you want to send a group of values? Java currently has no native support for Tuple’s, but with the use of generics, there’s a solution.

Here’s what we think the solution needs to be:

  • Type safe
  • Immutable
  • Easy to create and to use

The first version of out solution looks like this:

public class Tuple2<X, Y> {

    private final X x;
    private final Y y;
    protected Tuple2(X x, Y y) {
        checkParameters(x, y);
        this.x = x;
        this.y = y;
    }

    public X getFirst() {
        return x;
    }

    public Y getSecond() {
        return y;
    }

    @Override
    public String toString() {
        return String.format("[Tuple2<%s, %s>: (%s, %s)]", x.getClass().getSimpleName(), y.getClass().getSimpleName(), x, y);
    }

    private void checkParameters(X xToCheck, Y yToCheck) {
        if (xToCheck == null || yToCheck == null) {
            throw new IllegalArgumentException("Null Values are not allowed");
        }
    }
}

Though when we try to use it, we realize that the syntax is not so nice:

Tuple2<String, Double> tup = new Tuple2<String, Double>("Kalle", 123D);

Using this syntax is cumbersome, and doesn’t make our life as easy as we thought it would. Perhaps a factory could help here?

Here’s the factory’s first implementation:

public class TupleFactoy {

    private TupleFactoy() {
        // I'm a util class, please do not create me!
    }

    public static <X, Y> Tuple2 createTuple2(X x, Y y) {
        return new Tuple2(x, y);
    }
}

Using the factory looks a bit better (also, it’s a great place to create more stuff like, hmm, maybe Tuple3 or Tuple4):

Tuple2<String, Double> tup = TupleFactoy.createTuple2("Kalle", 123D);

But still we have a problem: we can’t force users to use the factory – the Tuple2 constructor is public! We want the TupleFactory to be as straitforward as possible, and we achieve that using some refactoring. Here’s the final version of the factory and the Tuple classes:

package se.diversify.util;

public class TupleFactory {

    private TupleFactory() {
        // I'm a util class, please do not create me!
    }

    public static <X, Y> Tuple2 createTuple2(X x, Y y) {
        return new Tuple2Impl(x, y);
    }

    private static class Tuple2Impl<X, Y> implements Tuple2 {
        private final X x;
        private final Y y;

        protected Tuple2Impl(X x, Y y) {
            checkParameters(x, y);
            this.x = x;
            this.y = y;
        }

        @Override
        public X getFirst() {
            return x;
        }

        @Override
        public Y getSecond() {
            return y;
        }

        @Override
        public X _1() {
            return x;
        }

        @Override
        public Y _2() {
            return y;
        }

        @Override
        public String toString() {
            return String.format("[Tuple2<%s, %s>: (%s, %s)]", x.getClass().getSimpleName(), y.getClass().getSimpleName(), x, y);
        }

        private void checkParameters(X xToCheck, Y yToCheck) {
            if (xToCheck == null || yToCheck == null) {
                throw new IllegalArgumentException("Null Values are not allowed");
            }
        }
    }

    public interface Tuple2<X, Y> {
        X getFirst();
        Y getSecond();
        X _1();
        Y _2();

        @Override
        String toString();
    }
}

Using the classes is quite simple:

Tuple2<String, Double> tup = TupleFactory.createTuple2("Kalle", 123D);

Or, as a return value from a method:

Tuple2<String, Integer> getHostAndPort() {
    String host = "localhost";
    Integer port = Integer.valueOf(8080);

    return TupleFactory.createTuple2(host, port);
}

As you can see, we’ve also added the methods _1() and _2() to make the implementation look a bit more Scala-ish:

Tuple2<String, Integer> tup = getHostAndPort();

String host =  tup._1(); // or tup.getFirst();
Integer port = tup._2(); // or tup.getSecond();

Great isn’t it? Now, go write your own implementations of Tuple3 and Tuple4 :)

  3 Responses to “Tuple – a nice way to combine a group of values”

  1. Nice! It looks pretty much like the Tuple class found in the .NET Framework. It might be me not understanding Java, but it looks strange to me that the Tuple2Impl class is static?

    • If you want the inner class to act as a top-level class, you use static on the inner class.

      A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class. In effect, a static nested class is behaviorally a top-level class that has been nested in another top-level class for packaging convenience.

 Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>