【问题标题】:How to tell Java that two wildcard types are the same?如何告诉 Java 两个通配符类型相同?
【发布时间】:2015-06-11 06:53:06
【问题描述】:

我编写了一个小型列表排序器,它使用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;
            }
        });
    }
}

因此,例如,您可以拥有一个提取 String(即 Comparable)的 JSONStringKey,并且您可以拥有一个单独的 JSONNumericKey 提取 Double)(这也是 @ 987654328@)。永远不会比较来自两个不同键的值,但会比较两个不同对象中的相同键

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

它警告 avbv 使用原始类型声明:Comparable 而不是 Comparable&lt;?&gt;。他们是。但是如果我把类型改成Comparable&lt;?&gt;,那么下一行av.compareTo(bv)就失败了,因为两个不同的Comparable&lt;?&gt;不一定是同一个类型。在我的具体实现中,它们是,但我不知道如何将其表达给类型系统。

如何告诉类型系统avbv 具有完全相同的类型?我无法通过给出特定类型(例如Comparable&lt;String&gt;)来“修复”它,因为在我的示例中,循环中的第一个键返回String(实现Comparable&lt;String&gt;),循环中的第二个键返回@ 987654343@(实现Comparable&lt;Double&gt;)。

我可以在key[i].get() 行上写@SuppressWarnings("rawtypes"),在av.compareTo(bv) 行上写@SuppressWarnings("unchecked"),但我希望尽可能检查类型。

编辑:感谢 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);
}

【问题讨论】:

    标签: java generics types


    【解决方案1】:

    您需要使用类型参数来表示两个“未知”类型是相同的。我想也许你应该改变你的方法签名:

    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&lt;U&gt; 不一定是extend U。问题是您的密钥返回Comparable&lt;V&gt;,但您想比较其中两个;那是不可能的。 Comparable&lt;V&gt; 可以与 V 进行比较,但不能与另一个 Comparable&lt;V&gt; 进行比较。

    一般来说,这里的问题太多了,无法给你一个简单的解决方案。您需要完全重新考虑类型。例如,如果您希望 Key 的 get 方法返回可比较的对象彼此,那么它应该返回 V 而不是 Comparable&lt;V&gt;

    我希望以上建议至少可以为您指明正确的方向。

    【讨论】:

    • 根据您的建议,我已将Key.get 的定义更改为返回V 而不是Comparable&lt;V&gt;,但我仍然不确定如何告诉类型系统@987654337 @ 和 bv 对于任何给定的循环迭代共享相同的类型。
    • @StuartCaie 您应该可以通过根据我的答案的顶部引入类型变量来做到这一点。例如将内部循环中的三行移动到一个新方法中,该方法由键返回的类型参数化;类似:&lt;T,U extends Comparable&lt;U&gt;&gt; int compareParts(Key&lt;T,U&gt; key, T a, T b)
    【解决方案2】:

    按照您现在声明它们的方式,您可以使用私有捕获助手:

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

    或者你可以完全摆脱Key中的V,只对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;
            }
        });
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多