I was reading Effective Java by Joshua Bloch.
In Item 17: "Use interfaces only to define types", I came across the explanation where it is not advised to use Interfaces for storing constants. I am putting the explanation below.
"Worse, it represents a commitment: if in a future release the class is modified so that it no longer needs to use the constants, it still must implement the interface to ensure binary compatibility."
What does binary compatibility mean here?
Can someone guide me with an example in Java to show that code is binary compatible.
Binary-code compatibility (binary compatible or object-code-compatible) is a property of computer systems meaning that they can run the same executable code, typically machine code for a general-purpose computer CPU.
Java API Compliance Checker (JAPICC) is a tool for checking backward binary and source-level compatibility of a Java library API. The tool checks classes declarations of old and new versions and analyzes changes that may break compatibility: removed methods, removed class fields, added abstract methods, etc.
Source: Source compatibility concerns translating Java source code into class files including whether or not code still compiles at all. Binary: Binary compatibility is defined in The Java Language Specification as preserving the ability to link without error.
The binary name of a type variable declared by a generic constructor (§8.8. 4) is the binary name of the type declaring the constructor, followed by $, followed by the descriptor of the constructor (JVMS §4.3. 3), followed by $, followed by the simple name of the type variable.
In short, binary compatibility means that when you change your class, you do not need to recompile classes that use it. For example, you removed or renamed a public or protected method from this class
public class Logger implements Constants { public Logger getLogger(String name) { return LogManager.getLogger(name); } }
from your log-1.jar library and released a new version as log-2.jar. When users of your log-1.jar download the new version it will break their apps when they will try to use the missing getLogger(String name) method.
And if you remove Constants interface (Item 17) this will break binary compatibility either, due to the same reason.
But you can remove / rename a private or package private member of this class without breaking the binary compatibility, because external apps cannot (or should not) use it.
To better understand the concept, it is interesting to see that binary compatibility does NOT imply API compatibility, nor vice versa.
API compatible but NOT binary compatible: static removal
Version 1 of library:
public class Lib { public static final int i = 1; }
Client code:
public class Main { public static void main(String[] args) { if ((new Lib()).i != 1) throw null; } }
Compile client code with version 1:
javac Main.java
Replace version 1 with version 2: remove static
:
public class Lib { public final int i = 1; }
Recompile just version 2, not the client code, and run java Main
:
javac Lib.java java Main
We get:
Exception in thread "main" java.lang.IncompatibleClassChangeError: Expected static field Lib.i at Main.main(Main.java:3)
This happens because even though we can write (new Lib()).i
in Java for both static
and member methods, it compiles to two different VM instructions depending on Lib
: getstatic
or getfield
. This break is mentioned at JLS 7 13.4.10:
If a field that is not declared private was not declared static and is changed to be declared static, or vice versa, then a linkage error, specifically an IncompatibleClassChangeError, will result if the field is used by a pre-existing binary which expected a field of the other kind.
We would need to recompile Main
with javac Main.java
for it to work with the new version.
Notes:
(new Lib()).i
is bad style, raises a warning, and should never be donefinal
primitives are useless: always use static final
for primitives: private final static attribute vs private final attribute Binary compatible but NOT API compatible: null pre-condition strengthening
Version 1:
public class Lib { /** o can be null */ public static void method(Object o) { if (o != null) o.hashCode(); } }
Version 2:
public class Lib { /** o cannot be null */ public static void method(Object o) { o.hashCode(); } }
Client:
public class Main { public static void main(String[] args) { Lib.method(null); } }
This time, even if recompile Main
after updating Lib
, the second invocation will throw, but not the first.
This is because we changed the contract of method
in a way that is not checkable at compile time by Java: before it could take null
, after not anymore.
Notes:
null
values is a questionable practiceC binary compatibility example
What is an application binary interface (ABI)?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With