简体   繁体   English

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

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

I'm developing an open source library in Java and would like to ensure that it is convenient for Java 8 users, and takes advantage of new concepts in Java 8 wherever possible (lambdas etc.) 我正在用Java开发一个开源库,并希望确保它对Java 8用户来说很方便,并尽可能利用Java 8中的新概念(lambdas等)

At the same time I absolutely need to maintain backwards compatibility (the library must still be usable for people using Java 6 or 7). 同时我绝对需要保持向后兼容性(库必须仍然可以用于使用Java 6或7的人)。

What useful features from Java 8 can I adopt that would be beneficial for library users without breaking library compatibility for users of older Java versions? 我可以采用Java 8的哪些有用功能,这对于库用户而言是有益的,而不会破坏旧Java版本用户的库兼容性?

I don't know about your library, this advice might be slightly off . 我不知道你的图书馆,这个建议可能略有偏差

Lambdas : Don't worry. 兰巴斯 :别担心。 Any functional interface can be implemented using a Lambda expression. 可以使用Lambda表达式实现任何功能接口。

Method references : Same as lambdas, they should just be usable. 方法参考 :与lambdas相同,它们应该是可用的。

Streams : If this fits your library, you should use them, but keeping compatibility is harder here. Streams :如果这适合您的库,您应该使用它们,但保持兼容性在这里更难。 The backwards compatibility could be achieved using a second library part, wrapping around the base library and hooking into the public API of it. 向后兼容性可以使用第二个库部件来实现,包围基础库并挂接到它的公共API。 It could therefore provide additional sugar/functionality without abandoning Java 6/7. 因此,它可以在不放弃Java 6/7的情况下提供额外的糖/功能。

Default methods : By all means, use these! 默认方法 :无论如何,请使用这些! They are a quick/cheap/good way to enhance existing implementations without breaking them. 它们是一种快速/廉价/好的方法来增强现有的实现而不会破坏它们。 All default methods you add will be automatically available for implementing classes. 您添加的所有默认方法都将自动用于实现类。 These will, however, also need the second library part, so you should provide the base interfaces in your base library, and extend the interfaces from the companion-library. 但是,这些也需要第二个库部分,因此您应该在基础库中提供基本接口,并从companion-library扩展接口。

Don't fork the library, abandoning the old one, as there are still many developers who cannot use Java 8, or even Java 7. If your library is sensible to use on eg Android, please keep that compatibility. 不要分叉库,放弃旧库,因为仍有许多开发人员无法使用Java 8,甚至Java 7.如果您的库在Android上使用是明智的,请保持兼容性。

If you want your code to be usable by Java 6 consuming VMs, you have to write using Java 6 language compatibility. 如果您希望代码可供Java 6消耗VM使用,则必须使用Java 6语言兼容性进行编写。 Alas. 唉。 The bytecode format critically changed from 6 to 7, so code for 7 and 8 won't load into 6. (I found this with my own code migrating to 7; even when all I was using was multi- catch — which should be writable in the 6 bytecode — I couldn't build it for a 6 target. The compiler refused.) 字节码格式从6严重变为7,因此7和8的代码不会加载到6中。(我发现这个用我自己的代码迁移到7;即使我所使用的都是多catch - 它应该是可写的在6字节码中 - 我无法为6个目标构建它。编译器拒绝了。)

I've yet to experiment with 8, but you'll have to be careful because if you depend on any new Java packages or classes, you will still be unable to work with older versions; 我还没有尝试8,但你必须要小心,因为如果你依赖任何新的Java包或类,你仍然无法使用旧版本; the old consuming VMs simply won't have access to the relevant classes and won't be able to load the code. 旧的消费虚拟机根本无法访问相关的类,也无法加载代码。 In particular, lambdas definitely won't work . 特别是, lambdas肯定不会起作用

Well, can we target a different classfile version? 那么,我们可以针对不同的类文件版本吗? There's no combination of source and target that will actually make javac happy with this. 没有源和目标的组合实际上会让javac对此感到满意。

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

So there's simply no way to compile Java source with lambdas into a pre-Java 8 classfile version. 因此,根本无法将带有lambdas的Java源代码编译为Java 8之前的类文件版本。

My general take is that if you want to support old versions of Java, you have to use old versions of Java to do so. 我的一般看法是,如果要支持旧版本的Java,则必须使用旧版本的Java来实现。 You can use a Java 8 toolchain, but you need Java 7 or Java 6 source. 可以使用Java 8工具链,但需要Java 7或Java 6源代码。 You can do it by forking the code, maintaining versions for all the versions of Java you want to support, but that's far more work than you could ever justify as a lone developer. 可以通过分配代码来维护代码,维护你想要支持的所有Java版本的版本,但这比你作为一个单独的开发人员所证明的要多得多。 Pick the minimum version and go with that (and kiss those juicy new features goodbye for now, alas). 选择最低版本并继续使用(并亲吻那些多汁的新功能再见,唉)。

