改造void方法以返回其参数以促进流畅性:打破变化? [英] Retrofitting void methods to return its argument to facilitate fluency: breaking change?

查看:168
本文介绍了改造void方法以返回其参数以促进流畅性:打破变化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


API设计就像性:做出一个错误并在余生中支持它 Josh Bloch在推特上

Java库中存在许多设计错误。 Stack extends Vector 讨论),我们无法在不造成破损的情况下解决这个问题。我们可以尝试弃用 Integer.getInteger 讨论),但它可能会永远存在。

There are many design mistakes in the Java library. Stack extends Vector (discussion), and we can't fix that without causing breakage. We can try to deprecate Integer.getInteger (discussion), but it's probably going to stay around forever.

尽管如此,某些类型的改装可以在不造成破损的情况下完成。

Nonetheless, certain kinds of retrofitting can be done without causing breakage.


有效的Java第2版,第18项:首选接口到抽象类:现有的类可以很容易地改装到实现一个新的接口。

Effective Java 2nd Edition, Item 18: Prefer interfaces to abstract classes: Existing classes can be easily retrofitted to implement a new interface".

示例: String实现CharSequence Vector实现List 等。


Effective Java 2nd Edition,Item 42:明智地使用varargs :您可以改进一个现有的方法,该方法将数组作为其最终参数,而不是对现有客户端采用varags。

Effective Java 2nd Edition, Item 42: Use varargs judiciously: You can retrofit an existing method that takes an array as its final parameter to take varags instead with no effect on existing clients.

一个着名的例子是 Arrays.asList ,这引起了混淆(讨论),但不是破损。

An (in)famous example is Arrays.asList, which caused confusions (discussion), but not breakage.

这个问题是关于另一种类型的改装:

This question is about a different kind of retrofitting:

我的初步预感指向是,因为:

