JDK8新特性-lambda表示式,函式式介面
1. String.join
/** * Returns a new String composed of copies of the * {@code CharSequence elements} joined together with a copy of * the specified {@code delimiter}. * * <blockquote>For example, * <pre>{@code * String message = String.join("-", "Java", "is", "cool"); * // message returned is: "Java-is-cool" * }</pre></blockquote> * * Note that if an element is null, then {@code "null"} is added. * * @param delimiter the delimiter that separates each element * @param elements the elements to join together. * * @return a new {@code String} that is composed of the {@code elements} * separated by the {@code delimiter} * * @throws NullPointerException If {@code delimiter} or {@code elements} * is {@code null} * * @see java.util.StringJoiner * @since 1.8 */ public static String join(CharSequence delimiter, CharSequence... elements) { Objects.requireNonNull(delimiter); Objects.requireNonNull(elements); // Number of elements not likely worth Arrays.stream overhead. StringJoiner joiner = new StringJoiner(delimiter); for (CharSequence cs: elements) { joiner.add(cs); } return joiner.toString(); }
使用了StringJoiner
/** <pre> {@code * List<Integer> numbers = Arrays.asList(1, 2, 3, 4); * String commaSeparatedNumbers = numbers.stream() * .map(i -> i.toString()) * .collect(Collectors.joining(", ")); * }</pre> * * @see java.util.stream.Collectors#joining(CharSequence) * @see java.util.stream.Collectors#joining(CharSequence, CharSequence, CharSequence) * @since 1.8 */ public final class StringJoiner { private final String prefix; private final String delimiter; private final String suffix; /* * StringBuilder value -- at any time, the characters constructed from the * prefix, the added element separated by the delimiter, but without the * suffix, so that we can more easily add elements without having to jigger * the suffix each time. */ private StringBuilder value; /* * By default, the string consisting of prefix+suffix, returned by * toString(), or properties of value, when no elements have yet been added, * i.e. when it is empty. This may be overridden by the user to be some * other value including the empty String. */ private String emptyValue;
是支援前後綴的StringBuilder工具類,推薦使用。
2. 介面的預設方法
public interface TestI {
default double sqrt(int a){
return Math.sqrt(a);
}
double cacl();
}
使用default關鍵字,有點抽象類的感覺。
3. Lambda 表示式
3.1 用法
解決匿名內部類作為方法引數的冗餘寫法,Java編譯器可以自動推匯出引數型別
在lambda表示式中訪問外層作用域和老版本的匿名物件中的方式很相似。你可以直接訪問標記了final的外層區域性變數,或者例項的欄位以及靜態變數。
Lambda 表示式不會像內部類生成一個帶$的class,jd-gui已經不能反編譯了。
public void runT() {
}
public void test(){
new Thread(new Runnable() {
@Override
public void run() {
runT();
}
}).start();
new Thread(()-> runT()).start();
} }
lambda表示式在Java編譯器可以自動推匯出引數型別。
3.2 作用域
在lambda表示式中訪問外層作用域和匿名內部類的訪問方式很相似。可以直接訪問標記了final的外層區域性變數(final可省略),或者例項的欄位以及靜態變數。
3.2.1 訪問區域性變數
lambda表示式訪問區域性變數預設就是final。
3.2.2 訪問例項變數與靜態變數
lambda內部對於例項變數、靜態變數是可讀,可寫。與匿名內部類訪問時一致的。
static int num = 1;
public static void main(String[] args) {
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
System.out.println(stringConverter.apply(2));
num = 5;
}
3.2.3 訪問介面的預設方法
lambda表示式只能覆寫一個抽象方法。因此介面預設的方法無法訪問。在 2. 介面的預設方法 介面的default方法,lambda表示式不能訪問。
4. 函式式介面
4.1. 自定義函式式介面
函式式需要介面新增 @FunctionalInterface 註解,僅有一個抽象方法,當編譯器如果發現你標註了這個註解的介面有多於一個抽象方法的時候會報錯。
@FunctionalInterface
public interface Converter<F, T> {
//方法名自定義,因為只有一個抽象方法
T apply(F from);
}
一個以上抽象方法報錯了
每一個lambda表示式都對應一個型別,通常是介面型別。將lambda表示式可以對映到一個單方法的介面,lambda表示式對映的介面可以不是函式式介面
而“函式式介面”是指僅僅只定義一個抽象方法的介面,每一個該型別的lambda表示式都會被匹配到這個抽象方法。因為預設方法 不算抽象方法,所以你也可以給你的函式式介面新增預設方法。
如果覆寫父類的方法則可支援多個抽象方法,比如equals方法。
@FunctionalInterface
public interface Converter<F, T> {
T apply(F from);
boolean equals(Object obj);
}
如下示例
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.apply("123");
System.out.println(converted); // 123
4.2. JDK 變更或增加的函式式介面
JDK 1.8 增加的函式式介面Runnable、Comparator等,便於使用lambda表示式。
如: Comparator
/** <p>This interface is a member of the
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
* Java Collections Framework</a>.
*
* @param <T> the type of objects that may be compared by this comparator
*
* @author Josh Bloch
* @author Neal Gafter
* @see Comparable
* @see java.io.Serializable
* @since 1.2
*/
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
/**
* Returns a comparator that imposes the reverse ordering of this
* comparator.
*
* @return a comparator that imposes the reverse ordering of this
* comparator.
* @since 1.8
*/
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
Runnable
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
Predicate介面
/**
* Represents a predicate (boolean-valued function) of one argument.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #test(Object)}.
*
* @param <T> the type of the input to the predicate
*
* @since 1.8
*/
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
支援單引數,用於或且非比對。
Predicate<String> predicate = (s) -> s.length() > 0;
predicate.test("ddddd"); // true
predicate.negate().test("ddddd"); // false
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
這裡要注意
//isEmpty不是靜態方法
Predicate<String> isEmpty = String::isEmpty;
當函式式介面的引數型別,此引數型別的類的方法呼叫,如上;必須直接通過類名呼叫的引用,否則報錯,不能解析方法。
Function 介面
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of input to the {@code before} function, and to the
* composed function
* @param before the function to apply before this function is applied
* @return a composed function that first applies the {@code before}
* function and then applies this function
* @throws NullPointerException if before is null
*
* @see #andThen(Function)
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*
* @see #compose(Function)
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* Returns a function that always returns its input argument.
*
* @param <T> the type of the input and output objects to the function
* @return a function that always returns its input argument
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
帶引數和返回結果
Supplier 介面
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
及其簡單,只有返回結果,沒有引數。
Consumer 介面
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
只有引數,不返回。
Comparator 介面
@FunctionalInterface
public interface Comparator<T> {
/**
* Compares its two arguments for order. Returns a negative integer,
* zero, or a positive integer as the first argument is less than, equal
* to, or greater than the second.<p>
*
* In the foregoing description, the notation
* <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
* <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
* <tt>0</tt>, or <tt>1</tt> according to whether the value of
* <i>expression</i> is negative, zero or positive.<p>
*
* The implementor must ensure that <tt>sgn(compare(x, y)) ==
* -sgn(compare(y, x))</tt> for all <tt>x</tt> and <tt>y</tt>. (This
* implies that <tt>compare(x, y)</tt> must throw an exception if and only
* if <tt>compare(y, x)</tt> throws an exception.)<p>
*
* The implementor must also ensure that the relation is transitive:
* <tt>((compare(x, y)>0) && (compare(y, z)>0))</tt> implies
* <tt>compare(x, z)>0</tt>.<p>
*
* Finally, the implementor must ensure that <tt>compare(x, y)==0</tt>
* implies that <tt>sgn(compare(x, z))==sgn(compare(y, z))</tt> for all
* <tt>z</tt>.<p>
*
* It is generally the case, but <i>not</i> strictly required that
* <tt>(compare(x, y)==0) == (x.equals(y))</tt>. Generally speaking,
* any comparator that violates this condition should clearly indicate
* this fact. The recommended language is "Note: this comparator
* imposes orderings that are inconsistent with equals."
*
* @param o1 the first object to be compared.
* @param o2 the second object to be compared.
* @return a negative integer, zero, or a positive integer as the
* first argument is less than, equal to, or greater than the
* second.
* @throws NullPointerException if an argument is null and this
* comparator does not permit null arguments
* @throws ClassCastException if the arguments' types prevent them from
* being compared by this comparator.
*/
int compare(T o1, T o2);
同類型比較,兩個引數。返回int。
5. 方法與建構函式引用
Java 8 允許你使用 :: 關鍵字來傳遞方法或者建構函式引用
5.1 先看方法的引用。
Converter<String, Integer> converter2 = Integer::valueOf;
Integer converted2 = converter2.apply("123");
System.out.println(converted2); // 123
Converter<String, String> converter3 = new StringConverter() ::startWith;
String converted3 = converter3.apply("www");
System.out.println(converted3); // "w"
可以在ide上看出
這種是lambda表示式的變種,僅一個抽象方法的lambda表示式變種,賦值是對介面做對映,介面對映可以不是函式式介面,只是函式式介面僅有一個抽象方法而已,可以避免出錯。
void forEach(Consumer<? super T> action);
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
list.stream().forEach(System.out::println);
list.stream().forEach(System.out::println);
這句程式碼可以直觀看出方法的引用就是lambda表示式的變種,對映的介面的引用。
System.out::println對映Consumer介面,相當於lambda表示式
(s)->System.out.println(s)
5.2 建構函式引用
@Data
public static class Man {
private int age;
private String name;
public Man(int age, String name) {
this.age = age;
this.name = name;
}
}
Factory<Man> converter6 = Man::new;
Man man = converter6.getFactoryBean(10, "Tom");
//@FunctionalInterface
public interface Factory<T> {
T getFactoryBean(int age,String name);
}
這裡要注意,如上示例,符號 ::new 是呼叫建構函式Man的建構函式,介面的方法引數,必須要與建構函式的構造引數型別和個數一致
由以上示例,@FunctionalInterface僅提供強制限制一個自定義抽象方法,非強制約束,普通介面在以上示例均正常執行。
6. Optional 介面
Optional 用來防止NullPointerException異常的輔助類,在Java 8中,不推薦你返回null而是返回Optional。專案中用起來不習慣,主要用來包裝可能為null的欄位。