【问题标题】:Use overloading judiciously明智地使用重载
【发布时间】:2015-07-29 22:42:57
【问题描述】:

除了标准的构造函数之外,TreeSet 的构造函数还包括一个允许您提供 Comparator 的构造函数和一个允许您从另一个 SortedSet 创建的构造函数:

TreeSet(Comparator<? super E> c)
// construct an empty set which will be sorted using the
// specified comparator 
TreeSet(SortedSet<E> s)
// construct a new set containing the elements of the 
// supplied set, sorted according to the same ordering

其中的第二个在其声明中与标准的“转换构造函数”太接近了:

TreeSet(Collection<? extends E> c)

正如 Joshua Bloch 在 Effective Java(方法一章中的“明智地使用重载”项)中解释的那样,调用两个采用相关类型参数的构造函数或方法重载之一可能会产生令人困惑的结果。这是因为,在 Java 中,对重载的构造函数和方法的调用是在编译时根据参数的静态类型解析的,因此对参数应用强制转换会对调用的结果产生很大的影响,因为以下代码显示:

// construct and populate a NavigableSet whose iterator returns its
// elements in the reverse of natural order:
NavigableSet<String> base = new TreeSet<String>(Collections.reverseOrder());
Collections.addAll(base, "b", "a", "c");

// call the two different constructors for TreeSet, supplying the
// set just constructed, but with different static types: 
NavigableSet<String> sortedSet1 = new TreeSet<String>((Set<String>)base);
NavigableSet<String> sortedSet2 = new TreeSet<String>(base);
// and the two sets have different iteration orders: 
List<String> forward = new ArrayList<String>(); 
forward.addAll(sortedSet1);
List<String> backward = new ArrayList<String>(); 
backward.addAll(sortedSet2);
assert !forward.equals(backward); 
Collections.reverse(forward); 
assert forward.equals(backward);

这个问题困扰着框架中所有排序集合(TreeSet、TreeMap、ConcurrentSkipListSet 和 ConcurrentSkipListMap)的构造函数。为避免在您自己的类设计中出现这种情况,请为不同的重载选择参数类型,以便适合某个重载的类型的参数不能转换为适合不同重载的类型。如果这不可能,则应将两个重载设计为具有相同参数的行为相同,而不管其静态类型如何。例如,从集合构造的 PriorityQueue 使用原始的排序,无论为构造函数提供的静态类型是包含比较器的类型 PriorityQueue 或 SortedSet 之一,还是只是一个普通的 Collection。为了实现这一点,转换构造函数使用提供的集合的 Comparator,只有在没有自然排序时才回退到自然排序。

目前在看《Java Generics and Collections》这本书,上面是我看不懂的[page185~186]。

首先,我不太明白它为什么使用这个例子,以及它想要说明什么。

其次,我不太了解“转换构造函数”的概念。是不是因为有转换构造函数的存在,才应该慎重使用重载呢?

【问题讨论】:

标签: java data-structures collections constructor overloading


【解决方案1】:

问题在于这两个构造函数的行为略有不同,因此违反了所谓的“principle of least astonishment”。

TreeSet(SortedSet&lt;E&gt;)“使用与指定排序集相同的顺序”构造一个新集,而TreeSet(Collection&lt;? extends E&gt;) 使用“其元素的自然顺序”。这意味着使用相同底层实例构造的两个 TreeSet 的行为可能会有所不同,具体取决于它们构造时使用的引用的静态类型。

SortedSet<Integer> original = getReverseSet(); // { 5, 4, 3, 2, 1}
Collection<Integer> alsoOriginal = original; // same instance exactly

TreeSet<Integer> a = new TreeSet<>(original);
TreeSet<Integer> b = new TreeSet<>(alsoOriginal);

乍一看ab 应该是相同的——毕竟,它们是使用相同的实例构造的!但是第一个使用TreeSet(SortedSet) 构造函数(因此保留了反向排序),而第二个使用TreeSet(Collection) 构造函数(因此使用元素的自然排序,这与反向排序不同)。此外,a.comparator() 将返回反向比较器,而b.comparator() 将返回 null。

这本身并没有错误,但它可能会让图书馆的用户感到惊讶和困惑!

【讨论】:

  • 这里的相关原理是LSP。构造函数中的语言问题在 Java 中没有名称。这也是其他语言中 cuit 运算符重载的问题。
【解决方案2】:

提供的代码 sn-p 编译得很好。确保您已从 java.util 包中导入 TreeSet(单个导入 import java.util.*; 应该足以编译此代码)。

关于您的问题:

  1. 此示例说明,如果您有两个带有参数的重载方法可以在单个类层次结构中,有时很难判断将调用哪个方法。在给定的示例中,base 同时是 SortedSetCollection。当您调用TreeSet 的构造函数并将base 作为参数传递时,调用哪个构造函数取决于base 的编译时类型。因此,当您“按原样”传递它时,它的类型将为TreeSet,并且将选择具有最具体类型的构造函数(采用SortedSet 的构造函数)。当您将base 显式转换为Set 时,它的编译时类型会发生变化,因此调用的构造函数将是采用Collection 的构造函数。

  2. 在这种情况下,“转换构造函数”只是获取一个集合的元素并创建另一个集合的构造函数。 “明智地使用重载”规则可以应用于任何方法,它不必是构造函数。

例如,看看List&lt;Integer&gt;remove(int|Integer) 方法,并尝试找出在哪种情况下会调用哪个方法。

【讨论】:

    【解决方案3】:

    首先,我不太明白它为什么使用这个例子,以及它想要说明什么。

    他说明了这样一个事实,即继承允许编译器做出他认为开发人员应该明确做出的决定。

    我也读过这本书。前半部分对理解 Java 令人困惑的泛型很有帮助。

    编译器正在挑选

    TreeSet(Collection<? extends E> c)
    

    或者...

    TreeSet(SortedSet<E> s)
    

    只需简单的演员表。但是我们传递了相同的 base 变量。他说这太近了,不舒服。您不应创建参数位于同一类型层次结构中的转换构造函数。 IE。 SortedSet 是一个集合

    第二,我不太明白“转换构造函数”的概念。

    Effective Java,第 61 页将“对话构造函数”定义为“复制构造函数”。他提到...

    接口映射的复制构造函数和...更恰当地称为转换构造函数...

    同样来自https://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html

    这个构造函数,称为转换构造函数,初始化新集合以包含指定集合中的所有元素,无论给定集合的子接口或实现类型如何。换句话说,它允许你转换集合的类型。

    【讨论】:

      猜你喜欢
      • 2014-03-05
      • 2013-10-31
      • 1970-01-01
      • 1970-01-01
      • 2011-04-10
      • 2018-07-07
      • 1970-01-01
      • 1970-01-01
      • 2021-03-15
      相关资源
      最近更新 更多