如何告诉Java两个通配符类型是否相同?

时间:2015-06-09 11:38:17

标签: java generics types

我编写了一个小型列表排序器,它通过使用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来提取StringComparable),你可以有一个单独的JSONNumericKey来提取{ {1}})(也是Double)。永远不会比较来自两个不同键的值,但会比较两个不同对象中的相同键

Comparable

Java在分拣机中警告这一行:

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});

它警告final Comparable av = key[i].get(a), bv = key[i].get(b); av使用原始类型声明:bv而不是Comparable。他们是。但是,如果我将类型更改为Comparable<?>,则下一行Comparable<?>会失败,因为两个不同的av.compareTo(bv)不一定是同一类型。在我的具体实现中,他们,但我不知道如何向类型系统表达。

如何告诉类型系统Comparable<?>av具有完全相同的类型?我无法修复&#34;它通过给出一个特定的类型(例如bv),因为在我的例子中,循环中的第一个键返回Comparable<String>(实现String)并且循环中的第二个键返回{{ 1}}(实现Comparable<String>)。

我可以在Double行和Comparable<Double>@SuppressWarnings("rawtypes")上写key[i].get(),但我希望尽可能检查类型。

编辑:感谢davmac的回答,创建一个修复特定类似类型的中间方法可以正常工作:

@SuppressWarnings("unchecked")

2 个答案:

答案 0 :(得分: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>

我希望上述建议至少指出你正确的方向。

答案 1 :(得分:0)

你现在宣布它们的方式,你可以使用私人捕获助手:

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的{​​{1}}并仅对Key进行约束,这将在sort上进行参数设置:

U