My initial hunch points to yes, because:


  • 返回type不影响在编译时选择哪个方法


    • 请参阅: JLS 15.12.2.11 - 不考虑返回类型

    • 因此,改变回报类型不会改变编译器选择的方法

    • void 进行改造以返回合法的内容(但不是其他方式)周围!)

    • Return type doesn't affect which method is chosen at compile time
      • See: JLS 15.12.2.11 - Return Type Not Considered
      • Thus, changing return type doesn't change which method is chosen by the compiler
      • Retrofitting from void to return something is legal (but not the other way around!)

      但是,我想听听由Java / API设计经验丰富的其他人进行更彻底的分析。

      However, I'd like to hear a more thorough analysis by others more experienced in Java/API design.

      如标题所示,一个动机是促进流畅的界面风格编程。

      As suggested in the title, one motivation is to facilitate fluent interface style programming.

      考虑这个打印洗牌名单的简单片段:

      Consider this simple snippet that prints a shuffled list of names:

          List<String> names = Arrays.asList("Eenie", "Meenie", "Miny", "Moe");
          Collections.shuffle(names);
          System.out.println(names);
          // prints e.g. [Miny, Moe, Meenie, Eenie]
      

      Collections.shuffle(List) 已声明返回输入列表,我们可以写:

      Had Collections.shuffle(List) been declared to return the input list, we could have written:

          System.out.println(
              Collections.shuffle(Arrays.asList("Eenie", "Meenie", "Miny", "Moe"))
          );
      

      集合中还有其他方法如果他们要返回输入列表而不是 void ,那么使用起来会更愉快,例如 reverse(List) sort(List) 等。事实上,拥有 Collections.sort Arrays.sort 返回 void 特别不幸,因为它剥夺了我们写这样的富有表现力的代码:

      There are other methods in Collections that would've been much more pleasant to use if they were to return the input list instead of void, e.g. reverse(List), sort(List), etc. In fact, having Collections.sort and Arrays.sort return void is especially unfortunate, because it deprives us from writing expressive code such as this:

      // DOES NOT COMPILE!!!
      //     unless Arrays.sort is retrofitted to return the input array
      
      static boolean isAnagram(String s1, String s2) {
          return Arrays.equals(
              Arrays.sort(s1.toCharArray()),
              Arrays.sort(s2.toCharArray())
          );
      }
      

      void 返回当然,防止流畅性的类型不仅限于这些实用方法。 java.util.BitSet 方法也可以编写为返回(ala StringBuffer StringBuilder )以方便流利。

      This void return type preventing fluency isn't just restricted to these utility methods, of course. The java.util.BitSet methods could've also been written to return this (ala StringBuffer and StringBuilder) to facilitate fluency.

      // we can write this:
          StringBuilder sb = new StringBuilder();
          sb.append("this");
          sb.append("that");
          sb.insert(4, " & ");
          System.out.println(sb); // this & that
      
      // but we also have the option to write this:
          System.out.println(
              new StringBuilder()
                  .append("this")
                  .append("that")
                  .insert(4, " & ")
          ); // this & that
      
      // we can write this:
          BitSet bs1 = new BitSet();
          bs1.set(1);
          bs1.set(3);
          BitSet bs2 = new BitSet();
          bs2.flip(5, 8);
          bs1.or(bs2);
          System.out.println(bs1); // {1, 3, 5, 6, 7}
      
      // but we can't write like this!
      //  System.out.println(
      //      new BitSet().set(1).set(3).or(
      //          new BitSet().flip(5, 8)
      //      )
      //  );
      

      不幸的是,不像 StringBuilder / StringBuffer ALL BitSet 的mutators返回 void

      Unfortunately, unlike StringBuilder/StringBuffer, ALL of BitSet's mutators return void.

      • Method chaining - why is it a good practice, or not?

      推荐答案

      不幸的是,是的,更改 void 返回某物的方法是一个突破性的变化。此更改不会影响源代码兼容性(即,相同的Java源代码仍将像以前一样编译,绝对没有明显的效果)但它会破坏二进制兼容性(即先前针对旧API编译的字节码将不再运行)。

      Unfortunately, yes, changing a void method to return something is a breaking change. This change will not affect source code compatibility (i.e. the same Java source code would still compile just like it did before, with absolutely no discernible effect) but it breaks binary compatibility (i.e. bytecodes that was previously compiled against the old API will no longer run).

      以下是 Java语言规范第3版的相关摘录:

      二进制兼容性与源兼容性不同。

      13.2 What Binary Compatibility Is and Is Not

      Binary compatibility is not the same as source compatibility.

      本节描述更改对类及其成员和构造函数的声明的影响o n预先存在的二进制文件。

      This section describes the effects of changes to the declaration of a class and its members and constructors on pre-existing binaries.

      更改方法的结果类型,用<替换结果类型code> void ,或用结果类型替换 void 具有以下组合效果:

      Changing the result type of a method, replacing a result type with void, or replacing void with a result type has the combined effect of:


      • 删除旧方法

      • 使用新结果类型添加新方法或新 void result。

      • deleting the old method, and
      • adding a new method with the new result type or newly void result.

      从类中删除方法或构造函数可能会破坏与引用此方法或构造函数的任何预先存在的二进制文件的兼容性;当链接来自预先存在的二进制文件的此类引用时,可能会抛出 NoSuchMethodError 。只有在超类中没有声明具有匹配签名和返回类型的方法时才会发生这样的错误。

      Deleting a method or constructor from a class may break compatibility with any pre-existing binary that referenced this method or constructor; a NoSuchMethodError may be thrown when such a reference from a pre-existing binary is linked. Such an error will occur only if no method with a matching signature and return type is declared in a superclass.

      也就是说, Java编译器在方法解析过程中在编译时忽略方法的返回类型,此信息在JVM字节码级别的运行时很重要。

      That is, while the return type of a method is ignored by the Java compiler at compile-time during the method resolution process, this information is significant at run-time at the JVM bytecode level.

      方法的签名不包括返回类型,但其字节码描述符不包括。

      A method's signature does not include the return type, but its bytecode descriptor does.


      8.4.2方法签名



      如果两个方法具有相同的名称和参数类型,则它们具有相同的签名。

      8.4.2 Method Signature

      Two methods have the same signature if they have the same name and argument types.

      最具体方法的描述符(签名加返回类型)是在运行时用于执行方法调度的方法。

      The descriptor (signature plus return type) of the most specific method is one used at run time to perform the method dispatch.

      在编译时选择最适用的方法;它的描述符确定在运行时实际执行的方法。

      The most applicable method is chosen at compile time; its descriptor determines what method is actually executed at run time.

      如果向类中添加了一个新方法,那么使用该类的旧定义编译的源代码可能不会使用new方法,即使重新编译会导致选择此方法。

      If a new method is added to a class, then source code that was compiled with the old definition of the class might not use the new method, even if a recompilation would cause this method to be chosen.

      理想情况下,只要更改了依赖的代码,就应重新编译源代码。但是,在不同组织维护不同类的环境中,这并不总是可行的。

      Ideally, source code should be recompiled whenever code that it depends on is changed. However, in an environment where different classes are maintained by different organizations, this is not always feasible.

      对字节码进行一点检查帮助澄清这一点。当名称shuffling片段运行 javap -c 时,我们会看到如下说明:

      A little inspection of the bytecode will help clarify this. When javap -c is run on the name shuffling snippet, we see instructions like this:

      invokestatic java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
                   \______________/ \____/ \___________________/\______________/
                      type name     method      parameters        return type
      
      invokestatic java/util/Collections.shuffle:(Ljava/util/List;)V
                   \___________________/ \_____/ \________________/|
                         type name        method     parameters    return type
      



      相关问题




      • java:what is这个: [Ljava.lang.Object;

      • 我们现在解决为什么要改装新的界面或vararg,如 Effective Java 2nd Edition 中所述,不会破坏二进制兼容性。

        Let's now address why retrofitting a new interface or a vararg, as explained in Effective Java 2nd Edition, does not break binary compatibility.


        < a href =http://java.sun.com/docs/books/jls/third_edition/html/binaryComp.html#13.4.4\"rel =nofollow noreferrer> 13.4.4超类和超级接口



        更改直接超类或类类型的直接超接口集不会破坏与预先存在的二进制文件的兼容性,前提是超类或超接口的总集合,重新特定地,类类型没有成员。

        13.4.4 Superclasses and Superinterfaces

        Changing the direct superclass or the set of direct superinterfaces of a class type will not break compatibility with pre-existing binaries, provided that the total set of superclasses or superinterfaces, respectively, of the class type loses no members.

        改造新的界面不会导致类型丢失任何成员,因此这不会破坏二进制兼容性。同样,由于varargs是使用数组实现的,因此这种改进也不会破坏二进制兼容性。

        Retrofitting a new interface does not cause the type to lose any member, hence this does not break binary compatibility. Similarly, due to the fact that varargs is implemented using arrays, this kind of retrofitting also does not break binary compatibility.


        8.4.1正式参数



        如果最后一个形式参数是类型为 T 的变量arity参数,则认为它定义了类型<$ c的形式参数$ c> T [] 。

        8.4.1 Formal Parameters

        If the last formal parameter is a variable arity parameter of type T, it is considered to define a formal parameter of type T[].



        相关问题




        • 正式参数类型声明中 double ... double [] 之间的区别

        • Related questions

          • Difference between double… and double[] in formal parameter type declaration
          • 实际上,是的,有一种方法可以在之前的 voi上改进返回值d 方法。我们不能在Java源代码级别有两个具有相同精确签名的方法,但我们 CAN 在JVM级别具有该方法,前提是它们具有不同的描述符(由于具有不同的返回类型) 。

            Actually, yes, there is a way to retrofit a return value on previously void methods. We can't have two methods with the same exact signatures at the Java source code level, but we CAN have that at the JVM level, provided that they have different descriptors (due to having different return types).

            因此我们可以为例如提供二进制文件 java.util.BitSet ,其方法包含 void 和非 void 同时返回类型。我们只需要发布非 void 版本作为新API。事实上,这是我们可以在API上发布的唯一内容,因为在Java中使用具有完全相同签名的两种方法是非法的。

            Thus we can provide a binary for e.g. java.util.BitSet that has methods with both the void and non-void return types simultaneously. We only need to publish the non-void version as the new API. In fact, that's the only thing we can publish at the API, since having two methods with the exact same signature is illegal in Java.

            这个解决方案是一个糟糕的黑客,需要特殊(和规范)处理才能将 BitSet.java 编译为 BitSet.class ,因此它可能不会值得努力这样做。

            This solution is a terrible hack, requiring special (and spec-defying) processing to compile BitSet.java to BitSet.class, and thus it may not be worth the effort to do so.

            这篇关于改造void方法以返回其参数以促进流畅性:打破变化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