java8新特性学习

时间:2022-02-21 19:03:52

内容:
1. lambda表达式
2. streamAPI
3. 内置函数接口
4. 接口默认实现方法
5. android中使用lambda/streamAPI

=======

1.lambda表达式:
几种形式:

()->statement
()->(statement)
()->{statement}

以Thread为例:

new Thread(new Runnable(){
public void run(){
}
}).start();
new Thread(()->{
System.out.println(Thread.currentThread().getName());
})
.start();

再比如:

Collections.sort(list, (x, y) -> y - x);

lambda表达式简化了匿名内部类的写法.它要求匿名内部类中只有一个抽象方法,这其实也称为函数接口。如果该抽象方法有多个参数,可以这样写:

(arg1,arg2)->{
}
(int arg1,double arg2)->{
}

两种写法都是可以的。
lambda的范围与匿名内部类类似,可以访问外部区域的局部final变量,以及成员变量和静态变量。

@FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}
final int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2); // 3
//但是与匿名对象不同的是,变量num并不需要一定是final。
int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2); // 3
//num在编译的时候被隐式地当做final变量来处理,下面的写法是错误的
int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
num = 3;

2.函数接口:
函数式接口是只包含一个抽象方法的接口。对于函数式接口,除了可以使用Java中标准的方法来创建实现对象之外,还可以使用lambda表达式来创建实现对象。这可以在很大程度上简化代码的实现。在使用lambda表达式时,只需要提供形式参数和方法体。由于函数式接口只有一个抽象方法,所以通过lambda表达式声明的方法体就肯定是这个唯一的抽象方法的实现,而且形式参数的类型可以根据方法的类型声明进行自动推断。

3.内置函数接口:
Java 8 API 还提供了很多新的函数式接口,来降低程序员的工作负担。

Predicates
Predicate是一个布尔类型的函数,该函数只有一个输入参数。Predicate接口包含了多种默认方法,用于处理复杂的逻辑动词(and, or,negate)

private static void testPredicate(List<String> list,Predicate<String> p){
list.stream().filter(p).forEach(arg->{
System.out.println(arg);
});
}

//client调用
List<String> l = Arrays.asList("aaa","hhaha","hiahia","hehe");
testPredicate(l,(arg)->arg.length()==3);

testPredicate方法的功能是在list集合中通过predicate指定的规则过滤元素,最后把过滤通过的元素打印出来。过滤条件的实现当然可以通过lambda表达式:(arg)->arg.length==3,意为过滤条件是元素的size为3。当然,Predicate可以允许有多个条件:

Predicate<String> p1 = (arg)->arg.charAt(0)=='h';
Predicate<String> p2 = (arg)->arg.length()<6;
testPredicate(l,p1.and(p2));

原理可以参考Predicate源码:

@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}

可见,Predicate其实是一个函数接口。negate/and/or都是它的默认方法。

Functions
Function接口接收一个参数,并返回单一的结果。默认方法可以将多个函数串在一起(compse, andThen)

String s = "abcdefg";
Function<String,String> f = arg->arg.substring(3);
System.out.println(f.apply(s));//defg

再比如:

String s = "abcdefg";
Function<String,String> f = arg->arg.substring(3);
Function<String,Integer> f2 = f.andThen(arg->(arg.length()));
System.out.println(f2.apply(s));//输出4

先计算f,返回arg.substring(3),然后对返回的String执行arg.length(),所以结果应该是4.

参考源码:

@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}

static <T> Function<T, T> identity() {
return t -> t;
}
}

Supplier:
Supplier接口产生一个给定类型的结果。与Function不同的是,Supplier没有输入参数。
比如构造对象:

Supplier<Person> s = ()->new Person();
s.get().sayHello();
static class Person{
public void sayHello(){
System.out.println("hello");
}

}

参考源码:

@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/

T get();
}

Consumers:
Consumer代表了在一个输入参数上需要进行的操作。
比如:

