Понимание Коллекций.reverseOrder() метод в Java

рассмотрим одно из перегруженных определений sort метод Array класс:

public static <T> void sort(T[] a, Comparator<? super T> c)

обычный способ сортировки массива в обратном порядке-передать Comparator возвращено Collections.reverseOrder() как второй аргумент этого метода.

давайте посмотрим на реализацию Collections.reverseOrder() метод из openjdk 7:

public static <T> Comparator<T> reverseOrder() {
    return (Comparator<T>) ReverseComparator.REVERSE_ORDER;
}

ReverseComparator класс:

private static class ReverseComparator
    implements Comparator<Comparable<Object>>, Serializable {

    private static final long serialVersionUID = 7207038068494060240L;

    static final ReverseComparator REVERSE_ORDER = new ReverseComparator();

    public int compare(Comparable<Object> c1, Comparable<Object> c2) {
        return c2.compareTo(c1);
    }

    private Object readResolve() { return reverseOrder(); }
}

мой вопрос: почему Collections.reverseOrder() сделано, чтобы быть общим? И почему просто ReverseComparator.REVERSE_ORDER не может быть вернулся?

конечно, мы можем указать тип, явно вызывая Collections.<T>reverseOrder(). Но какая польза от простого Collections.reverseOrder() в этом случае?

я нашел там довольно полезную дискуссию:

как коллекции.reverseOrder () знаете, какой параметр типа использовать?

и мне интересно, как sort способ использования compare метод ReverseComparator класса. Как можем см.compare принимает доводы Comparable<Object> тип. И что делать, если мы сортируем массив объектов, реализующих Comparable<T>, где T к примеру Integer? Мы не можем вызвать compare С Comparable<Integer> причина Comparable<Integer> не отлит в Comparable<Object>.

2 ответов


почему коллекциях.reverseOrder () должен быть общим?

эта функция является общей, чтобы избавить вас от необходимости бросать результат в ваш конкретный Comparable<T> тип. (Вы можете сказать, что вам все равно, потому что вы все равно не бросаете его, и в этом случае это говорит нам о том, что у вас недостаточно предупреждений.)

почему мы не можем просто вернуть ReverseComparator.REVERSE_ORDER?

один причина в том, что ReverseComparator.REVERSE_ORDER является частным пакетом, поэтому вы не можете получить к нему доступ извне этого пакета. Что, в свою очередь, вызывает вопрос: "почему это пакет-частный?"Ну, в основном потому, что это удовлетворяет пуристов, которые съеживаются, когда видят, что переменные-члены напрямую доступны, даже если они являются окончательными, но на самом деле я бы не винил их в этом случае, потому что аксессоры предлагают прямую совместимость на двоичном уровне, что может быть совершенно ненужным в коде приложения, но это становится необходимость в языковой среде выполнения. И ReverseComparator является частью среды выполнения Java.

но более важная причина в том, что Collections.reverseOrder() делает бросок (Comparator<T>) для вас, чтобы вам не пришлось делать это самому. (Опять же, если вы не видите проблемы с этим, это потому, что у вас недостаточно предупреждений, что означает, что вам нужно пересмотреть свои практики.)

короче говоря, если вы попытались сделать следующее:

Comparator<MyObject> myComparator = ReverseComparator.REVERSE_ORDER;

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

Comparator<MyObject> myComparator = 
    (Comparator<MyObject>)ReverseComparator.REVERSE_ORDER;

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

@SuppressWarnings( "unchecked" )
Comparator<MyObject> myComparator = 
    (Comparator<MyObject>)ReverseComparator.REVERSE_ORDER;

что это некрасиво. Итак,Collections.reverseOrder() спасает вас от этого, позволяя вам сделать это:

Comparator<MyObject> myComparator = Collections.reverseOrder();

как мы видим, compare принимает аргументы сопоставимого типа. И что делать, если мы сортируем массив объектов, реализующих Comparable, где T для пример целого числа? Мы не можем вызвать сравнение с сопоставимой причиной, сопоставимой, не приведено к сопоставимому.

