Java 8:Best practices

Learn
4 min readJan 22, 2018

What is target typing in the context of Lambdas?
Context determines the type of the lambdas as in the type is implicitly inferred. If you have an array of Strings and you supply a lambda to the forEach method on the array, then the type of the lambda is a String. If you had started off with an array of Integers, then the type of the lambda is an Integer. Since target typing is there, there is no need to specify the type.

Only specify types when the compiler needs it
The spirit of lambda is brevity. Since types of the parameters for a lambda can be easily inferred from the context from both a compiler and a readability perspective, omit types and keep the code simple and concise.

Do this

names.sort((nameOne, nameTwo) -> nameOne.compareTo(nameTwo));

instead of

names.sort((String nameOne, String nameTwo) -> nameOne.compareTo(nameTwo));

In the context of Lambdas, do not use parameter brackets when optional

names.replaceAll(name->name.toUpperCase());

instead of

names.replaceAll((name)->name.toUpperCase());

What is effectively final?
When invoking a lambda from within a method, all the variables in the enclosing scope of the lambda are “effectively final”. Meaning the lamba cannot change the variables in the function that is invoking the lambda. This makes sense when lamdas are looked at as qualified functions. The called function would only have access to the parameters passed in — not all the variables in the enclosing function.

public static void testEffectivelyFinal() {
Set<String> names = Set.of(“abc”, “def”, “ghi”);
Integer someVariable = 100;
names.forEach(name-> {
someVariable = 200; // This line does not compile.
// Gives the compilation error — Local variable someVariable defined in an enclosing scope must be final or effectively final
});
}

Effectively final sounds like ‘less than final’. How is that the case?
The variable cannot be changed within the lambda. However, it can be changed outside the lambda.

public static void testEffectivelyFinal() {
Set<String> names = Set.of(“abc”, “def”, “ghi”);
Integer someVariable = 100;
names.forEach(name-> {});

someVariable = 200; // Not final outside the lambda.Can be changed.
}

What is the best practice around “effectively final”?
Since the local variables are “effectively final”, do not declare the variables as final. Less code to type. Less weight to read.

What is an expression lambda?
A lambda that is just an expression, a one-liner is referred to as an expression lambda.

List<String> names = Arrays.asList(“abc”, “def”, “ghi”);
names.replaceAll(name->name.toUpperCase());

[ABC, DEF, GHI]

What is a block lambda?
A lambda that is a block of code instead of a single one-liner expression is referred to as block lambda.

List<String> names = Arrays.asList(“abc”, “def”, “ghi”);
names.replaceAll(name-> {
if(name.startsWith(“a”)) {
return name.toUpperCase();
} else {
return name.toLowerCase();
}});

System.out.println(names);

[ABC, def, ghi]

What is the best practice around block lambdas and expression lambdas?
Avoid using block lambdas and try to use expression lambdas. If the logic is complicated, write a separate method and have an expression that merely invokes that method as the lambda expression.

private static String caser(String name) {
if(name.startsWith(“a”)) {
return name.toUpperCase();
} else {
return name.toLowerCase();
}
}

List<String> names = Arrays.asList(“abc”, “def”, “ghi”);

names.replaceAll(name->caser(name));

System.out.println(names);

[ABC, def, ghi]

The lambda can also be written using method reference as below.

names.replaceAll(ClientOne::caser);

What are functional interfaces?
Interfaces with just one abstract method.

What is the best practice around functional interfaces?
Learn java.util.function package and see if there is a functional interface in there that has the semantics that you are looking for and if yes, do not write your own functional interface. Also, if you do end up writing your own functional interface, use the @ FunctionInterface annotation to protect yourself from adding a second method.

When using lambdas that throw exceptions, you will need to use block lambdas and have try-catch blocks.

public static void getClasses() {
List<String> classNames = new ArrayList(Arrays.asList(“java.util.Map”, “java.util.List”));
classNames.forEach(className->{
try {
Class klass = Class.forName(className);
System.out.println(klass);
} catch (ClassNotFoundException e) {e.printStackTrace();…

interface java.util.Map
interface java.util.List

Using optionals
Since the service indicates that it just returns an optional, the client knows that it needs to prepare for that and so the client can decide on a default value to use in the event that the service does not return a valid value back.

public static Optional<String> getDigitName(int digit) {
Optional<String> result = Optional.ofNullable(null);
switch(digit){
case 0: result = Optional.of(“Zero”);break;
case 1: result = Optional.of(“One”);break;
case 2: result = Optional.of(“Two”);break;

}return result;}

Now, the client side code uses the orElse method to handle the optional empty result scenario.

int digitArg = 7;
String digitName = DigitName.getDigitName(digitArg).orElse(“Digit not recognized”);
Digit not recognized

Alternatively, if the value is not found, an exception can be thrown

String digitName = DigitName.getDigitName(digitArg).orElseThrow(RuntimeException::new);

Exception in thread “main” java.lang.RuntimeException
at java.base/java.util.Optional.orElseThrow(Unknown Source)

Optionals are there a concept so that client code should never have to worry about null pointer exceptions.

What is the best practice around Optionals?
Optional return values should never be null. And the reason for the Optional never ever being allowed to be null is that if the optional is null, then you cannot run the orElse methods on it.
The following defaulting of the Optional to null defeats the orElse methods in that a null pointer gets thrown and the orElse behavior never kicks in.

Optional<String> result = null;

switch(digit){
case 0: result = Optional.of(“Zero”);
break;}
return result;

Setting the result as null is WRONG and should not be done.

Client code

String digitName = DigitName.getDigitName(digitArg).orElse(“Digit not recognized”);
System.out.println(digitName);

does not work as expected. A null pointer exception is thrown instead.

Exception in thread “main” java.lang.NullPointerException
at java9_study.ClientOne.digitNameClient(ClientOne.java:43)

Client basically gets duped. It is a breach of contract from the service side to return null. Client seeing that the service indicated that the return type is Optional took the necessary steps to safeguard himself but did not benefit from it. From another perspective, the service lied in that it said it would always return an optional where as it returned a null instead.

Another best practice around Optionals is to use the orElse methods a lot. Lean towards the orElse pattern

For public return types, do not use nulls. Use Optionals instead.

Best practice to use default methods on interface instead of factory class like List.of , Set.of etc

--

--