技术控

    今日:124| 主题:49179
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] 在 Android 中使用 Java8 的特性

[复制链接]
姐的霸气凸不出来 发表于 2016-10-5 14:20:54
168 2

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
根据    Android 官网的说明,在开发面向 Android N 的应用时,可以使用 Java8 语言功能。目前 Android 只支持一部分 Java8 的特性:  
  
       
  •       Lambda 表达式   
  •       方法引用   
  •       默认和静态接口方法   
  •       重复注解  
  其中,只有前两者可以兼容 API 23 以下的版本。
  Lambda 表达式

  从一个实际例子来引入 lamdba 的使用。
  有一组 Person 对象(具体实现不复杂,参考    这里),需要通过年龄大小来过滤出满足要求的对象,然后对其进行输出操作,实现很简单,如下:  
  1. public static void printPersonsOlderThan(List<Person> roster, int age) {
  2.     for (Person p : roster) {
  3.         if (p.getAge() >= age) {
  4.             p.printPerson();
  5.         }
  6.     }
  7. }
复制代码
如果我的过滤条件变更了,就必须修改这个方法的代码,比如我现在根据年龄上下限进行过滤:
  1. public static void printPersonsWithinAgeRange(List<Person> roster, int low, int high) {
  2.     for (Person p : roster) {
  3.         if (low <= p.getAge() && p.getAge() <= high) {
  4.             p.printPerson();
  5.         }
  6.     }
  7. }
复制代码
这样一来,过滤条件经常变更的话,需要频繁修改这个方法。根据面向对象的思想,封装变化,把经常改变的逻辑封装起来,有外部来决定。这里我把过滤条件封装到    CheckPerson接口里,根据不同的过滤条件去实现这个接口即可。  
  1. @FunctionalInterface
  2. public interface CheckPerson {
  3.     boolean test(Person p);
  4. }
  5. public static void printPersons(List<Person> roster, CheckPerson tester) {
  6.     for (Person p : roster) {
  7.         if (tester.test(p)) {
  8.             p.printPerson();
  9.         }
  10.     }
  11. }
  12. // 实际使用
  13. List<Person> roster = Person.createRoster(); // 制造一些数据
  14. printPersons(roster, new CheckPerson() {
  15.     @Override
  16.     public boolean test(Person p) {
  17.         return p.getGender() == Person.Sex.MALE
  18.                 && p.getAge() >= 18
  19.                 && p.getAge() <= 25;
  20.     }
  21. });
复制代码
     CheckPerson接口是一个    函数式接口(functional interface),即    仅有一个抽象方法的接口。 因此实现这个接口的时候可以忽略掉方法名称,使用 Lambda 表达式来替代匿名类。  
  1. printPersons(roster,
  2.         p -> p.getGender() == Person.Sex.MALE
  3.                 && p.getAge() >= 18
  4.                 && p.getAge() <= 25
  5. );
复制代码
其实在 Java8 的包中,已经内置了一些标准的函数式接口。比如    CheckPerson接收一个对象,然后输出一个 boolean 值。可以使用    java.util.function.Predicate<T>来替代,它相当于 RxJava 中的    Func1<T, Boolean>,接收一个对象,返回布尔值。  
  1. public static void printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester) {
  2.     for (Person p : roster) {
  3.         if (tester.test(p)) {
  4.             p.printPerson();
  5.         }
  6.     }
  7. }
  8. // 使用起来并没有什么差别
  9. printPersonsWithPredicate(roster,
  10.         p -> p.getGender() == Person.Sex.MALE
  11.                 && p.getAge() >= 18
  12.                 && p.getAge() <= 25
  13. );
复制代码
这是,我并不满足于仅仅把过滤条件封装起来,还想把过滤之后对 Person 对象的操作也封装起来,便于修改。可以用另外一个标准的函数式接口    java.util.function.Consumer<T>,它相当于 RxJava 中的    Action1<T>,接收一个对象,返回 void。  
  1. public static void processPersons(List<Person> roster, Predicate<Person> tester, Consumer<Person> block) {
  2.     for (Person person : roster) {
  3.         if (tester.test(person)) {
  4.             block.accept(person);
  5.         }
  6.     }
  7. }
  8. // 具体使用
  9. processPersons(roster,
  10.         p -> p.getGender() == Person.Sex.MALE
  11.                 && p.getAge() >= 18
  12.                 && p.getAge() <= 25,
  13.         p -> p.printPerson()
  14. );
