> For the complete documentation index, see [llms.txt](https://hanfak.gitbook.io/workspace/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hanfak.gitbook.io/workspace/languages/java/effective-java/best-practice-api.md).

# best-practice-api

\#Best Practices for api

### Free up resources in reverse order

* **Whenever you implement logic using before/after, allocate/free, take/return semantics, think about whether the after/free/return operation should perform stuff in the inverse order.**
* similar to destructors in c++
* For example
  * When using @Before and @After JUnit annotations
  * When allocating, freeing JDBC resources
  * When calling super methods
* <https://stackoverflow.com/questions/8274098/possibility-of-starvation-of-dining-philosophers>

### Don’t trust your early SPI evolution judgement

* Service provider interface (SPI) is an API intended to be implemented or extended by a third party
  * Providing an SPI to your consumers is an easy way to allow them to inject custom behaviour into your library / code
* Creating an SPI, may trick you into thinking that you’re (not) going to need that additional parameter.
* once you’ve published your SPI and once you’ve decided following semantic versioning, you’ll really regret having added a silly, one-argument method to your SPI when you realise that you might need another argument in some cases
  * YAGNI
* API evolution will prevent you from adding that parameter easily
* better than polluting your SPI with dozens of methods, use a context object (or argument object)
  * Can define a type, but can keep adding data to the context object without breaking the interface
* **Whenever you specify an SPI, consider using context / parameter objects instead of writing methods with a fixed amount of parameters.**
* **good idea to also communicate results through a dedicated MessageResult type, which can be constructed through a builder API.**

### Avoid returning anonymous, local, or inner classes

* They keep a reference to the outer instance. And they will drag that outer instance to wherevery they go, e.g. to some scope outside of your local class if you’re not careful.
  * This can be a major source for memory leaks, as your whole object graph will suddenly entangle in subtle ways.
* **Whenever you write an anonymous, local or inner class, check if you can make it static or even a regular top-level class. Avoid returning anonymous, local or inner class instances from methods to the outside scope.**
* Avoid double-curly braces for simple object instantiation
  * ie for hashmap init
  * leverages Java’s instance initializer as specified by the JLS §8.6.
  * This now keeps a reference to the outer instance, whatever that just happens to be

### Use functional interfaces

* or SAM (single abstract methods )
* Consumers like them
* The best SAMs are those with single arguments, as they will further simplify writing of a lambda

### Avoid returning null from API methods

* Help reduce number of NPE
* This allows users to chain methods
* return iterable objects or optional
* nulls can happen with lazy init
  * lazy initialisation should be used carefully, only.
  * ie If large data structures are involved.

### Never return null arrays or lists from API methods

* always instantiate a list
* reduce null checks
* use empty arrays or collections

### Avoid state, be functional

* Like http protocol
* pass in data to method
* It is very hard to correctly deal with stateful APIs in multi-threaded environments
* It is very hard to make stateful resources globally available, as the state is not documented
* **Manipulate less object state.**
  * Like jdbc

### Short-circuit equals()

* In large object graphs, you can gain significantly in terms of performance, if all your objects’ equals() methods dirt-cheaply compare for identity first:
* apply null checks first

### Try to make methods final by default

* if you’re in full control of all source code, there’s absolutely nothing wrong with making methods final by default
  * If you do need to override a method (do you really?), you can still remove the final keyword
  * You will never accidentally override any method anymore
* This specifically applies for static methods, where “overriding” (actually, shadowing) hardly ever makes sense
  * static methods don’t override each other, as the call-site binds a static method invocation at compile-time. If you’re unlucky, you might just get the wrong method accidentally.

### Avoid the method(T…) signature

* Using `Object.. all` argument type is fine
* But is differetn `T... all`
  * T can always be inferred to Object.
  * You cannot overload, as makes it ambiguous
    * you will never again be able to typesafely overload it.
  *
