繁体   English   中英

增强Java 8库,同时保持向后兼容性

[英]Enhance library for Java 8 while keeping backwards compatibility

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

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

我可以采用Java 8的哪些有用功能,这对于库用户而言是有益的,而不会破坏旧Java版本用户的库兼容性?

我不知道你的图书馆,这个建议可能略有偏差

兰巴斯 :别担心。 可以使用Lambda表达式实现任何功能接口。

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

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

默认方法 :无论如何,请使用这些! 它们是一种快速/廉价/好的方法来增强现有的实现而不会破坏它们。 您添加的所有默认方法都将自动用于实现类。 但是,这些也需要第二个库部分,因此您应该在基础库中提供基本接口,并从companion-library扩展接口。

不要分叉库,放弃旧库,因为仍有许多开发人员无法使用Java 8,甚至Java 7.如果您的库在Android上使用是明智的,请保持兼容性。

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

我还没有尝试8,但你必须要小心,因为如果你依赖任何新的Java包或类,你仍然无法使用旧版本; 旧的消费虚拟机根本无法访问相关的类,也无法加载代码。 特别是, lambdas肯定不会起作用

那么,我们可以针对不同的类文件版本吗? 没有源和目标的组合实际上会让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版本的版本,但这比你作为一个单独的开发人员所证明的要多得多。 选择最低版本并继续使用(并亲吻那些多汁的新功能再见,唉)。

如果在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的方法,则返回MutableListImmutableList 这样,该方法的调用者将能够链接由这些接口公开的丰富API的用法。

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


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

例如,如果您提供了一个采用两个参数的方法,则选择第一个参数更可能是Map的键的顺序非常有用。 因此,使用方法引用更有可能对Map.forEach有用。

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

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


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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM