码迷,mamicode.com
首页 > 编程语言 > 详细

java8新特性学习

时间:2015-09-28 21:03:58      阅读:298      评论:0      收藏:0      [点我收藏+]

标签:java   android   lambda   

内容:
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://stackoverflow.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
    }
} 

版权声明:本文为博主原创文章,未经博主允许不得转载。

java8新特性学习

标签:java   android   lambda   

原文地址:http://blog.csdn.net/chdjj/article/details/48789899

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!