Понимание Коллекций.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
вы можете скомпилировать его с помощью слепки:
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()