Как сказать Java, что два подстановочных знака совпадают?
Я написал небольшой список сортировщик, который работает с помощью Key
объекты для извлечения конкретных "ключей" из объектов в сопоставимой форме. Затем сортировщик сортирует список в соответствии со всеми ключами по очереди.
Сортировщик может сортировать, используя любой набор ключей, которые работают с данными типами объектов. Каждый ключ может работать с одним типом объекта и всегда возвращает один и тот же тип сопоставимого значения.
class Sorter {
public interface Key<T, V extends Comparable<V>> {
public V get(T t);
}
static <T> void sort(List<T> data, final Key<T, ?> key[], final int dir[]) {
Collections.sort(list, new Comparator<T>() {
public int compare(T a, T b) {
for (int i = 0; i < key.length; i++) {
final Comparable av = key[i].get(a), bv = key[i].get(b);
final int cmp = av.compareTo(bv);
if (cmp != 0) return cmp * dir[i];
}
return 0;
}
});
}
}
Так, например, вы могли бы иметь JSONStringKey
что извлекает String
(который Comparable
), и вы могли бы иметь отдельный JSONNumericKey
что извлекает Double
) (который также Comparable
). Значения двух разных ключей никогда не будут сравниваться, но один и тот же ключ для двух разных объектов будет сравниваться.
class JSONStringKey extends Sorter.Key<JSONObject, String> {
final String key;
JSONStringKey(String key) {this.key = key;}
public String get(JSONObject o) {return o.optString(key);}
}
class JSONNumericKey extends Sorter.Key<JSONObject, Double> {
final String key;
JSONNumericKey(String key) {this.key = key;}
public Double get(JSONObject o) {return o.optDouble(key);}
}
...
// sort by price descending then name ascending
final Key<JSONObject, ?> keys[] = { new JSONNumericKey("price"), new JSONStringKey("name") };
sort(list, keys, new int[]{-1, 1});
Java предупреждает об этой строке в сортировщике:
final Comparable av = key[i].get(a), bv = key[i].get(b);
Предупреждает, что av
а также bv
объявлены с необработанными типами: Comparable
вместо Comparable<?>
, И они. Но если я поменяю тип на Comparable<?>
затем следующая строка, av.compareTo(bv)
не удается, потому что два разных Comparable<?>
не обязательно того же типа. В моей конкретной реализации они будут, но я не знаю, как выразить это системе типов.
Как я могу сказать, что система типов av
а также bv
есть точно такой же тип? Я не могу исправить это, указав определенный тип (например, Comparable<String>
) потому что в моем примере первый ключ в цикле возвращает String
(который реализует Comparable<String>
) и второй ключ в цикле возвращает Double
(который реализует Comparable<Double>
).
Я мог бы написать @SuppressWarnings("rawtypes")
на key[i].get()
линии и @SuppressWarnings("unchecked")
на av.compareTo(bv)
линии, но я хочу, чтобы типы проверялись, насколько это возможно.
РЕДАКТИРОВАТЬ: благодаря ответу от davmac, создание промежуточного метода, который фиксирует определенный сопоставимый тип, работает правильно:
public int compare(T a, T b) {
for (int i = 0; i < key.length; i++) {
final int cmp = compareKey(key[i], a, b);
if (cmp != 0) return cmp * dir[i];
}
}
private <V extends Comparable<V>> compareKey(Key<T, V> key, T a, T b) {
final V av = key.get(a), bv = key.get(b);
return av.compareTo(bv);
}
2 ответа
Вам нужно использовать параметр типа, чтобы сказать, что два "неизвестных" типа одинаковы. Я подумал, что, возможно, вам следует изменить сигнатуру вашего метода с:
static <T> void sort(List<T> data, final Key<T, ?> key[], final int dir[])
в
static <T,U> void sort(List<T> data, final Key<T,U> key[], final int dir[])
Однако я не думаю, что это сработает с вашим полным примером, потому что элементы из ключей не одного типа:
// sort by price descending then name ascending
final Key<JSONObject, ?> keys[] = { new JSONNumericKey("price"), new JSONStringKey("name") };
sort(list, keys, new int[]{-1, 1});
Таким образом, вместо этого вы можете извлечь соответствующую часть из сортировщика в общий метод:
public int compare(T a, T b) {
for (int i = 0; i < key.length; i++) {
final Comparable<?> av = key[i].get(a), bv = key[i].get(b);
final int cmp = doCompareTo(av, bv);
if (cmp != 0) return cmp * dir[i];
}
return 0;
}
private <U extends Comparable<U>> int doCompareTo(U a, U b) {
return a.compareTo(b);
}
... но это тоже не сработает, потому что Comparable<U>
не обязательно extend U
, Проблема в том, что ваши ключи возвращаются Comparable<V>
, но вы хотите сравнить два из них; это невозможно. Comparable<V>
можно сравнить с V
, но не с другим Comparable<V>
,
В общем, здесь слишком много вопросов, чтобы дать вам простое решение. Вам нужно полностью переосмыслить типы. Например, если вы хотите Key
метод get для возврата объектов, которые сопоставимы друг с другом, тогда он должен вернуть V
и не Comparable<V>
,
Я надеюсь, что приведенные выше предложения, по крайней мере, укажут вам правильное направление.
Как вы их объявили прямо сейчас, вы можете использовать приватный помощник захвата:
static <T> void sort(List<T> data, final Key<T, ?> key[], final int dir[]) {
Collections.sort(list, new Comparator<T>() {
public int compare(T a, T b) {
for (int i = 0; i < key.length; i++) {
final int cmp = compareHelper(key[i], a, b);
if (cmp != 0) return cmp * dir[i];
}
return 0;
}
});
}
private static <T, U extends Comparable<U>> int compareHelper(Key<T, U> k, T a, T b) {
U av = k.get(a), bv = k.get(b);
return av.compareTo(bv);
}
Или вы можете избавиться от V
от Key
в целом и сделать ограничение на sort
только, который будет параметризован на U
:
public interface Key<T, V> {
public V get(T t);
}
static <T, U extends Comparable<? super U>> void sort(List<T> data, final Key<T, U> key[], final int dir[]) {
Collections.sort(list, new Comparator<T>() {
public int compare(T a, T b) {
for (int i = 0; i < key.length; i++) {
U av = k.get(a), bv = k.get(b);
final int cmp = av.compareTo(bv);
if (cmp != 0) return cmp * dir[i];
}
return 0;
}
});
}