jdk8新增了一下几个新的特性
- lambda表达式和函数式接口
- 方法引用
- stream流
这里stream流的api比较多在这篇文章就不详细展开了。
lambda表达式和函数式接口
lambda表达式
先简单介绍 一下lambda表达式:lambda表达式也可以称为是闭包,它允许把函数作为一个方法的参数传入到方法中,使用lambda表达式可以使代码更简介美观。
语法
我们来看下它的语法格式,很简单清晰
(入参)->{返回值}
他有如下几个特性
- 可选类型声明:入参部分传参的时候可以不声明参数类型,编译器能直接统一识别参数类型。
- 可选参数圆括号:入参部分是一个参数,那么可以把圆括号省略,如果是多个参数圆括号不能省略。
- 可选大括号:如果打括号里只有一句代码就可以省略大括号。
- 可选返回关键字:如果大括号内只有一个需要返回的值或则表达式,那么就可以省略return返回关键字。
使用
常见的几种写法
//无参不带括号且不带返回关键字
()->1;
//带返回关键字
()->return 1;
//带大括号
()->{return 1};
//有一个参数带小括号
(int a)->return a+1;
//不带小括号(不建议这么写)
a ->return a+1;
//多个参数,不写参数类型
(a,b)->{a+b};
//有参打印一句话
(String a)->System.out.printl(a)
大致的写法就是这样,后面会有更详细的用法。
lambda表达式常常和函数式接口一起使用,那么我们就来了解一下函数式接口
函数式接口
函数式接口首先它是个接口,然后这个接口里面有且仅有一个抽象方法,然后在该接口上加一个@FunctionalInterface注解,那么它就是一个函数式接口了。
语法
@FunctionalInterface
public interface FunctionIntfaceDemo {
String test1();
}
这样就简单定义了一个函数式接口。
如何使用
其实函数式接口可以隐式转换为lambda表达式
像上面我们定义的那个函数式接口它就可以用lambda表达式这么写
()->"返回一个String类型的参数";
或者直接赋值
FunctionIntfaceDemo test = ()->"返回一个String类型的参数";
java 给我们提前定义好了4个常用的函数式接口
分别是Functio< R, T>,Consumer,Supplier,Predicate。
接下来慢慢解释着几个函数的作用。
这里我们只简单的了解不做深入的讨论
Function< R,T >
这个函数它接收一个R对象类型的值,然后返回一个T对象类型的值,然后通过调用apply方法去执行内容。
我们直接看用法,这里我为了方便大家观察就不去简写lamdba表达式了
Function<Integer,Integer> s =(Integer a)->{return a+2;};
Integer apply = s.apply(2);
这里接收的参数必须是对象类型,不能是基础类型。
这串代码接收了一个integer类型的a,然后返回了一个integer类型的a+2。接着调用了apply传入了一个2,获得了一个结果。
关于Function的细节部分就不展开了,不然这篇文章就长了
Consumer
Consumer是消費者的意思,Consumer< V>就是接收一个V对象的值,然后不返回,本质上就接收的这个对象被消费掉了。然后使用accept去执行。
Consumer<Integer> consumer = (a)-> System.out.println(a) ;
consumer.accept(5);
Supplier
Supplier是提供者的意思,和上面的消費者相反,它不接收参数,Supplier< T>返回一个T对象类型的值。通过调用get方法执行。
Supplier<String> supplier = () -> "提供产品";
String s1 = supplier.get();
Predicate
这个单词翻译过来是谓语的意思,它是一个谓语接口Predicate< T,Boolean>它接收一个T类型的对象然后返回一个布尔值,通过test方法执行。其实它和Function一样,但是为了区分开来所以单独划分了出来。
Predicate<Integer> predicate = (Integer a) -> a >= 10;
boolean test1 = predicate.test(15);
其它接口
其实Java给我们提供了40多种函数接口,前面介绍的是最基础的四种接口。剩下的几个接口都是上面几个接口的变式,大多数都是对参数类型和参数数量做了限制。这里我们分别举一个例子
参数类型限制的接口:IntToDoubleFunction,接受一个int类型输入,返回一个double类型结果。这里同时限制了入参和返回值。这个就是Function的变种。
返回值类型限制的接口:DoubleSupplier,代表一个double值结构的提供方,其实它就是Supplier的变种
数量限制接口:这类接口需要接收两个参数,此类接口的名字前面一般都是Bi开头Binary
(二元的意思),比如BiConsumer< T,U>,代表了一个接受两个输入参数的操作,并且不返回任何结果。
Operator接口:这类接口就是一种简写一般用于两个相同类型的计算,这类型的接口总共就两种(不是两个),分别是一元操作的UnaryOperator< T>,接受一个参数为类型T,返回值类型也为T。和BinaryOperator
方法引用
方法引用我们可以理解为lambda表达式的另外一种形式具体概念就不多说了。
方法引用主要有四类,分别是静态方法引用,实例方法引用,对象方法引用以及构造方法引用。
接下来我们一个一个看他们的使用方法
我们先看下他们对应的语法格式
//静态方法引用
类名::静态方法
//实例方法引用
对象实例名::实例方法
//对象方法引用
对象名::实例方法
//构造方法引用
类名::new
上面这样提现出来可能不是太清晰,我们来具体看一下代码
代码举例
这里是列举了上面的语法结构,其实在使用时候可以很灵活。
public class MethodReferenceDemo {
static void StaticMethodQuote(){
System.out.println("静态方法引用");
}
void InstanceMethodQuote(){
System.out.println("实例方法引用");
}
MethodReferenceDemo(){
System.out.println("构造方法的引用");
}
public static void main(String[] args) {
//静态方法引用
Runnable staticMethodQuote = MethodReferenceDemo::StaticMethodQuote;
staticMethodQuote.run();
//实例方法引用
MethodReferenceDemo methodReferenceDemo = new MethodReferenceDemo();
Runnable instanceMethodQuote = methodReferenceDemo::InstanceMethodQuote;
instanceMethodQuote.run();
//对象方法引用
Consumer<MethodReferenceDemo> objectMethodQuote = MethodReferenceDemo::InstanceMethodQuote;
objectMethodQuote.accept(new MethodReferenceDemo());
//构造方法的引用
Runnable aNew = MethodReferenceDemo::new;
aNew.run();
}
}
我们来看下打印结果
静态方法引用
构造方法的引用
实例方法引用
构造方法的引用
对象方法的引用
构造方法的引用
这里构造方法的引用打印了三次是因为,我们将对象实例化了三次,除了静态方法的引用其余的都调用了构造方法。
其实上面的方法引用是可以直接转成lambda表达式的
大致就是这个样子
//静态方法引用
Runnable staticMethodQuoteLambda = () -> MethodReferenceDemo.StaticMethodQuote();
//实例方法引用
Runnable instanceMethodQuoteLambda = () -> methodReferenceDemo.InstanceMethodQuote();
//对象方法引用
Consumer<MethodReferenceDemo> objectMethodQuoteLambda= (objectMethodQuoteParameter)->objectMethodQuoteParameter.ObjectMethodQuote();
objectMethodQuoteLambda.accept(new MethodReferenceDemo());
//构造方法引用
Runnable ConstructionMethodLam = () -> new MethodReferenceDemo();
总结一下大概是这样的
//静态方法引用
(参数)->类名.静态方法(参数)
//实例方法引用
(参数)->实例名.实例方法(参数)
//对象方法引用
(对象,参数)->类名.实例方法(参数)
//构造方法引用
(参数)->new 类名(参数)
所以本质上这些东西区别也特别大
关于stream后面有时间就在专门写一篇总结下。