UniJ: Universal JDK 9+ API Facade
Introduction
UniJ targets JDK 8 and is a facade of:
-
unmodifiable
List
/Set
/Map
factory methods (equivalent to those introduced in JDK 9+) -
some new
Collector
providers (equivalent to those introduced in JDK 9+)
UniJ provides a facade of above-mentioned methods in a similar way that SLF4J (Simple Logging Facade for Java) provides a facade of logging. In both cases, there is an API that can be implemented in many different ways and then be injected at runtime as a Java service.
UniJ consists of three key parts described further on: its API, its bindings, and its bundles.
Note: if you look for a specific UniJ project, consult UniJ project layout.
Analogy
UniJ is to new parts of JDK 9+ API what SLF4J is to logging API — a facade.
Quick Example
JDK 9+
Set<Integer> set = Set.of(1, 2);
List<Integer> list = List.of(1, 2, 1);
Map<Integer, String> map = Map.of(1, "a", 2, "b");
Set.copyOf(list); // ⇒ [1, 2]
Set.of(1, 2, 1); // throws "duplicate element" exception
Set.of(1, 2, null); // throws null-pointer exception
UniJ (JDK 8+)
Set<Integer> set = UniSets.of(1, 2);
List<Integer> list = UniLists.of(1, 2, 1);
Map<Integer, String> map = UniMaps.of(1, "a", 2, "b");
UniSets.copyOf(list); // ⇒ [1, 2]
UniSets.of(1, 2, 1); // throws "duplicate element" exception
UniSets.of(1, 2, null); // throws null-pointer exception
Notes
-
UniJ is meant only as a facade of the official JDK APIs. UniJ will not introduce any APIs of its own design. UniJ may only introduce new APIs that directly correspond to APIs in the latest stable release of the JDK (currently, it's JDK 13).
-
UniJ is also a partial:
Method Summary
JDK 9+ | UniJ (JDK 8+) | ||
---|---|---|---|
type | static method name | type | |
List Set |
of , copyOf |
UniLists UniSets |
|
Map |
of , copyOf ,entry , ofEntries |
UniMaps |
|
Collectors |
toUnmodifiableList toUnmodifiableSet toUnmodifiableMap filtering flatMapping |
UniCollectors |
Motivation
This library has been designed primarily for:
End Users Stuck on JDK 8
If you're stuck on JDK 8, you can't use JDK 9+'s new methods like List.of
, etc.
However, by adding a dependency on a UniJ bundle of your choosing (plus some optional extra dependencies), you can enjoy a JDK 11-like API on JDK 8!
See:
Libraries Targeting JDK 8
If you maintain a library targeting JDK 8, you can't use JDK 9+'s new methods like List.of
, etc.
However, by adding a dependency on UniJ User API, you can program to JDK 11-like API!
Note: your users will have to provide implementations of the above-mentioned API (as per Usage for End Users).
See:
API
UniJ has two kind of APIs:
- User API: utility classes (this is what the user interacts with)
- Service API: interfaces (this is what the service provider implements)
The call chain looks as follows:
end user ⟷ User API ⟷ Service API ⟷ service provider
In other words, the end user isn't aware of the Service API, and the service provider isn't aware of the User API.
User API
The User API is defined in pl.tlinkowski.unij.api
and consists of the following utility classes: UniLists
, UniSets
, UniMaps
and UniCollectors
(see Method Summary for details).
This API has strict equivalence to the corresponding JDK API (see API Specification for details).
Service API
Disclaimer: As an end user, you don't need to be concerned with this API.
UniJ Service API is defined in pl.tlinkowski.unij.service.api
and consists of the following interfaces:
-
Collection
factories:UnmodifiableListFactory
,UnmodifiableSetFactory
,UnmodifiableMapFactory
-
miscellaneous:
MiscellaneousApiProvider
A module providing implementations of one or more of these interfaces constitutes a binding.
API Specification
UniJ APIs come with a detailed specification for the Service API interfaces. The specification is based on the contract of the original JDK API (expressed mostly in JavaDoc), and tries to follow it as close as possible.
The focal points of the specification are:
null
treatment (nonull
s allowed)- duplicate handling (e.g. no duplicates allowed in
of
methods ofUniSets
andUniMaps
) - consistency (e.g. only one empty collection instance)
The specification is fully expressed as the following Spock test classes defined pl.tlinkowski.unij.test
:
-
Collection
factories:UnmodifiableListFactorySpec
,UnmodifiableSetFactorySpec
,UnmodifiableMapFactorySpec
-
miscellaneous:
MiscellaneousApiProviderSpec
Read the source of the Spock tests linked above to see what every UniJ binding guarantees.
Bindings
A binding is simply a library with implementation(s) of the Service API.
Note that UniJ supports multiple bindings of the same type at runtime.
Predefined Bindings
UniJ comes with a number of predefined bindings, which can all be found under subprojects/bindings
.
Collection Factory API Bindings
UniJ currently provides four types of Collection
factory API bindings:
-
JDK 10 (
pl.tlinkowski.unij.service.collect.jdk10
)-
simply forwards all calls to the JDK
-
example:
Jdk10UnmodifiableListFactory
-
-
JDK 8 (
pl.tlinkowski.unij.service.collect.jdk8
)-
provides regular mutable JDK 8 collections wrapped using
Collections.unmodifiableList/Set/Map
-
example:
Jdk8UnmodifiableListFactory
-
-
Guava (
pl.tlinkowski.unij.service.collect.guava
)-
provides Guava's
ImmutableList
/ImmutableSet
/ImmutableMap
implementations -
example:
GuavaUnmodifiableListFactory
-
note: Guava is a compile-only dependency for this binding (see Guava / Eclipse Collections for details)
-
-
Eclipse Collections (
pl.tlinkowski.unij.service.collect.eclipse
)-
provides Eclipse's
ImmutableList
/ImmutableSet
/ImmutableMap
implementations -
example:
EclipseUnmodifiableListFactory
-
note: Eclipse Collections is a compile-only dependency for this binding (see Guava / Eclipse Collections for details)
-
Miscellaneous API Bindings
UniJ currently provides two types of miscellaneous API bindings:
-
JDK 11 (
pl.tlinkowski.unij.service.misc.jdk11
)-
simply forwards all calls to the JDK
-
example:
Jdk11MiscellaneousApiProvider
-
-
JDK 8 (
pl.tlinkowski.unij.service.misc.jdk8
)-
provides custom implementations based on the ones in JDK 11
-
example:
Jdk8MiscellaneousApiProvider
-
Custom Bindings
Instead of any of the predefined bindings mentioned above, you can create and depend on a custom binding.
See:
Bundles
A UniJ bundle is a module having no source (save for its module-info.java
) and depending on the following three modules:
pl.tlinkowski.unij.api
module (transitive dependency)Collection
factory API binding (= one ofpl.tlinkowski.unij.service.collect.___
modules)- miscellaneous API binding (= one of
pl.tlinkowski.unij.service.misc.___
modules)
Predefined Bundles
Currently, UniJ provides the following four bundles:
-
JDK 11 bundle (
pl.tlinkowski.unij.bundle.jdk11
), made of: -
pure JDK 8 bundle (
pl.tlinkowski.unij.bundle.jdk8
), made of: -
Guava on JDK 8 bundle (
pl.tlinkowski.unij.bundle.guava_jdk8
), made of: -
Eclipse Collections on JDK 8 bundle (
pl.tlinkowski.unij.bundle.eclipse_jdk8
), made of:
Usage
See Usage document.
Extra Information
See Extra Information document.
Questions & Answers
See Q&A document.
Changelog
See Changelog document.
Requirements
Usage: JDK 8+.
Building: Gradle 5+, JDK 11+.
About the Author
See my webpage (tlinkowski.pl) or find me on Twitter (@t_linkowski).