美文网首页
JDK1.8的新特性(Lambda表达式等)(一)

JDK1.8的新特性(Lambda表达式等)(一)

作者: ariazeng | 来源:发表于2018-12-19 13:37 被阅读0次

    一、接口的默认方法

    Java 8允许在接口中添加非抽象的方法实现,只需要使用default关键字即可,这个特征又叫做扩展方法,示例如下:

    interface iNews{
        List<News> getAllNews();
    
        default int aadd(int a){
              return a*100;
        }
    }
    

    iNews接口的子类只需要实现getAllNews();这个方法就可以了,默认就会带有getDate()这个方法不需要再实现。

    二、Lambda表达式

    我先举一个例子,示例代码如下:

      //定义一个stringlist
      List<String> names = Arrays.asList("Lucy","XiaoMing","Mike");
      //排序
      Collections.sort(names,Comparator<String>() {
        @Override
        public int compare(String a, String b) {
            return b.compareTo(a);
        }
    });
    

    老版本中的做法就是给静态方法Collections.sort方法传入一个list对象还有一个比较器来指定顺序排列。通常做法都是创建一个匿名的比较器对象然后传给sort方法。

    但是在Java 8 中就没有必要再这样做了,Java8中提供了更加简洁的方式:Lambda表达式
    直接上示例代码:

      //定义一个stringlist
      List<String> names = Arrays.asList("Lucy","XiaoMing","Mike");
      //排序
      Collections.sort(names,(String a,String b) ->{
          return b.compareTo(a);
      });
    

    只用Lambda表达式代码变得更加简洁了,但是还可以变短,示例代码如下:

      //排序
      Collections.sort(name,(String a , String b) -> b.compareTo(a) );
    

    如果函数体只有一行代码,那么可以直接去掉大括号{}还有return关键字,但实际上还可以更短

      //排序
      Collections.sort(name,(a,b) -> b.compareTo(a));
    

    Java编译器可以自动推导出来参数类型,所以你可以不用再写参数类型。

    三、函数式接口

    Lambda表达式是如何在Java的类型系统中表示的呢?每一个Lambda表达式都表示对应一个类型,通常是接口类型。而函数式接口就是指只有一个抽象方法的接口,每一个该类型的Lambda表达式都会匹配到这个抽象方法。因为默认方法 不算抽象方法,所以你也可以给你的函数式接口添加默认方法。
    默认方法指的就是Java8中的新特性——给接口添加一个默认方法,具体有在第一点讲到

    我们可以将Lambda表达式当做任意一个只包含一个抽象方法的接口类型,确保你的接口达到了这个要求,可以使用一个注解:@FunctionalInterface ,这个注解的作用也是非常简单明了,把这个注解贴在接口上,如果这个接口有多余一个的抽象方法,就会报错。(再直白点,就是贴上了之后,这个接口只能有一个抽象方法,多了就报错)。

    实例代码如下:

    @FunctionalInterface
    interface Converter<F,T>{
      T convert(F from);
    }
    Converter<String,Integer> converter = (from) -> Integer.valueOf(from);
    Integer converter = converter.convert("123");
    System.out.println(converter);    //结果为123
    

    四、方法与构造函数

    前一节的代码还可以通过静态方法来引用,示例代码如下:

    Converter <String,Integer> converter = Integer::valueOf;
    Integer converted = converter.convert("123");
    System.out.println(converted);
    

    Java8允许你使用::关键字来传递方法或者是构造函数的引用,上面的代码引用的是一个静态方法,我们还可以引用一个对象的方法,示例代码如下:

    converter = something::startsWith;
    String converted = converter.convert("java");
    System.out.println(converted);    //j
    

    接下来来试试构造函数的引用,先创建一个具有多个构造函数的简单Java类,示例代码如下:

    class Person{
        String name;
        Double money;
    }
    
    Person(){
    }
    Person(String name , Double money){
        this.name = name;
        this.money = money;
    }
    

    接下来指定一个用来创建Person对象的对象工厂接口,示例代码如下:

    interface PersonFactory<P extends Person> {
        P create(String name, Double money);
    }
    

    我们使用构造函数把他们关联起来,而不是实现一个完整的工厂,示例代码如下:

    PersonFactory<Person> personFactory = Person::new;
    Person person = personFactory.create("小明",1000.0);
    

    我们使用的时候,只需要用Persion :: new来获取Person类的构造函数的引用,Java编译器会自动根据PersonFactory.create方法的签名来选择合适的构造函数。

    五、访问局部变量

    我们可以直接在lambda表达式中访问外层的局部变量,示例代码如下:

    final int num = 1;
    Converter<Integer,String> stringConverter = (from) ->String.valueOf(from+num);
    stringConverter.convert("2");    //3
    

    但是这里的 final int num可以不用final 修饰,代码同样正确,但是如果去掉了final的话,num变量就不能被后面的代码修改(其实就是final了),如果被后面的代码修改了,代码就无法被编译。

    六、访问对象字段与静态变量

    和本地变量不同的是,lambda内部对于实例的字段以及静态变量是又可读又可写。
    示例代码如下:

    class lambda {
      static int staticNum;
      int num;
      void test(){
        Converter<Integer,String> stringConverter1 = (from) ->{
           staticNum = 123;
           return String.valueOf(from)  
        };
        Converter<Integer,String> stringConverter2= (from) ->{
           num = 123;
           return String.valueOf(from)  
        };
      }
    }
    

    七、访问接口的默认方法

    还记得第一节的例子吗?一个接口除了抽象方法,还可以定义一个默认方法,这个方法可以被它的实例对象或者是匿名对象访问,但是这个在Lambda中是不行的。
    Java8 API提供了很多全新的函数式接口让工作更加方便:

    1. Predicate接口
      Predicate接口只有一个参数,返回boolean类型,其中包含了很多默认方法来组成复杂的逻辑
      示例代码如下:
    Predicate<String> predicate = (s) -> s.length() > 0 ;
    predicate.test("foo");    //true
    predicate.negate().test("foo");     // false
    
    Predicate<Boolean> nonNull = Objects::nonNull;
    Predicate<Boolean> isNull = Objects::isNull;
    
    Predicate<String> isEmpty = String::isEmpty;
    Predicate<String> isNotEmpty = isEmpty.negate();
    
    1. Supplier 接口
      Supplier接口返回一个任意范围的值,和Function接口不同的是这个接口没有任何参数
      示例代码如下:
    Supplier<Person> personSupplier = Person :: new
    personSupplier.get();    //new Person
    
    1. Consumer接口
      Consumer接口代表在单个参数上执行的操作
      示例代码如下:
    Consumer<Person> con = (p) -> System.out.println("Hello,"+p.name);
    con.accept(new Person("Lucy",500.0));      //Hello,Lucy
    
    1. Comparator接口
      Comparator是以前版本中的经典接口,Java8在此之上添加了多种默认方法
      示例代码如下:
    Comparator<Person> comparator = (p1,p2) -> p1.name.comparaTo(p2.name);
    Person p1 = new Person("小明",100.0);
    Person p2 = new Person("小红",100.0);
    comparator.compara(p1,p2);
    comparator.reversed().compare(p1, p2);
    
    1. Optional接口
      Optional不是函数是接口,这个是用来防止NullPointerException异常的辅助类型
      Optional被定义为一个简单的容器,其值可能是null或者不是null。在Java8之前一般某个函数应该返回非空对象但是偶尔会返回null,而在Java8中,不推荐你返回null,而是返回Optional

    相关文章

      网友评论

          本文标题:JDK1.8的新特性(Lambda表达式等)(一)

          本文链接:https://www.haomeiwen.com/subject/wqydkqtx.html