复制代码
如果我的处理过程中有数据转换的过程,可以用    java.util.function.Function<T, F>将其封装起来,这个接口相当于 RxJava 中的    Func1<T, F>,接收一个类型的对象,返回另外个类型的对象,达到数据转换的目的。比如例子中,把 Person 转换成 String 对象。  
  1. public static void processPersonsWithFunction(
  2.         List<Person> roster,
  3.         Predicate<Person> tester,
  4.         Function<Person, String> mapper,
  5.         Consumer<String> block) {
  6.     for (Person person : roster) {
  7.         if (tester.test(person)) {
  8.             String data = mapper.apply(person);
  9.             block.accept(data);
  10.         }
  11.     }
  12. }
  13. // 实际使用
  14. processPersonsWithFunction(roster,
  15.         p -> p.getGender() == Person.Sex.MALE
  16.                 && p.getAge() >= 18
  17.                 && p.getAge() <= 25,
  18.         p -> p.getEmailAddress(), // 获取 Person 对象的 email 字符串
  19.         email -> System.out.println(email)
  20. );
复制代码
最后可以把数据源也封装成一个    java.lang.Iterable<T>对象。  
  1. public static <X, Y> void processElements(
  2.         Iterable<X> source,
  3.         Predicate<X> tester,
  4.         Function<X, Y> mapper,
  5.         Consumer<Y> block) {
  6.     for (X x : source) {
  7.         if (tester.test(x)) {
  8.             Y data = mapper.apply(x);
  9.             block.accept(data);
  10.         }
  11.     }
  12. }
  13. // 实际使用
  14. Iterable<Person> source = roster;
  15. Predicate<Person> tester = p -> p.getGender() == Person.Sex.MALE
  16.         && p.getAge() >= 18
  17.         && p.getAge() <= 25;
  18. Function<Person, String> mapper = p -> p.getEmailAddress();
  19. Consumer<String> block = email -> System.out.println(email);
  20. processElements(roster, tester, mapper, block);
复制代码
在 Java8 中也可以把 Collections 对象快速转换成 Stream 来使用方便的操作符。
  1. roster.stream()                                     // 获取数据流
  2.     .filter(                                        // 根据 Predicate 过滤数据
  3.             p -> p.getGender() == Person.Sex.MALE
  4.                     && p.getAge() >= 18
  5.                     && p.getAge() <= 25)
  6.     .map(p -> p.getEmailAddress())                  // 根据 Function 转换数据
  7.     .forEach(email -> System.out.println(email));   // 对数据执行操作(消费数据)
复制代码
Lambda 写法

  基本写法:
  1. p -> p.getGender() == Person.Sex.MALE
  2.     && p.getAge() >= 18
  3.     && p.getAge() <= 25
  4. // 没有参数
  5. () -> System.out.println("Hello lambda")
  6. // 参数多于 1 个
  7. (x, y) -> x + y
复制代码
参数列表,如果只有一个参数,可以省略掉括号,其他情况需要写上一对括号。
  需要注意的是, 箭头    ->后面必须是一个单独的表达式(expression)或者是一个语句块(statement block)。  
  1. public static void printPersonsWithinAgeRange(List<Person> roster, int low, int high) {
  2.     for (Person p : roster) {
  3.         if (low <= p.getAge() && p.getAge() <= high) {
  4.             p.printPerson();
  5.         }
  6.     }
  7. }0
复制代码
方法引用

  当你使用一个 lambda 表达式的时候,如果它仅仅是调用了一下已有的方法,并没有做其他任何操作,就可以把它转换成    方法引用。方法引用有四种写法,下面一一介绍。  
  1. public static void printPersonsWithinAgeRange(List<Person> roster, int low, int high) {
  2.     for (Person p : roster) {
  3.         if (low <= p.getAge() && p.getAge() <= high) {
  4.             p.printPerson();
  5.         }
  6.     }
  7. }1
复制代码
引用静态方法

  首先在 Person 类中有一个静态方法,通过年龄比较大小:
  1. public static void printPersonsWithinAgeRange(List<Person> roster, int low, int high) {
  2.     for (Person p : roster) {
  3.         if (low <= p.getAge() && p.getAge() <= high) {
  4.             p.printPerson();
  5.         }
  6.     }
  7. }2
