前言
java 8 新出了一個功能就是stream流,乍一聽還以爲和輸入流輸出流有關係。其實吧,這兩者基本沒啥關係。stream主要是用來操作集合和數組的。但是,要搞明白這個之前,要先要下面這個圖是從別處拿來的,也可以看看這篇博客:https://www.cnblogs.com/andywithu/p/7404101.html。
stream流操作大量使用了lambda表達式,所以開始學習一下lambda表達式。
一、Lambda表達式及函數式接口介紹
學習lambda表達式之前要先了解以下知識
1、函數式接口
(1)只有一個抽象方法(Object 類中的方法除外)的接口是函數式接口,@FunctionalInterface註解 標註函數式接口,如果改接口不是函數式接口則會報錯。
(2)jdk1.8之前的一些函數式接口:java.lang.Runable; java.util.concurrent.Callable<V>; java.util.Comparator<T>。
2、JDK1.8自帶的函數式接口(java.util.function包下)
(1)Supplier() 代表一個輸出
(2)Consumer() 代表一個輸入
BiConsumer() 代表兩個輸入
(3)Function() 代表一個輸入,一個輸出(輸入和輸出不是同類型的)
UnaryOperator() 代表一個輸入,一個輸出,(輸入和輸出是同類型)
(4)BiFunction() 代表兩個輸入一個輸出(輸入和輸出是不同類型的)
BinaryOperator() 代表兩個輸入一個輸出(輸入輸出類型相同)
二、Lambda表達式詳解
lambda表達式是對象,是函數式接口的實例。非函數式接口不能使用lambda表達式!
1、lambda表達式語法
LambdaParameters -> LambdaBody
2、lambda表達式示例
以下代碼爲Runnable接口的實例化的方法,由此可見,lambda表達式可以簡化代碼。
public static void main(String[] args) throws Exception {
//無參數無返回值
Runnable r1 = new Runnable(){
@Override
public void run() {
System.out.println("hello");
}
};
r1.run();
Runnable r2 = () -> {System.out.println("hello");};
r2.run();
Runnable r3 = () -> System.out.println("hello");
r3.run();
//無參數有返回值
Callable<String> c1 = new Callable<String>() {
@Override
public String call() throws Exception {
return "hello";
}
};
System.out.println(c1.call());
Callable<String> c2 = () -> {return "hello";};
System.out.println(c2.call());
Callable<String> c3 = () -> "hello";
System.out.println(c3.call());
//有參數有返回值
Function<Integer,Integer> f1 = (a) -> {
Integer sum = 0;
for (int i = 0; i <= a; i++) {
sum += i;
}
return sum;
};
System.out.println(f1.apply(100));
}
當參數只有一個時,可以省略()
//有參數有返回值
Function<Integer,Integer> f1 = a -> {
Integer sum = 0;
for (int i = 0; i <= a; i++) {
sum += i;
}
return sum;
};
System.out.println(f1.apply(100));
3、lambda表達式使用方法
(1)看參數
(2)看返回值
先寫好 () -> {} 這個格式,再根據參數和返回值填入括號裏。
先寫好表達式
BiFunction<String,String,Integer> bf = () -> {}
然後看參數和返回值,BiFunction上面說了代表了兩個輸入參數,一個輸出即返回值,所以
BiFunction<String,String,Integer> bf = (a,b) -> {
return a.length() + b.length();
};
三、方法的引用
方法引用:用來直接訪問類或者實例的已經存在的方法或者構造方法。如果抽象方法的實現恰好可以使用調用另一個方法來實現,就有可能可以使用方法引用
1、方法引用的分類
2、使用示例
(1)靜態方法引用
靜態方法引用,如果函數式接口是通過調用一個靜態方法來實現,那麼就可以使用靜態方法引用
語法:類名::staticMelthod
public class StaticMethodExample {
public static String put(){
return "word";
}
public static void con (Integer size){
System.out.println("size2: "+size);
}
public static String toUpperCase(String str){
return str.toUpperCase();
}
public static void main(String[] args) {
/**
* 靜態方法引用
* 如果函數式接口的實現恰好可以通過調用一個靜態方法來實現,那麼就可以使用靜態方法引用
* 語法:類名::staticMethod
*/
//1、無參數有返回值
Supplier<String> s1 = () -> StaticMethodExample.put();
Supplier<String> s2 = StaticMethodExample::put;
System.out.println(s1.get());
System.out.println(s2.get());
//2、有參數無返回值
Consumer<Integer> c1 = (size) -> StaticMethodExample.con(size);
Consumer<Integer> c2 = StaticMethodExample::con;
c1.accept(10);
c2.accept(100);
//3、有參數有返回值
Function<String,String> f1 = (str) -> StaticMethodExample.toUpperCase(str);
Function<String,String> f2 = StaticMethodExample::toUpperCase;
System.out.println(f1.apply("hello"));
System.out.println(f2.apply("word"));
}
}
(2)實例方法引用
實例方法引用如果函數式接口的實現恰好可以通過調用一個實例的實例方法來實現,那麼就可以使用實例方法引用
語法:inst::instMethod
public class InstanceMethodExample {
public String put(){
return "hello";
}
public void con (Integer size){
System.out.println("size2: "+size);
}
public String toUpperCase(String str){
return str.toUpperCase();
}
public static void main(String[] args) {
/**
* 實例方法引用
* 如果函數式接口的實現恰好可以通過調用一個實例的實例方法來實現,那麼就可以使用實例方法引用
* 語法:inst::instMethod
*/
//1、無參數有返回值
Supplier<String> s1 = () -> new InstanceMethodExample().put();
Supplier<String> s2 = new InstanceMethodExample()::put;
System.out.println(s1.get());
System.out.println(s2.get());
//2、有參數無返回值
Consumer<Integer> c1 = (size) -> new InstanceMethodExample().con(size);
Consumer<Integer> c2 = new InstanceMethodExample()::con;
c1.accept(10);
c2.accept(100);
//3、有參數有返回值
Function<String,String> f1 = (str) -> new InstanceMethodExample().toUpperCase(str);
Function<String,String> f2 = new InstanceMethodExample()::toUpperCase;
System.out.println(f1.apply("hello"));
System.out.println(f2.apply("word"));
}
}
(3)對象方法引用
抽象方法沒有輸入參數,不能使用對象引用,抽象方法的第一個參數類型剛好是實例方法的類型,抽象方法剩餘的參數恰好可以當做實例方法的參數,如果函數式接口的實現能有上面說的實例方法來實現的話,那麼就可以使用對象方法引用。
語法:類名::instMethod
public class ObjectMethodExample {
public static void main(String[] args) {
/***
* 對象方法引用
* 抽象方法沒有輸入參數,不能使用對象引用
* 抽象方法的第一個參數類型剛好是實例方法的類型,抽象方法剩餘的參數恰好可以當做實例方法的參數,
* 如果函數式接口的實現能有上面說的實例方法來實現的話,那麼就可以使用對象方法引用。
* 語法:類名::instMethod
*/
//1、沒有剩餘參數
Consumer<Foot> c1 = (foot) -> {new Foot().get();};
Consumer<Foot> c2 = Foot::get;
c1.accept(new Foot());
c2.accept(new Foot());
//2、有剩餘參數
BiConsumer<Foot,String> b1 = (foot,str) -> {new Foot().put(str);};
BiConsumer<Foot,String> b2 = Foot::put;
b1.accept(new Foot() , "hello");
b2.accept(new Foot() , "word");
}
}
class Foot{
public void get(){
System.out.println("hello word");
}
public void put(String str){
System.out.println(str);
}
}
(4)構造方法引用
如果函數式接口的實現恰好可以通過調用一個類的構造方法來實現,那麼就可以使用構造方法引用
語法:類名::new
public class ConstructMethodExample {
public static void main(String[] args) {
/***
* 構造方法引用
* 如果函數式接口的實現恰好可以通過調用一個類的構造方法來實現,那麼就可以使用構造方法引用
* 語法:類名::new
*/
//1、無參數有返回值
Supplier<Person> s1 = () -> new Person();
Supplier<Person> s2 = Person::new;
s1.get();
s2.get();
//2、有參數無返回值
Consumer<String> c1 = (str) -> {new Person(str);};
Consumer<String> c2 = Person::new;
c1.accept("hello");
c2.accept("word");
}
}
class Person {
public Person() {
System.out.println("hello word");
}
public Person(String str) {
System.out.println(str);
}
}
寫在後面的話
Lambda表達式基本就這些內容了,方法引用可能比較難以理解,多練習一下就可以熟練使用了。下一篇開始說一下stream流的操作。