【问题标题】:Why this difference in the method signature?为什么方法签名有这种差异?
【发布时间】:2013-07-04 14:59:22
【问题描述】:

来自API

1)以下是 TreeMap 的构造函数,它接受另一个地图并使用(传递的地图的)可比较接口对其进行排序。

TreeMap

public TreeMap(Map<? extends K,? extends V> m)

Constructs a new tree map containing the same mappings as the given map, ordered according to the natural ordering of its keys. All

插入新地图的键必须实现 Comparable 界面。此外,所有这些键必须是相互可比的: k1.compareTo(k2) 不能为任何键 k1 抛出 ClassCastException 和地图中的k2。此方法在 n*log(n) 时间内运行。

Parameters:
    m - the map whose mappings are to be placed in this map
Throws:
    ClassCastException - if the keys in m are not Comparable, or are not mutually comparable
    NullPointerException - if the specified map is null

2) 下面是 TreeMap 的构造函数,它接受另一个地图并使用(传递的地图的)比较器接口对其进行排序。

树图

public TreeMap(SortedMap<K,? extends V> m)

Constructs a new tree map containing the same mappings and using the same ordering as the specified sorted map. This method runs in

线性时间。

Parameters:
    m - the sorted map whose mappings are to be placed in this map, and whose comparator is to be used to sort this map
Throws:
    NullPointerException - if the specified map is null

为什么第一个签名是public TreeMap(Map&lt;? extends K, ? extends V&gt; m),第二个是public TreeMap(SortedMap&lt;K,? extends V&gt; m)

更新:如果问题不够清楚,我想知道为什么与构造函数中的 KEYS 参数相关的泛型部分彼此不同。 ? extends KK

【问题讨论】:

    标签: java api generics


    【解决方案1】:

    Map 构造函数使用? extends K,因为让它尽可能松散总是好的。

    SortedMap 构造函数使用K,因为它采用另一个映射的比较器,如果边界是? extends K,则不会进行类型检查。

    让我更详细地解释一下。

    假设我们有 BaseMiddle extends BaseDerived extends Middle implements Foo 类。

    然后我们有两个特殊的比较器,BaseComp implements Comparator&lt;Base&gt;FooComp implements Comparator&lt;Foo&gt;

    现在我们创建一个TreeMap&lt;Derived, String&gt; 并给它一个FooComp

    SortedMap<Derived, String> sm1 = new TreeMap<Derived, String>(new FooComp());
    

    注意我们使用的带有签名TreeMap(Comparator&lt;? super K&gt; comp) 的构造函数。 FooComp 实现了Comparator&lt;Foo&gt;,并且由于FooDerived(我们的K)的超类,所以这个类型检查。

    接下来,我们来看这张地图:

    SortedMap<Derived, String> sm2 = new TreeMap<Derived, String>(new BaseComp());
    

    再次,这很好。 BaseComp 实现 Comparator&lt;Base&gt;Base super Derived 类型检查。

    好的,让我们进一步假设采用排序地图的构造函数具有签名TreeMap(SortedMap&lt;? extends K, ? extends V&gt; map),就像普通地图一样。那么我们现在可以这样说:

    SortedMap<Middle, String> sm3 = new TreeMap<Middle, String>(sm2);
    

    然后构造函数将采用sm2 的比较器,这是一个BaseComp,这很好:Base super Middle 仍然进行类型检查。但是,我们也可以这样做:

    SortedMap<Middle, String> sm4 = new TreeMap<Middle, String>(sm1);
    

    构造函数取sm1的比较器,是FooComp,而Foo super Middle不正确。因此,如果允许这样做,那就不安全了。

    但是,这是不允许的。特别是,假设您更改了构造函数的边界。我将为问号分配数字——不是有效的语法,但有助于理解。所以首先你有SortedMap 接口,它可以访问比较器:

    public interface SortedMap<K1, V1> extends Map<K1, V1> {
        Comparator<?1 super K1> comparator();
    }
    

    然后你就有了 TreeMap 类及其构造函数:

    public class TreeMap<K2, V2> ... {
        private Comparator<?2 super K2> comp;
    
        public TreeMap(SortedMap<?3 extends K2, ?4 extends V2> map) {
            this.comp = map.comparator();
            // magic stuff to transfer elements
        }
    }
    

    好的,在我们的具体例子中,我们尝试做new TreeMap&lt;Middle, String&gt;((SortedMap&lt;Derived, String&gt;)x),所以匹配这些类型:K1 = DerivedK2 = Middle。我将忽略这些值,它们无关紧要。

    构造函数的签名很好。 ?3 被推导出为K1DerivedDerived extends Middle 类型检查。

    构造函数的第一行是有趣的部分。 this.comp 的类型是 Comparator&lt;?2 super Middle&gt;。根据SortedMap的定义map.comparator()的类型是?1 super K1,所以在我们的例子中是?1 super Derived

    所以我们尝试将?1 super Derived 分配给?2 super Middle。为了使其工作,Derived 的每个基本类型也必须是Middle 的基本类型。但是我们已经展示了一个反例:FooDerived 实现的接口,而不是Middle,就是其中之一。如果在MiddleDerived 之间有一个Middle2,那就是另一个。

    所以编译器不能允许这个赋值,因此必须无法编译代码。

    这很有趣!

    【讨论】:

      【解决方案2】:

      两者都是有效的构造函数,但作为惯例,您应该始终使用尽可能高的接口。因此,虽然采用 Map 的构造函数在所有情况下都可以工作,但在从 SortedMap 构建 TreeMap 时,应该使用 SortedMap 构造函数。

      但是为什么在这种情况下呢?因为通过让构造函数知道您传递的不是常规地图,而是已经排序的地图,它可能会利用它并以最佳方式运行。在这种特殊情况下,很容易看出为什么从已排序的地图构建 TreeMap(这是一个排序的地图)会比从无序地图(如 Hasmap)构建它更快:构造函数将具有首先订购地图,但它会跳过 SortedMap 上的这部分,因为它已经订购了。

      【讨论】:

      • 我更关心泛型。为什么在第一个 K 是?扩展 K,第二个是 K
      • 您的编辑不正确。它与强制它实现 Comparable 无关。
      • 确实如此,您的回答很好地解释了这部分。删除了我的修改并为你的修改点赞。
      猜你喜欢
      • 2010-12-28
      • 2015-03-27
      • 1970-01-01
      • 2013-02-03
      • 2011-09-14
      • 2010-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多