хорошо, я вижу, в чем ваш настоящий вопрос. Добро пожаловать в удивительный мир Java generics и стирания типов. Я попытаюсь объяснить, но обязательно также посмотрите термин "стирание типа", чтобы полностью понять концепцию.

в java дженерики были введены в язык как запоздалая мысль. По этой причине они должны были быть реализованы в таких способ, которым код с поддержкой дженериков будет обратно совместим со старым кодом, который не был с поддержкой дженериков. Решение было трюком под названием стирание типа, что в основном означает, что общая информация полностью удаляется после компиляции. Это означает, что на уровне байт-кода, Comparator<String> и Comparator<Integer> и Comparator это одно и то же. Никакой разницы. Это то, что позволяет среде выполнения java реализовать один класс, который действует как обратный компаратор любого объекта. Это не действительно a Comparator<Object>, это Comparator<ANYTHING>, потому что все, что он делает, это меняет направление сравнения, поэтому он действительно не заботится о природе сравниваемых объектов.

Итак, в java, если вы действительно знаете, что делаете, вы можете привести экземпляр универсального класса к экземпляру того же класса, но с другим общим параметром. В этом случае создатели Java runtime выполняют кастинг Comparator<Object> to Comparator<T>, который в мае факт будет позже назначен Comparator<Integer>, и это нормально.

этот бросок сложен, потому что компилятор должен доверять тому, что вы действительно знаете, что делаете, поэтому по умолчанию компилятор выдает предупреждение "непроверенное назначение" на таких бросках, а затем, в свою очередь, мы указываем, что мы клянемся, что знаем, что мы делаем с @SuppressWarnings( "unchecked" ) Примечание.

Collections.reverseOrder() избавляет вас от необходимости беспокоиться обо всем этом.


это все о стирании типа. Помните, что во время выполнения нет такой вещи, как Comparable<Object>, есть только такая вещь, как Comparable. Таким образом,compare метод REVERSE_COMPARATOR работает на двух String экземпляров, например. Это не вызывает ClassCastException во время выполнения, потому что String осуществляет Comparable<String>, и во время выполнения, это просто Comparable.

метод reverseComparator должно быть общим, потому что в противном случае пользователю пришлось бы привести возвращаемый объект к соответствующий тип, прежде чем он может быть использован. Например, рассмотрим этот код, где компаратор имеет тот же тип, что и объявленный тип REVERSE_COMPARATOR.
Comparator<Comparable<Object>> comparator = Collections.reverseOrder();
String[] arr = {"A", "B", "C"};
Arrays.sort(arr, comparator);       // Doesn't compile.

почему это не компиляция, потому что arr это String массив, и так Arrays.sort ждет Comparator<? super String>, а Comparable<Object> не является супертипом Comparable<String> (является ли List подклассом List? Почему дженерики Java не являются неявно полиморфными?).

вы можете скомпилировать его с помощью слепки:

Comparator<Comparable<Object>> comparator = Collections.reverseOrder();
String[] arr = {"A", "B", "C"};
Arrays.sort(arr, (Comparator<String>) (Object) comparator);
System.out.println(Arrays.toString(arr));   // prints [C, B, A]

это генерирует предупреждение, но, как вы увидите, вы попробуете его, он работает. Используя общий метод, уродство приведения и предупреждения сохраняется из кода с помощью метода.

тот факт, что тот же объект (REVERSE_COMPARATOR) можно рассматривать как Comparator<String> или Comparator<Integer> или Comparator<X> здесь X любой тип реализации Comparable одно из много преимуществ типа стирания. Это повторное использование объектов невозможно в C#, потому что в C# экземпляры универсального класса знают тип.

существует много примеров такого повторного использования. Например, все эти общие методы всегда возвращают один и тот же экземпляр, независимо от того, какой универсальный тип вы предоставляете.

Collections.emptySet()
Optional.empty()
Comparator.naturalOrder()
Collections.emptyListIterator()