Кортежи объектов в Java и их коллекции

Достаточно часто на практике возникает необходимость воспользоваться такими конструкциями как «пара» (Pair<First, Second>), менее часто «тринарными» объектами (Triplet<First, Second, Third>) и более длинными цепочками ассоциированных объектов. В связи с чем всегда удивлялся, почему в JDK (в java.lang.* или java.util.*) до настоящего момента нет стандартной Pair<First, Second> или более длинных конструкций. Думаю многие программисты практикующие на Java имеют свою реализацию «пары». Не исключением являюсь и я.

И вот в очередной раз наткнувшись на необходимость работать с большим количеством различных кортежей, решил взяться за эту проблему системно. Придумал название проекту, определил цели и после экспериментов, занявших некоторое время, выложил код ( git://github.com/telesik/rumba.git ).

Чего хотелось достичь:
  • Необходима удобная конструкция (назовем ее кортеж) для ассоциации нескольких объектов, типы которых определялись бы из позиции в полученном кортеже;
  • Кортеж должен быть неограниченным по длине и типу;
  • Типы элементов должны быть относительно контролируемым (как минимум ограничен дженериками);
  • Описание структуры кортежа должно быть простым;

Явление на свет кортежа


И вот на свет появился интерфейс Cortege:
public interface Cortege<V, T extends Cortege> {
    T setValue(V value);
    T nextElement();
    <Vi> Vi getValue(int index) throws ClassCastException; // be careful with types. Type is not bounded
    int getDeep();
    <Vi> void setValue(int index, Vi value); // be careful with types. Type is not bounded
    Cortege<V, T> setValues(Object... values); // be careful with types. Type is not bounded!!!
    V getValue();
    static abstract class End<V, T extends Cortege<V, T>> implements Cortege<V, T> {
    }
}

На рисунке ниже наглядно показан механизм описания кортежей произвольной длины и типов элементов
image
Рис. 1. механизм описания кортежей произвольной длины и типов элементов

Но что есть “голый” интерфейс, если нет ни одной реализации? Есть такая! Имя ей CortegeChain. Наличие в названии слова Chain обусловлено способом организации хранения элементов в кортеже. Предполагаю и надеюсь, что в будущем появятся другие реализации, оптимизированные для различных типов использования.
В реализации CortegeChain я не ставил особых целей оптимизации с точки зрения использования памяти или скорости работы. Основной задачей, которую я хотел решить — это попробовать саму идею, найти слабые места, обозначить очевидные и не очевидные возможности, открывающиеся перед разработчиком.

Примеры использования


Перейду сразу к наглядным примерам использования:

// создание инстанса кортежа:
Cortege<Long, Cortege<String, Cortege.End>> cortegeLS = CortegeChain.create(2);

Следует обратить внимание на аргумент метода create. В связи с тем, что в Java дженерики существуют лишь на этапе компиляции, и добраться к ним в runtime я «законных» методов не нашел :(, пришлось «заплатить» за это такой ценой. Суть этого параметра — декларация глубины создаваемого кортежа. Но в виду того, что разработчик как правило знает кол-во элементов, необходимость указания этого параметра не должно вызывать больших проблем.

примечание: Но все же, если кто-то может предложить свое решение это проблемки с параметром — я буду чрезвычайно благодарен.

Итак пример,
// заполнение элементов значениями
// 1-й вариант (заполняем первый элемент в кортеже, с контролем типа)
cortegeLS.setValue(4L);
cortegeLS.nextElement().setValue("str");
// 2-й вариант (заполняем подряд цепью, с контролем типа)
cortegeLS.setValue(4L).setValue("str");
// 3-й вариант (заполняем массивом, без контроля типа)
cortegeLS.setValues(4L, "str");

Теперь чтение,
// 1-й вариант (чтение первого элемента в кортеже, с контролем типа)
Long valueA = cortegeLS.getValue();
// 2-й вариант (чтение выбранного элемента в кортеже, с контролем типа)
String valueB = cortegeLS.nextElement().getValue();
// 3-й вариант (чтение выбранного элемента в кортеже, без контроля типа)
Long valueC = cortegeLS.getValue(1);
String valueD = cortegeLS.getValue(2);

Что еще можно сделать с кортежем:
1. Получить кортеж “справа”.
Cortege<String, Cortege.End> rightCortegeS = cortegeLS.nextElement();
// еще раз
Cortege.End rightCortegeEnd = cortegeLS.nextElement().nextElement();

2. Получить “глубину” (кол-во элементов в кортеже)
int deep = cortegeLS.getDeep();

Пожалуй все. Но не все! :)

Коллекции кортежей


Создавал я кортеж не просто так. Теперь имея такую конструкцию хорошо бы ее «прикрутить» к дружному семейству коллекций (java.util.Collection). Сказано — сделано. Но банальное использование кортежа как элемента коллекции не интересно. Хочется не просто находить, удалять, модифицировать элементы коллекции вцелом, что дают традиционные реализации интерфейсов java.util.Set и java.util.List, но и искать, фильтровать по отдельным элементам кортежей, хранящимся в коллекции, модифицировать колонки целиком и т.п, что является естественным для реляционных таблиц.

Итак вновь поставив цели:
  • Коллекция должна быть совместима с давно ставшими стандартом интерфейсами семейства java.util.Collection (такими как java.util.Set и java.util.List)
  • Имплементация должна расширять функционал своих аналогов в мире простых объектов (например таких как java.util.HashSet<T>);
  • В коллекции должно быть легко искать по абстрактному критерию;
  • Необходимо реализовать получение подмножества;
  • Очень хотелось бы реализовать возможность создания представления (аналог view в реляционных базах) над коллекцией.
    • представление не “владеет” исходными данными, а лишь фильтрует оригинальную коллекцию согласно критерию
    • фильтрация должна быть не одноразовой, а динамической. т.е. должна быть актуальной и отражать состояние оригинальной таблицы. все вставки, модификации и удаления из коллекции должны отражаться в ее представлении согласно предикату наложенному на view.

Примечание: Из всего, что я хотел, не получилось только реализовать последний пункт “представление” (аналог view в реляционных базах данных). Но работа идет и возможно в недалеком будущем на свет появится реализация и этой многообещающей идеи.

И вот что получилось:
public interface CortegeCollection<T extends Cortege> extends Collection<T>, Iterable<T> {
    <T> boolean contains(int num, T obj);
    CortegeCollection<T> extract(int num, Object key);
    CortegeCollection<T> extract(Corteges.Predicate<T> predicate);
//    <C> CortegeCollection<T> view(int num, Corteges.Predicate<C> predicate);
    T findAny(int num, Object key);
    T findAny(Corteges.Predicate<T> predicate);
    <Vi> List<Vi> getColumnCopy(int num);
    <Vi> void fill(int num, Vi value);
}

public interface CortegeSet<T extends Cortege> extends CortegeCollection<T>, Set<T> {
}

public interface CortegeList<T extends Cortege> extends CortegeCollection<T>, List<T> {
}


image
Рис. 2. Топология кортежных коллекций

Примеры использования коллекций кортежей


Теперь наверное стоит как и в случае с описанием Cortege сразу перейти к наглядным примерам использования:
public class ExampleCollections {

    public static void main(String[] args) {
        // Создание экземпляра CortegeHashSet
        CortegeSet<Cortege<Long, Cortege<String, Cortege.End>>> cortegeHashSetLS = Corteges.newCortegeHashSet(2);
        for (long i = 0; i < 5; i++) {
            Cortege<Long, Cortege<String, Cortege.End>> cortegeLS = CortegeChain.create(2);
            cortegeLS.setValue(i).setValue("" + i);
            cortegeHashSetLS.add(cortegeLS);
        }
        for (Cortege cortege : cortegeHashSetLS) {
            System.out.println(cortege);
        }

        cortegeHashSetLS.add(CortegeChain.<Long, Cortege<String, Cortege.End>>create(2));

        Cortege<Long, Cortege<String, Cortege.End>> cortegeIS = CortegeChain.create(2);

        System.out.println(cortegeHashSetLS.contains(cortegeIS));
        cortegeIS.setValue(null).setValue("3");
        System.out.println(cortegeIS);
        System.out.println(cortegeHashSetLS.contains(cortegeIS));

        System.out.println(cortegeHashSetLS.contains(1, 3L));

        Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>> cortegeLLS1 = CortegeChain.create(3);
        Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>> cortegeLLS2 = CortegeChain.create(3);
        Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>> cortegeLLS3 = CortegeChain.create(3);
        CortegeChain<String, CortegeChain<Long, CortegeChain<String, Cortege.End>>> cortegeSLS = CortegeChain.create(3);

        cortegeLLS1.setValue(1L);
        cortegeLLS1.nextElement().setValue(11L);
        cortegeLLS1.nextElement().nextElement().setValue("AAA");
        cortegeLLS2.setValue(2L);
        cortegeLLS2.nextElement().nextElement().setValue("BBB");
        cortegeLLS3.setValue(3L);
        cortegeLLS3.nextElement().setValue(33L);
        cortegeLLS3.nextElement().nextElement().setValue("AAA");

        CortegeHashSet<Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>>> cortegeSetLLS = Corteges.newCortegeHashSet(cortegeLLS1.getDeep());

        System.out.println(cortegeSetLLS.contains(cortegeLLS1));
        cortegeSetLLS.add(cortegeLLS1);
        cortegeSetLLS.add(cortegeLLS2);
        cortegeSetLLS.add(cortegeLLS3);
        System.out.println(cortegeSetLLS.contains(cortegeLLS1));

        for (Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>> cortege : cortegeSetLLS) {
            System.out.println(cortege);
        }

        System.out.println(cortegeSetLLS.contains(3, "AAA"));

        cortegeSetLLS.fill(1, 5L);
        cortegeSetLLS.fill(2, 8L);
        cortegeSetLLS.fill(3, "XXX");

        for (Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>> cortege : cortegeSetLLS) {
            System.out.println(cortege);
        }

//        Collection<Cortege> corteges = cortegeSetLLS.extract(2, "111");
    }
}


Подведем итоги


Как видно из примера, получилась достаточно удобная библиотека для хранения и манипулирования с длинными кортежами объектов. К бенефитам библиотеки можно отнести:
  • Создание кортежа произвольной длины достаточно просто и сводится лишь к перечислению типов элементов составляющих будущий экземпляр кортежа
  • При последовательном обращении к элементам кортежа контролируется тип элемента
  • Благодаря выбранной структуре декларации и хранения кортежа возможно получение под-кортежа «справа» (метод nextElement())

Кроме самой структуры Cortege в библиотеке есть два типа коллекций, расширяющих всем известные java.util.Set и java.util.List) соответственно com.rumba.cortege.CortegeSet и com.rumba.cortege.CortegeList).
  • Есть возможность найти кортеж или подмножество кортежей в коллекции по простому критерию эквивалентности элемента эталонному объекту (методы extract(int num, Object key) и findAny(int num, Object key))
  • Есть возможность найти кортеж или подмножество кортежей в коллекции по предикату (методы extract(Corteges.Predicate<T> predicate) и findAny(Corteges.Predicate<T> predicate))
  • Есть возмодность получить любую колонку с указанным номером образованную из элементов кортежей коллекции
  • Есть возмодность заполнить целиком колонку элементов