复制代码
  1. public static void printPersonsWithinAgeRange(List<Person> roster, int low, int high) {
  2.     for (Person p : roster) {
  3.         if (low <= p.getAge() && p.getAge() <= high) {
  4.             p.printPerson();
  5.         }
  6.     }
  7. }3
复制代码
引用具体实例的方法

  1. public static void printPersonsWithinAgeRange(List<Person> roster, int low, int high) {
  2.     for (Person p : roster) {
  3.         if (low <= p.getAge() && p.getAge() <= high) {
  4.             p.printPerson();
  5.         }
  6.     }
  7. }4
复制代码
引用特定类型的对象的实例方法

  Person 实现一下    Comparable<T>接口,会有一个    compareTo(Person)方法。  
  1. public static void printPersonsWithinAgeRange(List<Person> roster, int low, int high) {
  2.     for (Person p : roster) {
  3.         if (low <= p.getAge() && p.getAge() <= high) {
  4.             p.printPerson();
  5.         }
  6.     }
  7. }5
复制代码
  1. public static void printPersonsWithinAgeRange(List<Person> roster, int low, int high) {
  2.     for (Person p : roster) {
  3.         if (low <= p.getAge() && p.getAge() <= high) {
  4.             p.printPerson();
  5.         }
  6.     }
  7. }6
复制代码
引用构造方法

  有一个    transferElements方法,将    SOURCE类型的集合转换成    DEST类型的集合。  
  1. public static void printPersonsWithinAgeRange(List<Person> roster, int low, int high) {
  2.     for (Person p : roster) {
  3.         if (low <= p.getAge() && p.getAge() <= high) {
  4.             p.printPerson();
  5.         }
  6.     }
  7. }7
复制代码
其中    java.util.function.Supplier<T>也是标准的函数式接口,它有一个    get()方法来获取所提供的对象。  
  1. public static void printPersonsWithinAgeRange(List<Person> roster, int low, int high) {
  2.     for (Person p : roster) {
  3.         if (low <= p.getAge() && p.getAge() <= high) {
  4.             p.printPerson();
  5.         }
  6.     }
  7. }8
复制代码
lambda 表达式中直接 new 了一个 HashSet,相当于调用了 HashSet 的构造方法,故可以写成    HashSet::new方法引用的形式。  
  静态和默认接口方法

  在 Java8 之前,接口不允许有默认实现,如果接口的两个实现类有同样的实现逻辑,就得写重复代码了。现在接口可以通过关键字    default实现默认方法,另外接口还可以实现静态方法。  
  1. public static void printPersonsWithinAgeRange(List<Person> roster, int low, int high) {
  2.     for (Person p : roster) {
  3.         if (low <= p.getAge() && p.getAge() <= high) {
  4.             p.printPerson();
  5.         }
  6.     }
  7. }9
复制代码
  1. @FunctionalInterface
  2. public interface CheckPerson {
  3.     boolean test(Person p);
  4. }
  5. public static void printPersons(List<Person> roster, CheckPerson tester) {
  6.     for (Person p : roster) {
  7.         if (tester.test(p)) {
  8.             p.printPerson();
  9.         }
  10.     }
  11. }
  12. // 实际使用
  13. List<Person> roster = Person.createRoster(); // 制造一些数据
  14. printPersons(roster, new CheckPerson() {
  15.     @Override
  16.     public boolean test(Person p) {
  17.         return p.getGender() == Person.Sex.MALE
  18.                 && p.getAge() >= 18
  19.                 && p.getAge() <= 25;
  20.     }
  21. });0
复制代码
最后输出结果:
  1. @FunctionalInterface
  2. public interface CheckPerson {
  3.     boolean test(Person p);
  4. }
  5. public static void printPersons(List<Person> roster, CheckPerson tester) {
  6.     for (Person p : roster) {
  7.         if (tester.test(p)) {
  8.             p.printPerson();
  9.         }
  10.     }
  11. }
  12. // 实际使用
  13. List<Person> roster = Person.createRoster(); // 制造一些数据
  14. printPersons(roster, new CheckPerson() {
  15.     @Override
  16.     public boolean test(Person p) {
  17.         return p.getGender() == Person.Sex.MALE
  18.                 && p.getAge() >= 18
  19.                 && p.getAge() <= 25;
  20.     }
  21. });1
