@HendrikEbbers
Karakun DevHub_
dev.karakun.com
@HendrikEbbers
JavaAPIsThe missing manual
Karakun DevHub_
@HendrikEbbersdev.karakun.com
About me
• Karakun Co-Founder
• Lead of JUG Dortmund
• JSR EG member
• JavaOne Rockstar, Java Champion
• JavaLand Programm Chair
Karakun DevHub_
@HendrikEbbersdev.karakun.com
About me
@HendrikEbbers
SPI
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Based on Interfaces it's quite easy to define a
general service and several implementations in
Java
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
public interface MathOperation {
String getSign();
double calc(double valA, double valB);
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• We can provide several implementations of math
operations
•AddOperation
•SubtractOperation
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
public class Multiply implements MathOperation {
public String getSign() { return "*";}
double calc(double valA, double valB){
return valA * valB;
}
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Instances of the implementations can be accessed
by using the general interface
• All instances can simply be stored in a collection
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
List<MathOperation> operations = new ArrayList<>();
operations.add(new MultipyOperation());
operations.add(new AddOperation());
operations.add(new DivideOperation());
operations.add(new SubtractOperation());
operations.forEach(o -> {
System.out.println("Supported Operation: " + o.getSign());
});
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Let's pimp our interface ;)
• With Java 8 default methods we can even define
default logic for some parts of the service
• Use defaults only if it really makes sense!
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
public interface MathOperation {
String getSign();
double calc(double valA, double valB);
default String getHelp() {
return "No help text for sign" + getSign();
}
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Let's think about a bigger application
• Often based on several modules / jars
• Several jars contains several service
implementations
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
Application
Basic Math
Module
Math Def
Module
Special Math
Module
contains the interface
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
Application
Basic Math
Module
Math Def
Module
Special Math
Module
contains the interface
contains implementations
contains implementations
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
Application
Basic Math
Module
Math Def
Module
Special Math
Module
contains the interface
contains implementations
contains implementations
use implementations
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
Application
Basic Math
Module
Math Def
Module
Special Math
Module
depends on
depends on
depends ondepends on
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
Application
Basic Math
Module
Math Def
Module
Special Math
Module
depends on
depends on
depends ondepends on
One Million
Dollar Question
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Where should we define the following method?
public List<MathOperation> getAllImplInClasspath();
?
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• We can't define it in the "Math Def Module" since
this module has no dependency to any
implementation
• Method would return

an empty list
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• We can't define it in the "Basic Math" or the
"Special Math" since this modules don't know all
implementations.
• Method would not return 

all implementations
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Only the "Application" module knows all
implementations since it's the only module that
depends on all other modules
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Only the "Application" module knows all
implementations since it's the only module that
depends on all other modules
That was easy, bro!
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Let's add some complexity
Basic Math
Module
Math Def 

Module
Special Math
Module
Algebra Math
Module
Geometry Math
Module
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
Basic Math
Module
Math Def 

Module
Special Math
Module
Algebra Math
Module
Geometry Math
Module
Customer A
App
Customer B
App
Customer C
App
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Headline
• We don't want to implement the



method for each application
• We need something new that loads all
implementations dynamically
public List<MathOperation> getAllImplInClasspath();
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
Basic Math
Module
Math Def 

Module
Special Math
Module
Algebra Math
Module
Geometry Math
Module
Customer A
App
Customer B
App
Customer C
App
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
Basic Math
Module
Math Def 

Module
Special Math
Module
Algebra Math
Module
Geometry Math
Module
Customer A
App
Customer B
App
Customer C
App
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Service Provider Interface (SPI) provides a simple
API to solve this issue
• Could be used to create a minimal but powerful
Plug In API
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Start by creating an interface
public interface MathOperation {
String getSign();
double calc(double valA, double valB);
}
we already have it
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Each module that provides implementations of
the interface most provide a service file
• File must be located under META-INF/services/
before
Java 9
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Name of the file must be equals to the interface
name
META-INF/services/com.canoo.math.MathOperation
File name
before
Java 9
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• File simply contains names of all implementations
com.canoo.basic.AddOperation

com.canoo.basic.MinusOperation
define one
implementation per
line
before
Java 9
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• The Service Provider Interface (SPI) will
automatically find all implementations at runtime
• Instances of the classes will automatically be
created
• Default constructor is needed
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• You can simply iterate over all implementations
Iterator<MathOperation> iterator = ServiceLoader
.load(MathOperation.class)
.iterator();


while (iterator.hasNext()) {

MathOperation operation = iterator.next();

}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• At the end we can implement the



method simple in the "Math Def Module"
• The service provider 

will automatically find

all implementations
public List<MathOperation> getAllImplInClasspath();
@HendrikEbbers
Annotations
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• Let's start with a quiz :)
• How many Java annotations are used in the code?
?
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
/**

* A helper class

* @see Class#getModifiers()

*

* @author Hendrik Ebbers
* @deprecated

*/
@Deprecated
public class Helper {
@Important
private Logger logger = new Logger();
/**

* Return {@code true} if the integer argument includes the {@code private} modifier, {@code false} otherwise.

*

* @param mod a set of modifiers

* @return {@code true} if {@code mod} includes the {@code private} modifier; {@code false} otherwise.

*/
@Static(false)

public static boolean isPrivate(@Param int mod) {

return (mod & 0x00000002) != 0;

}
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
/**

* A helper class

* @see Class#getModifiers()

*

* @author Hendrik Ebbers
* @deprecated

*/
@Deprecated
public class Helper {
@Important
private Logger logger = new Logger();
/**

* Return {@code true} if the integer argument includes the {@code private} modifier, {@code false} otherwise.

*

* @param mod a set of modifiers

* @return {@code true} if {@code mod} includes the {@code private} modifier; {@code false} otherwise.

*/
@Static(false)

public static boolean isPrivate(@Param int mod) {

return (mod & 0x00000002) != 0;

}
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
/**

* A helper class

* @see Class#getModifiers()

*

* @author Hendrik Ebbers
* @deprecated

*/
@Deprecated
public class Helper {
@Important
private Logger logger = new Logger();
/**

* Return {@code true} if the integer argument includes the {@code private} modifier, {@code false} otherwise.

*

* @param mod a set of modifiers

* @return {@code true} if {@code mod} includes the {@code private} modifier; {@code false} otherwise.

*/
@Static(false)

public static boolean isPrivate(@Param int mod) {

return (mod & 0x00000002) != 0;

}
}
@Deprecated
annotation is defined
in the JRE
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
/**

* A helper class

* @see Class#getModifiers()

*

* @author Hendrik Ebbers
* @deprecated

*/
@Deprecated
public class Helper {
@Important
private Logger logger = new Logger();
/**

* Return {@code true} if the integer argument includes the {@code private} modifier, {@code false} otherwise.

*

* @param mod a set of modifiers

* @return {@code true} if {@code mod} includes the {@code private} modifier; {@code false} otherwise.

*/
@Static(false)

public static boolean isPrivate(@Param int mod) {

return (mod & 0x00000002) != 0;

}
}
the @Important annotation is not defined in
the JRE but it's easy to create your own
custom annotations (like @Autowired in
Spring)
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
/**

* A helper class

* @see Class#getModifiers()

*

* @author Hendrik Ebbers
* @deprecated

*/
@Deprecated
public class Helper {
@Important
private Logger logger = new Logger();
/**

* Return {@code true} if the integer argument includes the {@code private} modifier, {@code false} otherwise.

*

* @param mod a set of modifiers

* @return {@code true} if {@code mod} includes the {@code private} modifier; {@code false} otherwise.

*/
@Static(false)

public static boolean isPrivate(@Param int mod) {

return (mod & 0x00000002) != 0;

}
}
the @Static annotation is
configured by a boolean parameter
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
/**

* A helper class

* @see Class#getModifiers()

*

* @author Hendrik Ebbers
* @deprecated

*/
@Deprecated
public class Helper {
@Important
private Logger logger = new Logger();
/**

* Return {@code true} if the integer argument includes the {@code private} modifier, {@code false} otherwise.

*

* @param mod a set of modifiers

* @return {@code true} if {@code mod} includes the {@code private} modifier; {@code false} otherwise.

*/
@Static(false)

public static boolean isPrivate(@Param int mod) {

return (mod & 0x00000002) != 0;

}
}
the @Param annotation is another
custom annotation that is used to
annotate a method param
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• Annotations can be used to add metadata to the
code
• Annotations can be added to different points of the
code
• Annotation can be parametrable
• Custom annotations can be defined
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
public @interface MyAnnotation {
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
public @interface MyAnnotation {
}
access modifier name
annotation keyword
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
@Documented
@Retention(RUNTIME)
@Target(METHOD)
public @interface MyAnnotation {
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
?
@Documented
@Retention(RUNTIME)
@Target(METHOD)
public @interface MyAnnotation {
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
@Documented
@Retention(RUNTIME)
@Target(ANNOTATION_TYPE)
public @interface Documented {
}
Annotations
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• If an annotation is annotated with @Documented
the usage of the annotation will be shown in
JavaDoc
• The usage of @Documented has no effect at
runtime
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• The @Target annotation defines where an
annotation can be used.
• The enum ElementType defines all supported
types
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• TYPE
• FIELD
• METHOD
• PARAMETER
• CONSTRUCTOR
• LOCAL_VARIABLE
• ANNOTATION_TYPE
• PACKAGE
• TYPE_PARAMETER
• TYPE_USE[ [
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
@Target(METHOD)
public @interface MyAnnotation {
}
@MyAnnotation
public void sqrt(int value) {
return 1;
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
@Target(METHOD)
public @interface MyAnnotation {
}
public void sqrt(@MyAnnotation int value) {
return 1;
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
@Target(PARAMETER)
public @interface MyAnnotation {
}
public void sqrt(@MyAnnotation int value) {
return 1;
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
@Target({PARAMETER, METHOD})
public @interface MyAnnotation {
}
@MyAnnotation
public void sqrt(@MyAnnotation int value) {
return 1;
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• The @Retention annotation defines how long an
annotation will be retained.
• The enum RetentionPolicy defines all
supported types
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
SOURCE CLASS RUNTIME
remove annotation at
compiletime
remove annotation at
runtime
do not remove
annotation
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
SOURCE CLASS RUNTIME
general meta
information like
@Override
for example the
annotations of findbugs
All annotations
that are used at
runtime
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• There are several ways how annotations can be
used
• Annotations like @Deprecated define useful
information for the developer (IDE support) and
compiler
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• Projects like Lombok use annotations for
annotation processing at compile time
• Annotations like @Inject are used to modify
objects at runtime
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• All annotations that have a @Retention policy
that is defined as RUNTIME can be accessed by
using reflection.
• In general this is one of the tricks that make DI or
Spring work
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• How can I check for annotations in my code at
runtime
?
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Only the "Application" module knows all
implementations since it's the only module that
depends on all other modules
Reflections
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
Class cls = MyCustomClass.class;

boolean val = cls.isAnnotationPresent(MyAnnotation.class);
• We will see more samples later
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• But there is more
• Annotations can be "configured"
• Java provides some nice language features here
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• Annotations can contain parameters
public @interface Column {
String name();
}
@Column(name="Description")
private String desc;
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• Annotations can define multiple parameters
• The following types are allowed:
• primitive
• String
• Class
• an Enum
• another Annotation
• an array of any of the types
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• If an Annotations only needs one param it should
be named as "value"
• By doing so the name must not be specified when
using the annotation
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
public @interface Column {
String value();
}
@Column("Name")
private String name;
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• Annotations can define a default value for a
parameters
public @interface Column {
String name() default "";
}
@Column
private String name;
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• Access annotation parameters
final Class cls = MyCustomClass.class;


if(cls.isAnnotationPresent(MyAnnotation.class)){
final MyAnnotation a = cls.getAnnotation(MyAnnotation.class);
final String name = a.name();
System.out.println("Name: " + name);
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Annotations
• Additional topics:
•@Inherited
• Lists of annotations
• Meta annotations
• Annotation Processor
@HendrikEbbers
Stream.error
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Streams
• Classes to support functional-style operations on
streams of elements, such as map-reduce
transformations on collections.
Stream.of("A", "B", "C")
.forEach(s -> System.out.println(s));
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Streams
• Sequence of elements
• Aggregate operations (filter, map, limit, reduce…)
• Pipelining − Most of the stream operations return
stream itself
Filter Map Reduce
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Streams
• Let's create a stream that converts String values
to int values
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Streams
Stream.of("1", "2", "3")
.map(s -> Integer.parseInt(s))
.forEach(i -> System.out.println("INT " + i));
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Only the "Application" module knows all
implementations since it's the only module that
depends on all other modules
That was easy, bro!
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Streams
• Let's see if this is really the perfect solution…
Stream.of("1", "2", "3", "X")
.map(s -> Integer.parseInt(s))
.forEach(i -> System.out.println("INT " + i));
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Exceptions in Streams
• Stream operations are not made to handle
exceptions
• To be more clear the functional interfaces in
java.util.function can not handle
exceptions.
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Exceptions in Streams
• Let's find a solution
Stream.of("1", "2", "3", "X")
.map(s -> {
try {
return Integer.parseInt(s)
} catch(Exception e) {
return -1;
}
})
.forEach(i -> System.out.println("INT " + i));
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Exceptions in Streams
• Let's find a solution
Stream.of("1", "2", "3", "X")
.map(s -> {
try {
return Integer.parseInt(s)
} catch(Exception e) {
return -1;
}
})
.forEach(i -> System.out.println("INT " + i));
Nope! That is not
what we want…
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Exceptions in Streams
• Let's find a solution
Stream.of("1", "2", "3", "X")
.map(s -> {
try {
return Integer.parseInt(s)
} catch(Exception e) {
throw new RuntimeException("…",e);
}
})
.forEach(i -> System.out.println("INT " + i));
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Exceptions in Streams
• Let's find a solution
Stream.of("1", "2", "3", "X")
.map(s -> {
try {
return Integer.parseInt(s)
} catch(Exception e) {
throw new RuntimeException("…",e);
}
})
.forEach(i -> System.out.println("INT " + i));
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Exceptions in Streams
public int convert(String s) {
try {
return Integer.parseInt(s)
} catch(Exception e) {
throw new RuntimeException("Can not convert to int!"e);
}
}
Stream.of("1", "2", "3", "X")
.map(s -> convert(s))
.forEach(i -> System.out.println("INT " + i));
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Exceptions in Streams
• The current approach looks ok but is far away
from a general solution
• We need to be more generic
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Checked functions
• Let's create or custom functional interface
• Libraries like VAVR do exactly this
@FunctionalInterface
public interface CheckedFunction<T, R> {
R apply(T t) throws Exception;
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Checked functions
@FunctionalInterface
public interface CheckedFunction<T, R> {
R apply(T t) throws Exception;
static <T,R> Function<T,R> wrap(CheckedFunction<T,R> f) {
Objects.requireNonNull(f, "Function must not be null");
return t -> {
try {
f.apply(t);
} catch(Exception e) {
throw new RuntimeException(e);
}
};
}
}
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Checked functions
• Now we have a general solution to handle
exceptions in streams…
Stream.of("1", "2", "3", "X")
.map(s -> CheckedFunction.wrap(s -> Integer.parseInt(s)))
.forEach(i -> System.out.println("INT " + i));
@HendrikEbbers
Result
1 2 3 Error
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Service Provider Interface
• Only the "Application" module knows all
implementations since it's the only module that
depends on all other modules
That was easy, bro!
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Checked functions
• Let's just change 2 chars…
Stream.of("1", "X", "3", "4")
.map(s -> CheckedFunction.wrap(s -> Integer.parseInt(s)))
.forEach(i -> System.out.println("INT " + i));
@HendrikEbbers
Result
1
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Additonal (functional) types
• The exception stops the stream
• But we can not return a default value
• We need to find a way to handle the complete
stream with exception support
• Again libraries like VAVR have some solutions
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Additonal (functional) types
Stream.of("1", "2", "A", "4").map(Result.of(s ->Integer.parseInt(s)))
.forEach(r -> {
if(r.isSuccessfull()) {
System.out.println(r.getInput() + " -> " + r.getResult());
} else {
System.out.println("Error for " + r.getInput());
Integer.parseInt("");
}
});
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Additonal (functional) types
public void handle(final Result<String, Integer> result) {
if(result.isSuccessfull()) {
System.out.println(result.getInput() + " -> " + result.getResult());
} else {
System.out.println("Error for " + result.getInput());
}
}
Stream.of("1", "2", "A", "4")
.map(Result.of(s ->Integer.parseInt(s)))
.forEach(r -> handle(r));
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Additonal (functional) types
Result<INPUT, OUTPUT>
Sucess<INPUT, OUTPUT> Fail<INPUT, OUTPUT>
Extends Extends
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Additonal (functional) types
Input Result
Karakun DevHub_
@HendrikEbbersdev.karakun.com
Additonal (functional) types
Input
Exeption
@HendrikEbbers
Karakun AG @
• Web-APIs: Das ultimative Handbuch
• Progressive Web Apps mit der Service Worker API
• Free React nighthacking in the office space of Eppleton
https://goo.gl/1wX3t3
dev.karakun.com | @HendrikEbbers

Java APIs - the missing manual