Definitive Guide to Designing Tuple in Java 8 with 7 different methods

1. Introduction

In this article, we will explore what is and how to write a Tuple in Java. Tuple is an immutable collection or finite list of elements of different types.

[“Thrones”, “Galaxy”, 3.14, 42] is a Tuple containing 4 different elements. The objects in Tuple do not relate to one another. It might seem pretty useless to use Tuple but in fact it can be an exciting feature to use.

Let’s start with learning Tuple and along the way we will have fun with Tuples by experimenting with few examples.

Let us now explore how to write Tuple in Java. We will write a Tuple containing with 2 elements.

2. Class signature

We will name the class as Tuple2 because we are creating a Tuple with 2 elements where T1 is type of element number 1 and T2 is type of element number 2.

public class Tuple2<T1, T2> {

}

3. Private constructor

We have private constructor as we will use static factory to create a Tuple. Look closely, two elements of tuple are public and not private. I will discuss this in a minute, why the elements are public and not private.

public class Tuple2<T1, T2> {

	public final T1 _1;
	public final T2 _2;
 
	private Tuple2(T1 _1, T2 _2) {
		this._1 = _1;
		this._2 = _2;
	}

}

4. Static Factory

This is a static factory to create Tuple2. If any element is null, we will throw NullPointerException so as not create Tuple2 object in an inconsistent state.

public static <T1, T2> Tuple2<T1, T2> of(T1 _1, T2 _2) {
	Objects.requireNonNull(_1);
	Objects.requireNonNull(_2);
	return new Tuple2<T1, T2>(_1, _2);
}

Example :

Tuple2<String, String> stringTuple = Tuple2.of("Jon", "Snow");

5. No get methods

T1 and T2 are only elements of Tuple2 and we should access them in a natural way because Tuple2 is just an elements holder. It just holds the elements which will be accessed by their names, which are _1 and _2, hence making them public. There is no point in declaring them private and providing accessor/getter method for them.

6. Arity

Arity means numbers of arguments to the tuple.

public int arity() {
	return 2;
}

7. Convert Tuple2 to Result

Applies a given BiFunction to T1 and T2 and produces the result of type R. This is the only method that will perform an operation on tuple and doesn’t return a tuple. The method will throw NullPointerException if BiFunction is null. The BiFunction should operate without side effects.

public <R> R apply(BiFunction<T1, T2, R> biFunction) {
	Objects.requireNonNull(biFunction);
	return biFunction.apply(_1, _2);
}

Example :

Tuple2<String, String> tuple = Tuple2.of("Jon", "Snow");

Integer value = tuple.apply((s1, s2) -> (s1.length() 
										+ s2.length())); 

8. Apply to _1

Apply Function to T1, which produces result R and returns new Tuple2<R, T2>.

public <R> Tuple2<R, T2> apply_1(Function<T1, R> function) {
	Objects.requireNonNull(function);
	return Tuple2.of(function.apply(_1), _2);
}

Example :

Tuple2<String, String> tuple = Tuple2.of("Jon", "Snow");

Tuple2<Integer, String> intStrTuple = tuple.apply_1(String::length);

9. Apply to _2

Apply Function to T2, which produces result R and returns new Tuple2<T1, R>.

public <R> Tuple2<T1, R> apply_2(Function<T2, R> function) {
	Objects.requireNonNull(function);
	return Tuple2.of(_1, function.apply(_2));
}

Example :

Tuple2<String, String> tuple = Tuple2.of("Jon", "Snow");

Tuple2<String, Integer> strIntTuple = tuple.apply_2(String::length);

10. Apply to _1 and _2 Version 1

Apply BiFunction to Tuple2<T1, T2> which produces result Tuple2<R1, R2>

public <R1, R2> Tuple2<R1, R2> 
applyBoth(BiFunction<T1, T2, Tuple2<R1, R2>> biFunction) {
	Objects.requireNonNull(biFunction);
	return biFunction.apply(_1, _2);
}

Example :

Tuple2<String, String> tuple = Tuple2.of("Jon", "Snow");

Tuple2<Integer, Integer> intIntTuple = 
	tuple.applyBoth((s1, s2) -> 
					Tuple2.of(s1.length(), s2.length()));

Below is a complex example :

Tuple2<String, String> stringTuple = Tuple2.of("Jon", "Snow");

Tuple2<Predicate<String>, Supplier<String>> complexTuple = 
	Tuple2.of(Objects::isNull, () -> "element must not be null.");
 
Tuple2<Predicate<String>, Supplier<String>> stringSupplier = 
	stringTuple.applyBoth((s1, s2) -> complexTuple);

11. Apply to _1 and _2 Version 2

This method accepts two different functions. Apply first Function to T1, which produces result R1 and apply seconds Function to T2, which produces result R2 and returns new Tuple<R1, R2>.

public <R1, R2> Tuple2<R1, R2> 
apply(Function<T1, R1> function1, Function<T2, R2> function2) {
	Objects.requireNonNull(function1);
	Objects.requireNonNull(function2);
	return Tuple2.of(function1.apply(_1), function2.apply(_2));
}

Example :

Tuple2<String, String> tuple = Tuple2.of("Jon", "Snow");

Tuple2<Integer, Integer> intIntTuple = 
    tuple.apply((str) -> str.length(), (str) -> str.length());

12. Conclusion

Tuple is a very interesting concept. Tuples can be a lot more interesting if used properly. Tuple are just like List except that it can store different elements.

The sequel to this article would be to use Tuple in validations. There are some validations that are common for objects; we will use functional interfaces to do such validations.

Leave a Reply

Your email address will not be published. Required fields are marked *