An extensive quote from Maven documentation describes this quite clearly:
Optional dependencies are used when it's not possible (for whatever reason) to split a project into sub-modules. The idea is that some of the dependencies are only used for certain features in the project and will not be needed if that feature isn't used. Ideally, such a feature would be split into a sub-module that depends on the core functionality project. This new subproject would have only non-optional dependencies, since you'd need them all if you decided to use the subproject's functionality.
However, since the project cannot be split up (again, for whatever reason), these dependencies are declared optional. If a user wants to use functionality related to an optional dependency, they have to redeclare that optional dependency in their own project. This is not the clearest way to handle this situation, but both optional dependencies and dependency exclusions are stop-gap solutions.
Why use optional dependencies?
Optional dependencies save space and memory. They prevent problematic jars that violate a license agreement or cause classpath issues from being bundled into a WAR, EAR, fat jar, or the like.
How do optional dependencies work?
Project-A -> Project-B
The diagram above says that Project-A depends on Project-B. When A declares B as an optional dependency in its POM, this relationship remains unchanged. It's just like a normal build where Project-B will be added in Project-A's classpath.
Project-X -> Project-A
When another project (Project-X) declares Project-A as a dependency in its POM, the optional nature of the dependency takes effect. Project-B is not included in the classpath of Project-X. You need to declare it directly in the POM of Project X for B to be included in X's classpath.
A practical example: imagine that you are a developer of a library/framework SuperLib
that is built as one superlib.jar
. Your library provides multiple features. Its main feature (that most of the users use) is dependency injection based on a third-party di
library. However, one of your classes - EmailApi
- offers features to send e-mails, using a third-party email
library. Since superlib
is one artifact, it needs both di
and email
to be compiled.
Now put yourself in the position of a user who uses superlib
. They are interested in the dependency injection features. This is the core role of your library, so the dependency between superlib
and di
would not be optional.
However, most users are not interested in sending emails and may be bothered by having a useless email
library and its dependencies added to their application (which will cause size increase of their application and may cause a dependency version clash between the dependencies of email
and dependencies of the user's application). Therefore, you would mark the dependency on email
as optional
. As long as the user does not use your EmailApi
class, everything will run fine. However, if they do use EmailApi
, they will need the email
dependency, otherwise the application will fail at runtime with ClassNotFoundException for whichever class from email
would be referenced in EmailApi
. The user of your library will need to add the email
dependency explicitly in their POM.
See also When to use <optional>true</optional>
and when to use <scope>provided</scope>
.