private static void testConsumer(List<Integer> l,Consumer<Integer> consumer){
l.stream().forEach(i->{
consumer.accept(i);
});
}
testConsumer(Arrays.asList(1222,12,9),i->{System.out.println("hello:"+i);});

BiConsumer:
跟Consumer类似,不过接受两个参数,常见的场景是Map的遍历。
Map的foreach遍历:

Map<String,String> map = new HashMap<>();

map.put("aa", "value1");
map.put("bb", "value2");
map.put("cc", "value3");

map.forEach((arg1,arg2)->{
System.out.println(arg1+","+arg2);
});

4.Stream API:
java.util.Stream表示了某一种元素的序列,在这些元素上可以进行各种操作。Stream操作可以是中间操作,也可以是完结操作。完结操作会返回一个某种类型的值,而中间操作会返回流对象本身,并且你可以通过多次调用同一个流操作方法来将操作结果串起来。Stream是在一个源的基础上创建出来的,例如java.util.Collection中的list或者set(map不能作为Stream的源)。Stream操作往往可以通过顺序或者并行两种方式来执行。
常见的操作filter/sorted/map/match/count/reduce/
比如现在创建一个集合:

List<String> l = Arrays.asList("aaa","hhaha","hiahia","hehe");

filter:过滤特定的元素(返回stream)

Object[] l1 = l.stream().filter(arg->{
return arg.length()>3;
}
).toArray();

for(int i = 0; i<l1.length; i++){
System.out.println(l1[i]);
}

map:对每个元素执行某种操作:(返回stream)

Object[] l2 = l.stream().map(arg1->arg1.toUpperCase()).toArray();

for(int i = 0; i<l2.length; i++){
System.out.println(l2[i]);
}

sorted:排序(返回stream)

l.stream().sorted().forEach(arg->System.out.println(arg));

Match:匹配某种条件,有anyMatch/allMatch/noneMatch ( 返回boolean)

boolean result = l.stream().anyMatch(arg->arg.startsWith("a"));
System.out.println(result);

Parallel Streams
像上面所说的,流操作可以是顺序的,也可以是并行的。顺序操作通过单线程执行,而并行操作则通过多线程执行。

l.parallelStream().filter(arg->arg.startsWith("h")).map(arg->arg.toUpperCase()).forEach(arg->{
System.out.println(arg);
});

使用上跟stream()一样,但是效率更高.

5.接口的默认实现:
java8允许接口中有默认的实现方法,方法需加default声明:

@FunctionalInterface
public interface FooInterface {


public default void funcA(){
System.out.println("hello java8");
}

public int evaluate();

}

@FunctionalInterface注解代表当前接口是一个函数接口,也就是说里面必须只有一个抽象方法,如果出现多个则会报错,当然,默认方法不算。

注:
java8的接口和抽象类的区别:
1.抽象类注重继承,是为了继承而生。
2.抽象类可以有构造器、成员属性,接口不能有构造器,成员都是静态变量,隶属于类。
3.java8接口的默认方法主要作用是兼容低版本。因为当一个接口定义后之后,如果后期想增加一个方法的话,会导致之前的实现类无法使用,必须也实现新方法。默认方法的出现可以解决这一问题,只需增加一个默认方法,之前的实现类不会报错,而且新实现的类可以复写默认方法也可以不复写。
参考:

http://*.com/questions/19998454/interface-with-default-methods-vs-abstract-class-in-java-8

想在android里面使用?

方案:
retrolambda+Lightweight-Stream-API
地址:

https://github.com/evant/gradle-retrolambda
https://github.com/aNNiMON/Lightweight-Stream-API

根目录下gradle文件中增加:

 dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
classpath 'me.tatarka:gradle-retrolambda:3.2.3'

}

module目录下gradle文件中增加:

apply plugin: 'me.tatarka.retrolambda'
dependencies {
...
compile 'com.annimon:stream:1.0.3'
...
}

另需在gradle中增加编译选项:

android{
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}