前言
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流的操作。