java8基本概念
来源:互联网 发布:电信大数据应用案例 编辑:程序博客网 时间:2024/06/06 14:07
函数式接口
只定义了一个抽象方法的接口就是函数式接口。
知识点回顾:
①接口中的方法默认为public abstract
,即默认就是公共的抽象方法。
函数描述符
官网解释:函数式接口中的抽象方法就是函数描述符
。
或者说是它的签名就是函数描述符。
具体点,比如Runable
这个接口里面只有一个方法run
。
@Overridepublic void run() {}
我们可以看到这个方法没有参数,也没有返回值。
那么我们就可以描述这个方法什么也不接受,什么也不返回。
①函数式接口的抽象方法的签名称为函数描述符
②抽象方法的签名可以描述Lambda
表达式的签名。
因为我们在使用Lambda
表达式时,总是要先定义一个函数式接口。这样很麻烦。
所以java8
的设计者,已经提前帮我们设计好了几种常用的接口。
Predicate
java.util.function.Predicate<T>
接口定义了一个名叫test
的抽象方法,它接受泛型T
对象,并返回一个boolean
。
@FunctionalInterfacepublic interface Predicate<T>{ boolean test(T t);}public static <T> List<T> filter(List<T> list, Predicate<T> p) { List<T> results = new ArrayList<>(); for(T s: list){ if(p.test(s)){ results.add(s); } } return results;}Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();//使用List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
Consumer
java.util.function.Consumer<T>
定义了一个名叫accept
的抽象方法,它接受泛型T
的对象,没有返回(void
)。你如果需要访问类型T
的对象,并对其执行某些操作,就可以使用这个接口。
@FunctionalInterfacepublic interface Consumer<T>{ void accept(T t);}public static <T> void forEach(List<T> list, Consumer<T> c){ for(T i: list){ c.accept(i); }}//使用forEach( Arrays.asList(1,2,3,4,5), (Integer i) -> System.out.println(i));
Function
java.util.function.Function<T, R>
接口定义了一个叫作apply
的方法,它接受一个泛型T
的对象,并返回一个泛型R
的对象。
@FunctionalInterfacepublic interface Function<T, R>{ R apply(T t);}public static <T, R> List<R> map(List<T> list, Function<T, R> f) { List<R> result = new ArrayList<>(); for(T s: list){ result.add(f.apply(s)); } return result;}// [7, 2, 6]List<Integer> l = map(Arrays.asList("lambdas","in","action"),(String s) -> s.length());
以上者三个是泛型函数式接口。但是java
除了引用类型,还有原始类型(int, double, byte,char)。但是泛型(比如Consumer<T>
中的T
)只能绑定到引用类型。这是由泛型内部的实现方式造成的。因此,在Java
里有一个将原始类型转换为对应的引用类型的机制。这个机制叫作装箱(boxing
)。相反的操作,也就是将引用类型转换为对应的原始类型,叫作拆箱(unboxing
)。Java还有一个自动装箱机制来帮助程序员执行这一任务:装箱和拆箱操作是自动完成的。
但这在性能方面是要付出代价的。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值。
Java 8为我们前面所说的函数式接口带来了一个专门的版本,以便在输入和输出都是原始类型时避免自动装箱的操作。
比如,在下面的代码中,使用IntPredicate
就避免了对值1000进行装箱操作,但要是用Predicate<Integer>
就会把参数1000装箱到一个Integer
对象中。
public interface IntPredicate{ boolean test(int t);}IntPredicate evenNumbers = (int i) -> i % 2 == 0;evenNumbers.test(1000); ----> true(无装箱)Predicate<Integer> oddNumbers = (Integer i) -> i % 2 == 1;oddNumbers.test(1000); ----> false(装箱)
一般来说,针对专门的输入参数类型的函数式接口的名称都要加上对应的原始类型前缀,比如DoublePredicate
、IntConsumer
、LongBinaryOperator
、IntFunction
等。Function
接口还有针对输出参数类型的变种:ToIntFunction<T>
、IntToDoubleFunction
等。
- java8基本概念
- 【Java8网络编程】第1章.基本概念
- Java8
- java8
- Java8
- java8
- java8
- java8
- java8
- Java8
- Java8
- Java8
- java8
- java8
- JAVA8
- JAVA8
- java8
- Java8
- #5 重定向
- 目前几款基于html5的前端框架:如Bootstrap、Foundation、Semantic UI 、Amaze UI
- Android下SQLite的简单实用
- 今日小程序推荐:好奇心日报-满足你的好奇心
- android动画Frame、Tween
- java8基本概念
- 主题五 内存管理的艺术----31.程序文件的一般布局
- <!-- value option -->
- Android的Fragment中关于Content的转换
- 你真的能够在区块链互联网中生存么?
- 解决cocos2dx-3.15下setBackgroundMusicVolume()不起作用的问题
- beginning
- bwapp medium writeup
- linux命令整理