Как сказать 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;
        }
    });
}
Другие вопросы по тегам