【问题标题】:Enhance library for Java 8 while keeping backwards compatibility增强 Java 8 库,同时保持向后兼容性
【发布时间】:2014-06-04 14:34:56
【问题描述】:

我正在使用 Java 开发一个开源库,并希望确保它对 Java 8 用户很方便,并尽可能利用 Java 8 中的新概念(lambdas 等)

同时我绝对需要保持向后兼容性(该库必须仍然可供使用 Java 6 或 7 的人使用)。

我可以从 Java 8 中采用哪些有用的特性来对库用户有益,而不会破坏旧 Java 版本用户的库兼容性?

【问题讨论】:

  • 也许你可以制作一个 Java8 的 fork 并让需要的人使用旧版本。

标签: java java-8 backwards-compatibility


【解决方案1】:

我不了解你的图书馆,这个建议可能有点不妥。

Lambdas:别担心。任何函数式接口都可以使用 Lambda 表达式来实现。

方法引用:与 lambdas 相同,它们应该只是可用的。

Streams:如果这适合您的库,您应该使用它们,但在这里保持兼容性更难。向后兼容可以使用第二个库部分来实现,围绕基础库并挂钩到它的公共 API。因此,它可以在不放弃 Java 6/7 的情况下提供额外的糖/功能。

默认方法:一定要使用这些!它们是在不破坏现有实现的情况下增强现有实现的快速/便宜/好方法。您添加的所有默认方法将自动可用于实现类。然而,这些也需要第二个库部分,因此您应该在基础库中提供基础接口,并从配套库中扩展接口。

不要分叉库,放弃旧库,因为仍然有许多开发人员无法使用 Java 8,甚至 Java 7。 Android,请保持兼容性。

【讨论】:

    【解决方案2】:

    如果您希望您的代码可供使用 Java 6 的 VM 使用,您必须使用 Java 6 语言兼容性来编写代码。唉。字节码格式从 6 严重更改为 7,因此 7 和 8 的代码不会加载到 6 中。(我发现我自己的代码迁移到 7;即使我使用的只是 multi-catch — 这应该在 6 字节码中可写——我无法为 6 目标构建它。编译器拒绝了。)

    我还没有尝试过 8,但您必须小心,因为如果您依赖任何新的 Java 包或类,您将仍然无法使用旧版本;旧的消费虚拟机根本无法访问相关类,也无法加载代码。特别是lambdas definitely won't work。

    好吧,我们可以针对不同的类文件版本吗?源和目标的组合实际上不会让 javac 对此感到满意。

    kevin$ $JAVA8/bin/javac -source 1.8 -target 1.7 *.java
    javac: source release 1.8 requires target release 1.8
    

    所以根本没有办法将带有 lambda 的 Java 源代码编译成 Java 8 之前的类文件版本。

    我的一般看法是,如果您想支持旧版本的 Java,则必须使用旧版本的 Java 才能做到这一点。您可以使用 Java 8 工具链,但您需要 Java 7 或 Java 6 源代码。您可以通过分叉代码、维护您想要支持的所有 Java 版本的版本来做到这一点,但这比您作为一个单独的开发人员所能证明的要多得多。选择最低版本并继续使用(唉,暂时告别那些有趣的新功能)。

    【讨论】:

    • 为了记录,我目前使用 Java 7 作为我的主要目标平台。我的代码非常需要 Java 7 的特性(改进的文件系统处理),而且我根本不针对智能手机。
    • +1 表示支持旧 Java 版本需要使用该版本进行构建。可以尝试$JAVA8/bin/javac -source 1.7 -target 1.7,但即使这样也可能让 Java 8 库的依赖在不经意间蔓延。这样做会产生类似warning: [options] bootstrap class path not set in conjunction with -source 1.7 的消息,它基本上说您必须将引导类路径设置为指向Java 7。此时您不妨使用Java 7 工具链。
    【解决方案3】:

    如果您使用 Java 8 中的任何新语言功能,则还需要使用 Java 8 字节码。

    $ javac -source 1.8 -target 1.7
    javac: source release 1.8 requires target release 1.8
    

    这意味着您的选择非常有限。您不能使用 lambdas、方法引用、默认方法、Streams 等并保持向后兼容性。

    您仍然可以做两件事,Java 8 的用户将从中受益。第一个是在公共 API 中使用功能接口。如果您的方法将 Runnables、Callables、Comparator 等作为参数,那么 Java 8 的用户将能够传入 lambda。您可能还想创建自己的 Single-Abstract-Method 接口。如果您发现需要函数和谓词,我建议您重用 GS Collections 或 Guava 附带的函数和谓词,而不是自己编写。

    您可以做的第二件事是使用丰富的集合 API,该 API 受益于使用功能接口。同样,这意味着使用 GS Collections 或 Guava。例如,如果您有一个返回 List 的方法,则改为返回 MutableList 或 ImmutableList。这样,该方法的调用者将能够链接这些接口公开的丰富 API 的使用。

    【讨论】:

      【解决方案4】:

      正如其他人所说,提供和使用具有单一方法的接口,以便在使用 Java 8 时可以使用 lambda 或方法引用来实现它们是在不破坏 Java 7 兼容性的情况下支持 Java 8 的好方法。


      这可以通过您的库提供适合 Java 8 标准函数类型之一(例如Supplier、(Bi)Consumer、(Bi)Function)的方法来补充,以便 Java 8开发人员可以为 Java 8 API 方法创建对它们的方法引用。这意味着它们的签名与这些功能接口之一匹配,并且它们不会抛出检查异常。这通常是自然而然的,例如getFoo() 可以充当 Function 和 isBar() 充当 Predicate,但有时可以通过考虑可能的 Java 8 使用场景来改进方法。

      例如,如果您提供一个采用两个参数的方法,那么选择第一个参数是更有可能成为Map 中的键的顺序会很有用。所以它更有可能对 Map.forEach 有一个方法引用有用。

      并避免使用模糊签名的方法。例如。如果你有一个类Foo 和一个实例方法ReturnType bar() 和一个static 方法ReturnType bar(Foo) 它们都不能再用作方法引用,因为Foo::bar 会模棱两可。消除或重命名这些方法之一。

      重要的是,此类方法没有未记录的内部状态,当被多个线程使用时会导致令人惊讶的行为。否则它们不能被并行流使用。


      另一个不容小觑的机会是使用名称来表示符合 Java 8 API 引入的模式的类、接口和成员。例如。如果您必须为您的库引入某种过滤器接口以及应该与 Java 7 一起使用的测试方法,您应该将接口命名为 Predicate 和方法 test 以将其与类似命名的功能接口相关联Java 8。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-17
        • 2019-10-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多