Но и есть очевидные недостатки обусловленные в первую очередь невозможностью «добраться» к декларациям дженериков в Java и, как следствие, потеря контроля над типом при обращении по индексу к элементу кортежа. Справедливости ради я поискал библиотеки, решающие подобные задачи, и нашел достаточно интересную реализацию javatuples. В ней проблема декларации «длинных» цепочек ассоциациированных объектов решается простым перебором “разумного” с точки зрения разработчиков максимального размера кортежа.
  • Unit<A> (1 element)
  • Pair<A,B> (2 elements)
  • Triplet<A,B,C> (3 elements)
  • Quartet<A,B,C,D> (4 elements)
  • Quintet<A,B,C,D,E> (5 elements)
  • Sextet<A,B,C,D,E,F> (6 elements)
  • Septet<A,B,C,D,E,F,G> (7 elements)
  • Octet<A,B,C,D,E,F,G,H> (8 elements)
  • Ennead<A,B,C,D,E,F,G,H,I> (9 elements)
  • Decade<A,B,C,D,E,F,G,H,I,J> (10 elements)


Заключение


Работая над библиотекой я старался покрыть наиболее типичные, и с моей точки зрения, полезные задачи, возникающие при работе с кортежами и их коллекциями. К сожалению не все планы реализовано, но надеюсь та толика работы, что была выполнена будет кому-нибудь полезна. Но работа продолжается, надеюсь не зря.
Было бы очень интересно получить конструктивный фитбэк на эту статью и библиотеку.

