我正在用Java开发一个开源库,并希望确保它对Java 8用户来说很方便,并尽可能利用Java 8中的新概念(lambdas等)
同时我绝对需要保持向后兼容性(该库仍然可以用于使用Java 6或7的人。)
我可以采用哪些有用的Java 8特性,这对于库用户来说是有益的,而不会破坏旧Java版本用户的库兼容性?
答案 0 :(得分:2)
我不了解你的图书馆,这个建议可能稍微偏离。
Lambdas :别担心。可以使用Lambda表达式实现任何功能接口。
方法引用:与lambdas相同,它们应该可用。
Streams :如果这适合您的库,您应该使用它们,但保持兼容性在这里更难。向后兼容性可以使用第二个库部件来实现,包围基础库并挂接到它的公共API。因此,它可以在不放弃Java 6/7的情况下提供额外的糖/功能。
默认方法:无论如何,请使用这些!它们是一种快速/廉价/好的方法来增强现有的实现而不会破坏它们。您添加的所有默认方法都将自动用于实现类。但是,这些也需要第二个库部分,因此您应该在基础库中提供基本接口,并从协同库扩展接口。
不要分叉库,放弃旧库,因为仍有许多开发人员无法使用Java 8,甚至Java 7。如果您的库可以使用,例如Android,请保持兼容性。
答案 1 :(得分:2)
如果您希望Java 6使用虚拟机可以使用您的代码,那么 可以使用Java 6语言兼容性进行编写。唉。字节码格式从6变为7,因此7和8的代码不会加载到6中。(我发现这个用我自己的代码迁移到7;即使我使用的只是多个 - 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
因此,根本无法将带有lambdas的Java源代码编译为Java 8之前的类文件版本。
我的一般看法是,如果要支持旧版本的Java,则必须使用旧版本的Java来执行此操作。您可以使用Java 8工具链,但您需要Java 7或Java 6源代码。您可以通过分叉代码来执行此操作,维护您想要支持的所有Java版本的版本,但这比您作为单独的开发人员所证明的要多得多。选择最低版本并继续使用(并暂时亲吻那些多汁的新功能,唉)。
答案 2 :(得分:2)
如果在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,Comparators等作为参数,那么Java 8的用户将能够传入lambdas。您可能还想创建自己的Single-Abstract-Method接口。如果您发现需要函数和谓词,我建议重复使用GS Collections或Guava附带的函数,而不是编写自己的函数。
您可以做的第二件事是使用丰富的集合API,这可以从使用功能接口中受益。同样,这意味着使用GS Collections或Guava。例如,如果您有一个返回List的方法,请返回MutableList或ImmutableList。这样,该方法的调用者将能够链接由这些接口公开的丰富API的用法。
答案 3 :(得分:0)
正如其他人所说,使用单一方法提供和使用接口,以便在使用Java 8时可以使用lambdas或方法引用实现它们,这是支持Java 8而不破坏Java 7兼容性的好方法。
这可以通过提供适合Java 8的标准函数类型之一的方法来补充(例如Supplier
,(Bi)Consumer
,{{1} })以便Java 8开发人员可以为Java 8 API方法创建对它们的方法引用。这意味着它们的签名与这些功能接口之一匹配,并且它们不会抛出已检查的异常。这通常是自然而然的,例如(Bi)Function
可以充当getFoo()
,Function
充当isBar()
,但有时可以通过考虑可能的Java 8使用方案来改进方法。
例如,如果您提供了一个采用两个参数的方法,那么选择第一个参数更可能是Predicate
中的键的顺序是有用的。因此,Map
更有可能对方法参考有用。
避免带有模糊签名的方法。例如。如果您的班级Map.forEach
包含实例方法Foo
和ReturnType bar()
方法static
,那么它们都不能用作方法参考,因为ReturnType bar(Foo)
将是暧昧。消除或重命名这些方法之一。
重要的是,此类方法没有未记录的内部状态,当由多个线程使用时会导致令人惊讶的行为。否则它们不能被并行流使用。
另一个不容低估的机会是将 names 用于符合Java 8 API引入的模式的类,接口和成员。例如。如果你必须为你的库引入某种类型的过滤器接口以及应该与Java 7一起使用的库,你应该命名接口Foo::bar
和方法Predicate
来将它与类似命名的Java 8功能接口。