Что означает подпись типа Java Comparator.comparing ()? - PullRequest
0 голосов
/ 14 марта 2019

У меня проблемы с пониманием сигнатуры метода:

static <T,U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T,? extends U> keyExtractor)

Смущение от super, extends, группового символа (?) и нескольких угловых скобок. Пожалуйста, не поймите меня неправильно, я понимаю основные дженерики. Именно высшие конструкции, используемые здесь, сбивают меня с толку.

1 Ответ

3 голосов
/ 15 марта 2019

Итак, основная форма функции:

static <T, U> Comparator<T> comparing(Function<T, U> extractor)

Мы рассмотрим мелкозернистые модификации позже. То, что сказано выше, в порядке:

  • static: просто, что важно, хотя это означает, что функция не привязана к типу своего экземпляра объекта, поэтому даже если Comparator определен и создан как Comparator<T>, мы все равно пишем T в ...

  • <T, U>: это говорит нам, что сама функция является общей для типов T и U. Это так же, как когда вы пишете class <T, U> MyClass - вы говорите, что эти типы будут определять поведение остальной части функции. В этом случае эти типы определяют параметры функции и будут выведены в зависимости от того, какой параметр мы предоставляем. Если мы дадим Function<Foo,Bar> comparing(), то компилятор выведет T = Foo.class и U = Bar.class.

  • Comparator<T>: тип возвращаемого значения, способный сравнивать T; довольно просто.

  • Function<T, U>: отображение от T до U; довольно просто.

Теперь для отдыха:

  • <T, U> на самом деле <T,U extends Comparable<? super U>>, что означает T, что угодно, а U необходимо расширить Comparable (т. Е. Быть сопоставимым), даже если он действительно просто сопоставим на основе своего супертипа. Например, у меня может быть Bar extends Foo, где Foo implements Comparable; Я все еще могу сравнивать объекты типа Bar, обрабатывая их как Foo. Вот конкретный пример:

    • Давайте сделаем класс, MyBigInt implements Comparable<MyBigInt>. Предположим, что мы реализовали интерфейс Comparable и наш класс правильно сравнивается на основе его целочисленного значения.
    • Теперь давайте создадим новый класс MyBigColorfulInt extends MyBigInt, который добавляет некоторые глупые нецелые функциональные возможности - важно, что он не реализует Comparable<MyBigColorfulInt>, но как подкласс MyBigInt это (неявно) реализует Comparable<MyBigInt>.
    • Теперь, чтобы вернуться к нашей проблеме с <T, U extends Comparable<? super U>: скажем, наш тип T содержит в себе поле типа MyBigColorfulInt, и я бы хотел сравнить объекты типа T на основе их MyBigColorfulInt члена (это U). ЕСЛИ подпись была U extends Comparable<U>, нам бы не повезло: MyBigColorfulInt не реализует Comparable<MyBigColorfulInt>. Тем не менее, мы все равно хотим сравнить MyBigColorfulInt с MyBigInt. U extends Comparable<? super U>> позволяет нам делать это бесплатно, потому что он позволяет сравнивать U на основе своего суперкласса. Если бы не это, лучшее, что мы могли бы сделать, это привести каждый MyBigColorfulInt к MyBigInt перед сравнением, что не очень хорошо.
  • Function<T, U> на самом деле Function<? super T,? extends U>, поэтому по одному:

    • <? super T> аналогично пункту выше о U, все это действительно означает, что мы должны быть в состоянии извлечь что-то для сравнения (U) из T, и это нормально, если вещь, которую мы извлекаем, на самом деле происходит из суперкласса T.

    • <? extends U> также похож (по-другому) на пункт выше: наша функция должна извлечь вещь типа U для сравнения, но это нормально, если то, что мы извлекаем, является потомок U, при условии, что использовал в качестве U для нашего сравнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...