If you use any new language features in Java 8, it requires also using Java 8 bytecode. 如果在Java 8中使用任何新的语言功能,则还需要使用Java 8字节码。

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

That means your options are quite limited. 这意味着您的选择非常有限。 You cannot use lambdas, method references, default methods, Streams, etc. and maintain backwards compatibility. 您不能使用lambdas,方法引用,默认方法,Streams等,并保持向后兼容性。

There are still two things you can do that users of Java 8 will benefit from. Java 8的用户仍然可以从中受益。 The first is using Functional Interfaces in your public API. 第一种是在公共API中使用功能接口。 If your methods take Runnables, Callables, Comparators, etc. as parameters, then users of Java 8 will be able to pass in lambdas. 如果您的方法将Runnables,Callables,Comparators等作为参数,那么Java 8的用户将能够传入lambdas。 You may want to create your own Single-Abstract-Method interfaces as well. 您可能还想创建自己的Single-Abstract-Method接口。 If you find you need Functions and Predicates, I'd suggest reusing the ones that ship with GS Collections or Guava instead of writing your own. 如果您发现需要函数和谓词,我建议重用GS Collections或Guava附带的那些,而不是自己编写。

The second thing you can do is use a rich collections API that benefits from using Functional Interfaces. 您可以做的第二件事是使用丰富的集合API,这可以从使用功能接口中受益。 Again, that means using GS Collections or Guava. 同样,这意味着使用GS Collections或Guava。 For example, if you have a method that would return List, return MutableList or ImmutableList instead. 例如,如果您有一个返回List的方法,则返回MutableListImmutableList That way, callers of the method will be able to chain usages of the rich API exposed by these interfaces. 这样,该方法的调用者将能够链接由这些接口公开的丰富API的用法。

As said by others, providing and using interfaces with a single method such that they can be implemented using lambdas or method references when using Java 8 is a good way of supporting Java 8 without breaking Java 7 compatibility. 正如其他人所说的那样,使用单一方法提供和使用接口,以便在使用Java 8时可以使用lambdas或方法引用来实现它们,这是在不破坏Java 7兼容性的情况下支持Java 8的好方法。


This can be complemented by providing methods by your library which fit into one of the standard function types of Java 8 (eg Supplier , (Bi)Consumer , (Bi)Function ) so that Java 8 developers can create method references to them for Java 8 API methods. 这可以通过提供您的库方法 ,其适应标准功能类型的Java 8中的一种补充(如Supplier(Bi)Consumer(Bi)Function ),这样的Java 8开发人员可以创建他们对Java 8的方法引用API方法。 This implies that their signature matches one of these functional interfaces and they don't throw checked exceptions. 这意味着它们的签名与这些功能接口之一匹配,并且它们不会抛出已检查的异常。 This often comes naturally, eg getFoo() may act as a Function and isBar() as a Predicate but sometimes it's possible to improve methods by thinking about possible Java 8 use scenarios. 这通常是自然而然的,例如, getFoo()可以充当FunctionisBar()充当Predicate但有时可以通过考虑可能的Java 8使用场景来改进方法。

For example, if you provide a method taking two parameters, it's useful to choose the order where the first parameter is the one that is more likely to be a key in a Map . 例如,如果您提供了一个采用两个参数的方法,则选择第一个参数更可能是Map的键的顺序非常有用。 So it is more likely to be useful for Map.forEach with a method reference. 因此,使用方法引用更有可能对Map.forEach有用。

And avoid method with ambiguous signatures. 并避免带有模糊签名的方法。 Eg if you have a class Foo with an instance method ReturnType bar() and a static method ReturnType bar(Foo) neither of them can be used as method reference anymore as Foo::bar would be ambiguous. 例如,如果你有一个带有实例方法的类Foo ReturnType bar()和一个static方法ReturnType bar(Foo)它们都不ReturnType bar(Foo)用作方法引用,因为Foo::bar是不明确的。 Eliminate or rename one of these methods. 消除或重命名这些方法之一。

It's important that such methods do not have undocumented internal state that causes surprising behavior when used by multiple threads. 重要的是,此类方法没有未记录的内部状态,当由多个线程使用时会导致令人惊讶的行为。 Otherwise they can not be used by parallel streams. 否则它们不能被并行流使用。


Another opportunity that should not be underestimated is to use names for classes, interfaces and members conforming to patterns introduced by the Java 8 API. 另一个不容低估的机会是使用符合Java 8 API引入的模式的类,接口和成员的名称 Eg if you have to introduce some sort of filter interface with a test method for your library that ought to work with Java 7 as well, you should name the interface Predicate and the method test to associate it with the similar named functional interface of Java 8. 例如,如果您必须为您的库引入某种类型的过滤器接口以及应该与Java 7一起使用的库,则应该将接口命名为Predicate ,并将方法test与Java 8的类似命名功能接口相关联。 。

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

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