On Github rskonnord-plos / java-8-features
Java 8 finally brings lambda syntax to Java.
Lambdas don't do anything that you couldn't have also done with anonymous nested classes, but lambdas do it...
Java 8 adds the ability to declare a default implementation for an interface method.
This lets you add new methods to interfaces without breaking downstream implementations, as long as you can define their behavior in terms of existing methods.
This looks spookily like multiple inheritance. Because interfaces inherit only multiple behaviors, not multiple states, it avoids the nastiest problems.
public interface Doi { /** * @return a DOI name such as "10.1371/journal.pone.0000000" */ String getDoiName(); }
public interface Doi { /** * @return a DOI name such as "10.1371/journal.pone.0000000" */ String getDoiName(); default URI getAsUri() { return URI.create("doi:" + getDoiName()); } }
Interfaces with default methods are similar to abstract classes. The differences are that interfaces:
Q: What happens if one class implements two interfaces, and each one provides a different default body for the same method signature?
A: Refactor your code until you don't need to know.
Java is still a statically typed language. Every value must have a declared type.
A lambda expression's type is always inferred from syntax.
The inferred type must be a functional interface.
An interface is functional if it has exactly one non-default, non-static method.
(Object methods (e.g., equals, hashCode, toString) don't count.)
package org.springframework.orm.hibernate3; import org.hibernate.HibernateException; import org.hibernate.Session; import java.sql.SQLException; public interface HibernateCallback<T> { T doInHibernate(Session session) throws HibernateException, SQLException; }
You can use the @FunctionalInterface annotation to mark interfaces that meet the definition of functional.
It works like @Override: it is never required (even if you actually use lambdas with that interface), but the compiler will complain if you put it somewhere invalid.
You generally want to use it only on interfaces that you actually expect to get anonymous implementations. It can be misleading otherwise.
Don't put it on interfaces that you expect to add methods to one day -- for example, a *Service class from our codebases.
@FunctionalInterface public interface Consumer<T> { void accept(T t); public static void main(String[] args) { Consumer<String> printTrimmed = (String s) -> { String trimmed = s.trim(); System.out.println(trimmed); }; } }
You can return things.
@FunctionalInterface public interface Predicate<T> { boolean test(T t); public static void main(String[] args) { Predicate<Integer> isEven = (Integer n) -> { int modulus = n % 2; return (modulus == 0); }; } }
If it is a single return statement, you can leave off the braces and write only the returned expression.
@FunctionalInterface public interface Predicate<T> { boolean test(T t); public static void main(String[] args) { Predicate<Integer> isEven = (Integer n) -> { return (n % 2 == 0); }; } }
@FunctionalInterface public interface Predicate<T> { boolean test(T t); public static void main(String[] args) { Predicate<Integer> isEven = (Integer n) -> (n % 2 == 0); } }
If the argument type can be inferred, you can leave it off.
@FunctionalInterface public interface Predicate<T> { boolean test(T t); public static void main(String[] args) { Predicate<Integer> isEven = (Integer n) -> (n % 2 == 0); } }
@FunctionalInterface public interface Predicate<T> { boolean test(T t); public static void main(String[] args) { Predicate<Integer> isEven = n -> (n % 2 == 0); } }
If it uses only a single method call, you can make it even terser with method reference syntax.
@FunctionalInterface public interface Function<T, R> { R apply(T t); public static void main(String[] args) { Function<String, Integer> getLength = s -> s.length(); } }
@FunctionalInterface public interface Function<T, R> { R apply(T t); public static void main(String[] args) { Function<String, Integer> getLength = String::length; } }
You can even use a method reference on a particular instance.
@FunctionalInterface public interface Supplier<T> { T get(); public static void main(String[] args) { String theValue = "resonance cascade"; Supplier<Integer> getLength = () -> theValue.length(); } }
@FunctionalInterface public interface Supplier<T> { T get(); public static void main(String[] args) { String theValue = "resonance cascade"; Supplier<Integer> getLength = theValue::length; } }
You can and should inline your lambdas.Type inference is your friend!
public static List<ArticleIdentity> getIdentities(List<Article> articles) { Function<Article, ArticleIdentity> extractId = article -> ArticleIdentity.create(article.getDoi()); return Lists.transform(articles, extractId); }
public static List<ArticleIdentity> getIdentities(List<Article> articles) { return Lists.transform(articles, article -> ArticleIdentity.create(article.getDoi())); }
Optional is a utility class that helps prevent null pointer exceptions.
Its purpose is to explicitly mark a value that, logically, may be either present or absent.
It works by convention: we NEVER allow null to be assigned to an Optional variable.