Freitag, 5. März 2010

Tuple и Java...

Tuple и Java... Возможно ли? Да конечно. Google поиск выдает первой строкой http://javatuple.com. Здесь реализация а ля Вандервуд и Джосати. Эти корифеи С++ известны своим изданием книги:

"Шаблоны С++. Справочник разработчика."


А причем здесь С++, - cпросите вы. Ответ прост. Для реализации Tuple в Java, используется точно такой же принцип как и в С++.
А именно:

public class Tuple<Type1, Type2> {
public Type1 type1;
public Type2 type2;
};

например такая декларация

Tuple<String, Integer> pair;

равносильна следующему классу:

public class Tuple {
public String type1;
public Integer type2;
};

или вот пример для трех элементов

Tuple<String, Tuple<Integer, Boolean>> triple;

по аналогии

public class Tuple {   
public String type1;
public Tuple<Integer, Boolean> type2;
};

но к сожалению чем больше элементов в Tuple тем сложнее читать код.

Tuple<Tuple<String, Integer>, Tuple<Boolean, Tuple<Double, Long>>> quintuple; //пять элементов.

String element1 = quintuple.type1.type1;
Integer element2 = quintuple.type1.type2;
Boolean element3 = quintuple.type2.type1;
Double element4 = quintuple.type2.type2.type1;
Long element6 = quintuple.type2.type2.type2;

это все выглядит не очень читаемо. Конечно можно написать еще кучу дополнительного кода что бы легко было манипулировать с данными.

public static <Type1, Type2> Tuple<Type1, Type2>   
make(Type1 type1, Type2 type2) {

Tuple<Type1, Type2> tuple = new Tuple<Type1, Type2>();
tuple.type1 = type1;
tuple.type2 = type2;

return tuple;
}
...
public static <Type1, Type2, Type3, Type4, Type5> Tuple<Tuple<Type1, Type2>, Tuple<Type3, Tuple<Type4, Type5>>>
make(Type1 type1, Type2 type2, Type3 type3, Type4 type4, Type5 type5) {

Tuple<Tuple<Type1, Type2>, Tuple<Type3, Tuple<Type4, Type5>>> tuple = new Tuple<Tuple<Type1, Type2>, Tuple<Type3, Tuple<Type4, Type5>>>();
quintuple.type1 = new Tuple<Type1, Type2>();
quintuple.type1.type1 = type1;
quintuple.type1.type2 = type2;

quintuple.type2 = new Tuple<Type3, Tuple<Type4, Type5>>();
quintuple.type2.type1 = type3;

quintuple.type2.type2 = new Tuple<Type4, Type5>();
quintuple.type2.type2.type1 = type4;
quintuple.type2.type2.type2 = type5;

return tuple;
}

...
по моему это выглядит ужасно.
Меня не покидала мысль как можно сделать Tuple доступнее для понимания и чтения.
В конце концов родился следующий вариант:

public class Tuple2<Type1, Type2> {
public Type1 type1;
public Type2 type2;
}

public class Tuple3<Type1, Type2, Type3> extends Tuple2<Type1, Type2> {
public Type3 type3;
}

public class Tuple4<Type1, Type2, Type3, Type4> extends Tuple3<Type1, Type2, Type3> {
public Type4 type4;
}

...
и т.д.
Ну как? Лучше?
Здесь я ушел от принципа бесконечной рекурсии Generic-a, заменив рекурсию на ручное создание классов.Но, так-как первый способ требует создания большого количества вспомогательных функции или классов, из-за сложности чтения кода, то я считаю что затраты оправдывают себя.
Не менее интересны вопрос - это область применения Tuple. Одна из возможностей - объединять данные в структуру без декларации оной, так сказать, на лету. Самый примитивный способ использования этих структур как return тип. Не знаю как вам, но мне часто хотелось вернуть из функции(, метода и т.д.) больше чем одну переменную.Например:

static Tuple2 split(String input) {
String[] splited = input.split(",");
// здесь вместо комментария проводим проверку или нет :)
Tuple2 output = new Tuple2();
output.type1 = Integer.valueOf(splited[0]);
output.type2 = splited[1];

return output;
}

почему не так:

statis String split(String input, Integer i);

или так

statis Integer split(String input, String out);

или вот как

statis Object[] split(String input);

думаю вопрос не стоит:)
и на ходу рабочий пример:

public class Tuple {
static class Tuple2<Type1, Type2> {
public Type1 type1;
public Type2 type2;
}

static class Tuple3<Type1, Type2, Type3> extends Tuple2<Type1, Type2> {
public Type3 type3;
}

static class Tuple4<Type1, Type2, Type3, Type4> extends Tuple3<Type1, Type2, Type3> {
public Type4 type4;
}

static Tuple2<Integer, String> split(String input) {
String[] splited = input.split(",");
Tuple2<Integer, String> output = new Tuple2<Integer, String>();
output.type1 = Integer.valueOf(splited[0]);
output.type2 = splited[1];

return output;
}

public static void main(String... args) {
final Tuple2<Integer, String> splited = split("10,number");
System.out.println(String.format("This is %s %d", splited.type2, splited.type1));

final Tuple3<String, Integer, Boolean> tuple3 = new Tuple3<String, Integer, Boolean>();
tuple3.type1 = "string";
tuple3.type2 = 10;
tuple3.type3 = true;
System.out.println(String.format("type1: %s, type2: %d, type3: %b", tuple3.type1, tuple3.type2, tuple3.type3));

final Tuple4<String, Integer, Boolean, Double> tuple4 = new Tuple4<String, Integer, Boolean, Double>();
tuple4.type1 = "string";
tuple4.type2 = 10;
tuple4.type3 = true;
tuple4.type4 = 2.5;
System.out.println(String.format("type1: %s, type2: %d, type3: %b, type4: %f", tuple4.type1, tuple4.type2, tuple4.type3, tuple4.type4));
}
}

Keine Kommentare:

Kommentar veröffentlichen