复制代码
使用接口的默认方法可以减少代码重复,静态方法也可以方便地封装一些通用逻辑。
  重复注解

  重复注解就是允许在同一申明类型(类,属性,或方法)多次使用同一个注解。
  1. @FunctionalInterface
  2. public interface CheckPerson {
  3.     boolean test(Person p);
  4. }
  5. public static void printPersons(List<Person> roster, CheckPerson tester) {
  6.     for (Person p : roster) {
  7.         if (tester.test(p)) {
  8.             p.printPerson();
  9.         }
  10.     }
  11. }
  12. // 实际使用
  13. List<Person> roster = Person.createRoster(); // 制造一些数据
  14. printPersons(roster, new CheckPerson() {
  15.     @Override
  16.     public boolean test(Person p) {
  17.         return p.getGender() == Person.Sex.MALE
  18.                 && p.getAge() >= 18
  19.                 && p.getAge() <= 25;
  20.     }
  21. });2
复制代码
使用时可以通过    AnnotatedElement.getAnnotationsByType()方法来获取到注解,然后进行相应的处理。  
  1. @FunctionalInterface
  2. public interface CheckPerson {
  3.     boolean test(Person p);
  4. }
  5. public static void printPersons(List<Person> roster, CheckPerson tester) {
  6.     for (Person p : roster) {
  7.         if (tester.test(p)) {
  8.             p.printPerson();
  9.         }
  10.     }
  11. }
  12. // 实际使用
  13. List<Person> roster = Person.createRoster(); // 制造一些数据
  14. printPersons(roster, new CheckPerson() {
  15.     @Override
  16.     public boolean test(Person p) {
  17.         return p.getGender() == Person.Sex.MALE
  18.                 && p.getAge() >= 18
  19.                 && p.getAge() <= 25;
  20.     }
  21. });3
复制代码
输出如下:
  1. @FunctionalInterface
  2. public interface CheckPerson {
  3.     boolean test(Person p);
  4. }
  5. public static void printPersons(List<Person> roster, CheckPerson tester) {
  6.     for (Person p : roster) {
  7.         if (tester.test(p)) {
  8.             p.printPerson();
  9.         }
  10.     }
  11. }
  12. // 实际使用
  13. List<Person> roster = Person.createRoster(); // 制造一些数据
  14. printPersons(roster, new CheckPerson() {
  15.     @Override
  16.     public boolean test(Person p) {
  17.         return p.getGender() == Person.Sex.MALE
  18.                 && p.getAge() >= 18
  19.                 && p.getAge() <= 25;
  20.     }
  21. });4
复制代码
使用就是这么简单~
  在 Android 中使用这些特性

  在主 module (app) 的    build.gradle里配置,开启 jack 编译器,使用 Java8 进行编译。 如果要体验接口的默认方法等特性,minSdkVersion 需要指定为 24 (Android N)。  
  1. @FunctionalInterface
  2. public interface CheckPerson {
  3.     boolean test(Person p);
  4. }
  5. public static void printPersons(List<Person> roster, CheckPerson tester) {
  6.     for (Person p : roster) {
  7.         if (tester.test(p)) {
  8.             p.printPerson();
  9.         }
  10.     }
  11. }
  12. // 实际使用
  13. List<Person> roster = Person.createRoster(); // 制造一些数据
  14. printPersons(roster, new CheckPerson() {
  15.     @Override
  16.     public boolean test(Person p) {
  17.         return p.getGender() == Person.Sex.MALE
  18.                 && p.getAge() >= 18
  19.                 && p.getAge() <= 25;
  20.     }
  21. });5
复制代码
Reference

  
       
  • 文中代码大部分来自于      Oracle 官方文档教程   
  •       在 Android N 预览版中使用 Java 8 的新特性  
友荐云推荐




上一篇:Banking industry group requests a way to disable forward secrecy in TLS 1.3
下一篇:React Component Curry
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

GennadiyMl 发表于 2016-10-5 17:00:18
锄禾日当午,姐的霸气凸不出来真辛苦。谁知坛中餐,帖帖皆辛苦!
回复 支持 反对

使用道具 举报

hnz1314 发表于 2016-10-5 19:14:29
大人,此事必有蹊跷!
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表