Полезные ссылки


javatuples
достаточно интересная попытка реализации
Поделиться публикацией
Комментарии 30
    +2
    Красивая схема классов. Я так понимаю, это IntellijIDEA генерирует такую?
      +1
      именно так
        +1
        Жаль Community Edition так не умеет, только Ultimate
      +5
      В английском нет такого слова «cortege» в математическом смысле. Есть «tuple».
        +1
        В простых случаях хватает org.apache.commons.collections.keyvalue.MultiKey из commons-collections
          +1
          Возможно и хватит. А если нужно больше 5-ти элементов? и MultiKey не имеет контроля типов элементов.
            +1
            Если брать не абстрактные примеры, то для такого случая уже стоило бы сделать класс для значения. (В общем-то, его иногда стоит сделать даже для 2-х элементов)
              +1
              В MultiKey можно хоть сколько объектов хранить. Контроля типов нет. Ну я и говорю в простых случаях.
                +1
                Я загрузил
                <dependency>
                <groupId>commons-collections</groupId>
                <artifactId>commons-collections</artifactId>
                <version>20040616</version>
                </dependency>

                И не увидел как можно сделать MultiKey длиней 5-ти элментов :(.
                Возможно не так библиотека.
                Но как альтернативу конечно тоже можно использовать.
                  0
                  Нужно использовать конструктор MultiKey(Object[] keys), в котором можно указать массив ключей.
              +1
              Cortege не самое главно что мне хотелось показать. Если рассматривать Cortege как банальный контейнер для цепочки объектов, то проще использовать простой массив. :)
              Более интересным с моей точки зрения является не кортеж, а коллекции CortegeSet и CortegeList.
              Возможность использовать их как альтерантиву физической таблицы подобно как в реляционных базах, но в памяти.

              Основной областью применения таких конструкций я вижу в первую очередь в случаях, когда нужно манипулировать с данными в таблице (искать, изменять, добавлять, удалять строки и/или элементы), но когда по окончанию жизненного цикла сама таблица больше не нужна (не сохраняется в базу данных). Например при формировании отчета.
                0
                CortegeSet очень похож на LinkedHashSet, а CortegeLinkedList на LinkedList, плюс стоит задуматься о тормозах при обращении к LinkedList по индексу и о постоянном (без кеширования) вычислении hashCode в Cortege.
              +1
              Писал когда-то похожую библиотеку, теперь жду от оракла включения project lambda в новую JDK.
                +1
                // создание инстанса кортежа:
                Cortege<Long, Cortege<String, Cortege.End>> cortegeLS = CortegeChain.create(2);

                Следует обратить внимание на аргумент метода create. В связи с тем, что в Java дженерики существуют лишь на этапе компиляции, и добраться к ним в runtime я «законных» методов не нашел :(, пришлось «заплатить» за это такой ценой. Суть этого параметра — декларация глубины создаваемого кортежа. Но в виду того, что разработчик как правило знает кол-во элементов, необходимость указания этого параметра не должно вызывать больших проблем.

                примечание: Но все же, если кто-то может предложить свое решение это проблемки с параметром — я буду чрезвычайно благодарен.
                Понятно, что в рантайме такой информации вы и не получите напрямую. Но первое что приходит в голову (и подобным образом в таких случаях делается):
                Cortege<Long, Cortege<String, Cortege.End>> cortegeLS = CortegeChain.create(Long.class,String.class);
                
                , соответственно create какая-то такая:
                create(Class... c)
                , и далее внутри вашего кортежа храните всю эту же информацию о типах, потом можно дёргать и по индексу и прочее, что вы описали ниже. Нет?
                  +1
                  Я именно о таком решении уже думал, но пока не решился на включение его в релиз:
                  Cortege<Long, Cortege<String, Cortege.End>> cortegeLS = CortegeChain.create(Long.class,String.class);

                  Действительно, осуществлять проверку в runtime можно будет достаточно легко.
                  Но есть два минуса:
                  1. декларация кортежа более трех элементов (основная область применения) будет очень длинной :(
                  2. к сожалению проверка будет только на runtime мне кажется этого мало, хочется придумать красивое и компактное решение для котроля на этапе компиляции.

                  Но я полностью с Вами согласен, что предложеное решение позволит повысить контроль над типами. Очень возможно что в следующий релиз войдет Ваше решение. Спасибо.
                    0
                    Для этапа компиляции по-любому надо дополнительно параметризовать генериками сам метод create. Это два несвязанных случая. Но ведь и сейчас он у вас совершенно без всякого контроля типов возвращает кортеж. Или я чего-то не понял?
                      0
                      Да, Вы правы, усилить контроль над типами элеменентов кортежа стоит. Ваше предложение на счет create(Class<? extends Object>... types) разумно. Но опять же остаются грустные случаи когда программист сможет продекларировать кортеж с одним набором типов и кол-вом элементов, но вызвать приэтом метод create с совершенно иным набором типов и их кол-вом. Приведу пример:
                      Cortege<Long, Cortege<String, Cortege.End>> cortegeLS = CortegeChain.create(Character.class, Integer.class, String.class);
                      Я к сожелению не могу придумать как проверить такую ситуацию на этапе компиляции. :( Если Вы можете подсказать решение, буду очень благодарен.
                      На счет:
                      Но ведь и сейчас он у вас совершенно без всякого контроля типов возвращает кортеж. Или я чего-то не понял?

                      Не везде контроль отсутствует. Если брать элементы поочередно методом getValue() тип контролируется на этапе компиляции.
                      Отсутствует контроль, когда происходит обращение к элементу по индексу как в getValue(int index).
                        0
                        Не везде контроль отсутствует. Если брать элементы поочередно методом getValue() тип контролируется на этапе компиляции.
                        Это то понятно, там у вас в некоторых местах так и пишется в комментариях «с контролем типа». Я имею в виду именно сам метод create у вас возвращает бесконтрольный на этапе компиляции объект.
                          0
                          Да, Вы правы:
                          Я имею в виду именно сам метод create у вас возвращает бесконтрольный на этапе компиляции объект.

                          С контролем типов есть большие трудности. Мне очевидно следует полностью пересмотреть идею хранения и контроля типов в кортеже. Это была первая, но возможна не самая удачная попытка. Именно для того чтобы как можно полней осознать все за и против этого способа храннеия кортежа я и вынес на обсуждение эту тему. Но с моей точки зрения всеже есть и явные «фишки» у такого рекурсивной способа описания структуры. Возможно кому-то будет удобно его применить совершенно в другой области. :)

                          Всеже справедливости ради надо сказать, что все так сконцентрировались на кортеже, но практически никто не смотрел на коллекции. В них собственно и весь «сахар» (я же писал о методах findAny и extract). В следующей версии скорее всего будет другая сруктура у кортежа, но то, что получилось сделать в коллекциях я постараюсь сохранить.
                  +8
                  Хм. А вот авторы Guava: Google Core Libraries против кортежей. Пишут, что:
                  Tuple types are awful obfuscators. Tuples obfuscate what the fields actually mean (getFirst and getSecond are almost completely meaningless), and anything known about the field values. Tuples obfuscate method signatures: ListMultimap<Route, Pair<Double, Double>> is much less readable than ListMultimap<Route, LatLong>.


                  code.google.com/p/guava-libraries/wiki/IdeaGraveyard#Tuples_for_n_%3E=_2

                  И правда, пара пар трех кортежей двух пар звучит зловеще:)
                    0
                    Я более чем согласен. В большинстве своем — это тупо лень разрабочика создавать новый класс с читаемыми названиями (даже внутренний статический).
                      0
                      Согласен. Действительно есть трудность с пониманием смысла значения хронящегося в какой-либо позиции. Особенно когда тип элементов часто повторяется. Например,
                      Cortege<Long, Cortege<Long, Cortege<Long, Cortege.End>>> cortegeLLS = CortegeChain.create(); 

                      Конечно лучше когда есть контейнер с вразумительными геттерами.
                      Но в некоторых случаях может понадобиться получить фрагмент кортежа и рассматривать его как самостоятельную сущность. Cortege как в примере,
                      Cortege<Long, Cortege<Long, Cortege.End>> rightCortegeLS = cortegeLLS.nextElement();

                      Опять же, стоит обратить внимание на инструментарий, который дают имплементации коллекций как: CortegeSetи CortegeList. В посте есть пример использование некоторых возможностей. Например, найти подколлекцию по значению элемента в кортежах или по предикату. (методы findAny и extract)
                      Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>> any5 = cortegeSetLLS.findAny(1, 5L);
                      или
                      CortegeSet<Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>>> extractAll5 = cortegeSetLLS.extract(1, 5L);
                      +1
                      Вы вызвали у меня вселенскую грусть. Кортежи — зло, за исключением исчезающе малого числа случаев.

                      Вот хотите вы воспользоваться библиотекой своего коллеги, смотрите на API и видите такой метод:
                      Triple<Long, Long, Long> doSomething(Pair<List<Quadruple<String, String, String, String>>, Map<Pair<Integer, Long>, Object>>)
                      

                      Ну как, что вы туда собираетесь передавать? И это, кстати, не надуманный пример: я встречался с этим в продакшне.

                      Или вот посмотрите на этот ужас: code-wtf.livejournal.com/180549.html

                      С использованием вашей «библиотеки» код становится ещё хуже. Мало того, что ни один коллега-иностранец не поймёт вашего слова «cortege», так кому приятно читать такой адский объём boiler plate кода? Добавьте ещё возможность сконфигурировать это XML-конфигами в 100 мегабайт.
                        –1
                        Мне честно очень жаль, что Ваша грусть столь велика.
                        Но мне кажется Вы привели не совсем честный пример, отношение к которому не имеет моя библиотека. Я какраз хотел уйти от подобный кашмарных выражений типа
                        Pair<List<Quadruple<String, String, String, String>>
                        Но конечно же я не могу с Вами не согласиться в том, что название интрефейса Cortege возможно не очень удачно. Обязуюсь отнестись к Вашему замечанию серъезно и постараться решить этот вопрос.
                        Конечно отчасти Ваше замечание на счет громоздкости декларации кортежа справедливо, на данный момент я еще не придумал более компактной формы декларации типов, без потери неограниченности длины кортежа.
                          +1
                          А чем, простите, лучше
                          Cortege< String, Cortege<String<Cortege<String, Cortege<String, Cortege.End>>>>>
                          

                          По мне, так читать стало существенно хуже.

                          От громоздкости вы не сможете избавиться. В java, к счастью, нет аналогов variadic templates; и, я надеюсь, вряд ли будут.
                        –1
                        pastebin.com/xzJamfC0

                        Посмотрите как мы доставали дженерики в рантайме. Надеюсь поможет: )

                        За статью пятерочка
                          0
                          Спасибо большое! Очень интересная ссылка!
                          Подумаю как использовать.
                            0
                            Вы, вообще-то, не достаёте generic-и в рантайме. Всё, что вы делаете — это в случае, если наследник класса указал явный тип, получить его. В противном случае ваш код будет падать с ClassCastException при попытке прикастить результат getActualTypeArguments к Class.

                            public class SomeClass<T> {
                                public Class<?> getEntityClass() {
                                    return ((Class) ((ParameterizedType) getClass().getGenericSuperclass())
                                            .getActualTypeArguments()[0]);
                                }
                            
                                static class ConcreteSubclass extends SomeClass<String> {}
                            
                                static class GenericSubclass<T> extends SomeClass<T> {}
                            
                                public static void main(String[] args) {
                                    System.out.println(new ConcreteSubclass().getEntityClass());
                                    System.out.println(new GenericSubclass<String>().getEntityClass());
                                }
                            }
                            

                            выводит:

                            class java.lang.String
                            Exception in thread "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
                            


                            Если убрать небезопасный каст, то нам вернётся T.

                            public Type getEntityClass() {
                                return ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
                            }
                            

                            выводит:

                            class java.lang.String
                            T
                            
                            +4
                            О боги, какой кошмар. Вот именно из-за таких примеров над нами, яверами, и издеваются за наши громоздкие конструкции поверх всего.

                            Смысл кортежа для программиста — это удобный способ передать или хранить пачку значений. Синтаксический сахар, ведь всегда же можно под конкретный набор передаваемых типов создать класс вроде РезультатМетодаА. И вот ваш синтаксический сахар, он еще и совсем не сладкий, он мало того что горький (нечитаемый), так еще и глубоко теоретический. Вот зачем, зачем вам понадобилось вот это условие:

                            Кортеж должен быть неограниченным по длине и типу


                            Кому от этого станет лучше? Часто вы в своей практике передавали кортежи длиной в 10 элементов? Если и да, то это место, которому точно помог бы рефакторинг.

                            Короче, я вот к чему. Код с использованием такой конструкции неудобен (.nextElement().nextElement()? правда? и это при заявленной неограниченной длине? что за лапша с ним станет при длине кортежа, скажем, в 20?) и, что хуже, не читаем. Решения, просто дающие ограниченный набор шаблонных классов ограниченной длины (будь то Pair, Triplet, etc., или Tuple2, Tuple3, Tuple10 и т.д.) куда как удобнее в использовании, не удовлетворяя при этом единственному, глубоко теоретическому условию.

                            А главное, все так аккуратно и точно сделано, залюбуешься. Эту бы энергию да на мирные цели…
                              0
                              Если и да, то это место, которому точно помог бы рефакторинг.

                              ИМО, тут не рефакторинг уместен, а тотальная экстерминация.

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое