BinaryCompatibleDefaultMethods
From APIDesign
(New page: DefaultMethods are useful when one desperately needs to add a method into an existing '''interface'''. However, they decrease clarity of a ProviderAPI. As such, don't overuse. ...) |
|||
Line 1: | Line 1: | ||
- | [[DefaultMethods]] are useful when one desperately needs to add a method into an existing '''interface'''. However, they decrease [[clarity]] of a [[ProviderAPI]]. As such, don't overuse. Morever it has been recently demonstrated that adding [[DefaultMethods]] can even compromise [[BinaryCompatibility]]. | + | [[DefaultMethods]] are useful when one desperately needs to add a method into an existing '''interface'''. However, they decrease [[clarity]] of a [[ProviderAPI]] (no, you [[DefaultMethods#Can_you_disagree.3F|can't disagree!]]). As such, don't overuse. Morever it has been recently demonstrated that adding [[DefaultMethods]] can even compromise [[BinaryCompatibility]]. Recently [https://twitter.com/emilianbold/status/1308677540408709125?s=20 Emilian Bold asked me] to participate in a tweeting about [[BinaryCompatible|binary incompatibility]] caused by adding '''CharSequence.isEmpty''' in JDK15. An interesting case. Following code compiles on JDK8 to JDK14: |
- | + | ||
- | + | ||
- | Recently [https://twitter.com/emilianbold/status/1308677540408709125?s=20 Emilian Bold asked me] to participate in a tweeting about [[BinaryCompatible|binary incompatibility]] caused by adding '''CharSequence.isEmpty''' in JDK15. An interesting case. Following code compiles on JDK8 to JDK14: | + | |
<source lang="java"> | <source lang="java"> | ||
- | + | interface ArrayLike { | |
- | + | int length(); | |
- | + | default boolean isEmpty() { | |
- | + | return length() == 0; | |
- | + | } | |
} | } | ||
- | class CharArrayLike implements CharSequence, ArrayLike { | + | final class CharArrayLike implements CharSequence, ArrayLike { |
- | + | private final char[] chars; | |
+ | |||
+ | CharArrayLike(char[] chars) { | ||
+ | this.chars = chars; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public int length() { | ||
+ | return chars.length; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public char charAt(int index) { | ||
+ | return chars[index]; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public CharSequence subSequence(int start, int end) { | ||
+ | return new String(chars, start, end); | ||
+ | } | ||
+ | |||
- | |||
} | } | ||
</source> | </source> | ||
+ | |||
+ | While the code compiles find with '''JDK14''' and older, it no longer compiles on '''JDK15'''. It results in: | ||
+ | |||
+ | <source lang="bash"> | ||
+ | $ /jdk-14/bin/javac ArrayLike.java | ||
+ | $ /jdk-15/bin/javac ArrayLike.java | ||
+ | ArrayLike.java:9: error: types CharSequence and ArrayLike are incompatible; | ||
+ | final class CharArrayLike implements CharSequence, ArrayLike { | ||
+ | ^ | ||
+ | class CharArrayLike inherits unrelated defaults for isEmpty() from types CharSequence and ArrayLike | ||
+ | 1 error | ||
+ | </source> | ||
+ | |||
+ | Why? Since '''JDK15''' there is '''CharSequence.isEmpty()''' [[DefaultMethods|default method]]. As such, when '''javac''' processes the '''CharArrayLike''' class it doesn't know whether to select the '''ArrayLike.isEmpty()''' or the newly added method. |
Revision as of 06:20, 28 September 2020
DefaultMethods are useful when one desperately needs to add a method into an existing interface. However, they decrease clarity of a ProviderAPI (no, you can't disagree!). As such, don't overuse. Morever it has been recently demonstrated that adding DefaultMethods can even compromise BinaryCompatibility. Recently Emilian Bold asked me to participate in a tweeting about binary incompatibility caused by adding CharSequence.isEmpty in JDK15. An interesting case. Following code compiles on JDK8 to JDK14:
interface ArrayLike { int length(); default boolean isEmpty() { return length() == 0; } } final class CharArrayLike implements CharSequence, ArrayLike { private final char[] chars; CharArrayLike(char[] chars) { this.chars = chars; } @Override public int length() { return chars.length; } @Override public char charAt(int index) { return chars[index]; } @Override public CharSequence subSequence(int start, int end) { return new String(chars, start, end); } }
While the code compiles find with JDK14 and older, it no longer compiles on JDK15. It results in:
$ /jdk-14/bin/javac ArrayLike.java $ /jdk-15/bin/javac ArrayLike.java ArrayLike.java:9: error: types CharSequence and ArrayLike are incompatible; final class CharArrayLike implements CharSequence, ArrayLike { ^ class CharArrayLike inherits unrelated defaults for isEmpty() from types CharSequence and ArrayLike 1 error
Why? Since JDK15 there is CharSequence.isEmpty() default method. As such, when javac processes the CharArrayLike class it doesn't know whether to select the ArrayLike.isEmpty() or the newly added method.