Java8之Lambda

Java8之Lambda
   本篇用代码的方式并结合自己对Lamba的理解讲了Java8的Lambda表达式及函数式接口相关。
   参考: http://winterbe.com/posts/2014/03/16/java-8-tutorial/

   1. 接口中扩展方法(default关键字)

package  com.mavsplus.java8.turtorial.one;

/**
 * 公式接口
 * <p>
 * Java 8 允许我们使用default关键字,为接口声明添加非抽象的方法实现。这个特性又被称为扩展方法
 * 
 *  @author  landon
 *  @since  1.8.0_25
 
*/
public  interface  Formula {
     public  double  calculate( int  a);

     /**
     * 默认的非抽象方法(default关键字)
     * 
     *  @param  a
     *  @return
     
*/
     default  double  sqrt( int  a) {
         return  Math.sqrt(a);
    }

     /**
     * 增加了public 关键字
     * 
     *  @param  a
     *  @return
     
*/
     public  default  double  sin( int  a) {
         return  Math.sin(a);
    }

     /**
     * 测试该特性
     * <p>
     * Run As Java Application的时候会出现两个选择,因为除了Formula接口类,还有一个内部类Formula$1
     * 
     *  @param  args
     
*/
     public  static  void  main(String  args) {
         //  匿名内部类的方式
        Formula formula =  new  Formula() {

            @Override
             public  double  calculate( int  a) {
                 //  调用了默认的非抽象方法
                 return  sqrt(a * 100);
            }
        };

        System.out.println(formula.calculate(144));
         //  直接调用默认的非抽象方法
        System.out.println(formula.sqrt(225));
    }
}

    2. Lamba表达式小试牛刀

package  com.mavsplus.java8.turtorial.lambda;

import  java.util.Arrays;
import  java.util.Collections;
import  java.util.Comparator;
import  java.util.List;

/**
 * 字符串列表排序,使用java8之前实现及java8 lambda实现
 * 
 *  @author  landon
 *  @since  1.8.0_25
 
*/
public  class  ListOfStringSort {

     private  List<String> names = Arrays.asList("landon", "kobe", "dirk", "tmac");

     /**
     * 旧版本实现,使用匿名内部类
     
*/
     public  void  oldVersionSort() {
        Collections.sort(names,  new  Comparator<String>() {

            @Override
             public  int  compare(String o1, String o2) {
                 return  o2.compareTo(o1);
            }
        });
    }

     /**
     * 使用Lambda表达式实现1
     * <p>
     * 从语法上看,(String o1, String o2)为Comparator接口中compare方法的两个参数,没有方法名; -> 为方法的实现
     * 此表达式返回一个Comparator对象
     * (编译器知道Collection.sort第二个参数是一个Comparator接口实现的对象,而该接口只有一个方法compare
     * ->个人认为编译器解释该lambda表达式的时候也会转为匿名内部类的方式实现)
     
*/
     public  void  lambdaSort1() {
        Collections.sort(names, (String o1, String o2) -> {
             return  o2.compareTo(o1);
        });
    }

     /**
     * 使用Lambda表达式实现2
     * <p>
     * 这个实现更加简单,较第一个版本没有了大括号和return(个人认为因为编译器能推断出返回类型)
     
*/
     public  void  lambdaSort2() {
        Collections.sort(names, (String o1, String o2) -> o2.compareTo(o1));
    }

     /**
     * 使用Lambda表达式实现3
     * <p>
     * 这是是最简单的实现,类型都可以省略不写.因为Java编译器能够自动识别参数的类型(注,用ide编辑代码的时候,敲o2.的时候,
     * 自动会出现String的方法_智能提示)
     
*/
     public  void  lambdaSort3() {
        Collections.sort(names, (o1, o2) -> o2.compareTo(o1));
    }
}

    3. 函数式接口(@FunctionalInterface)

package  com.mavsplus.java8.turtorial.lambda;

/**
 * Lambda函数式接口
 * 
 * <pre>
 * Lambda表达式如何匹配Java的类型系统?
 * 每一个lambda都能够通过一个特定的接口,与一个给定的类型进行匹配。
 * 一个所谓的函数式接口必须要有且仅有一个抽象方法声明,每个与之对应的lambda表达式必须要与抽象方法的声明相匹配。
 * 由于默认方法不是抽象的,因此你可以在你的函数式接口里任意添加默认方法。
 * 任意只包含一个抽象方法的接口,我们都可以用来做成lambda表达式。
 * 为了让你定义的接口满足要求,你应当在接口前加上@FunctionalInterface标注。
 * 编译器会注意到这个标注,如果你的接口中定义了第二个抽象方法的话,编译器会抛出异常。
 * </pre>
 * 
 * <pre>
 * @FunctionalInterface public interface Runnable {}
 * @FunctionalInterface public interface Comparator<T> {}
 * 
 * 从JDK 1.8的源码可以看到,一些常用接口都加上了@FunctionalInterface关键字,即都是函数式接口->都可以用Lambda表达式进行匹配
 * </pre>
 * 
 *  @author  landon
 *  @since  1.8.0_25
 
*/
public  class  LambdaFunctionalInterface {

     /**
     * 自定义的一个函数式接口.->转换器,将F(From)类型转为T(Target)类型
     *
     *  @param  <F>
     *  @param  <T>
     
*/
    @FunctionalInterface
     public  interface  Converter<F, T> {
        T convert(F from);
    }

     public  static  void  main(String  args) {
         //  使用lamba表达式1,(from)为convert参数列表 -> 方法实现
        Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
        System.out.println(converter.convert("123"));

         //  使用lambda表达式2,这里加上了大括号和返回值,这样更直观一些,->左边为参数列表,右边为方法实现
        Converter<String, Integer> converter2 = (String from) -> {
             return  Integer.valueOf(from);
        };
        System.out.println(converter2.convert("456"));

         //  使用lambda表达式3,这里和第一个的区别是加上了参数类型
        Converter<String, Integer> converter3 = (String from) -> Integer.valueOf(from);
        System.out.println(converter3.convert("789"));

         //  注:本例的Converter接口也可以不加@FunctionalInterface这个注解,因为内部本身只有一个抽象方法
    }
}

    4. :: 关键字获取方法或者构造函数的的引用(::)

package  com.mavsplus.java8.turtorial.lambda;

import  com.mavsplus.java8.turtorial.lambda.LambdaFunctionalInterface.Converter;

/**
 * 方法和构造函数引用
 * 
 * <pre>
 * Java 8 允许你通过::关键字获取方法或者构造函数的的引用
 * </pre>
 * 
 *  @author  landon
 *  @since  1.8.0_25
 
*/
public  class  MethodConstructorRef {
     /**
     * 引用静态方法
     
*/
     public  void  refStaticMethod() {
         //  使用静态方法引用::,赋值(个人认为编译器会处理一切-,>因为是函数式接口)
        
//  该种方式是相当于通过右侧方法引用实现了函数式接口的实现.
        Converter<String, Integer> converter = Integer::valueOf;
        System.out.println(converter.convert("365"));
    }

     /**
     * 定义一个内部类
     
*/
     public  class  Something {
         /**
         * 定义了一个参数是字符串,返回值也是字符串的方法
         * 
         *  @param  s
         *  @return
         
*/
         public  String startsWith(String s) {
             return  String.valueOf(s.charAt(0));
        }
    }

     /**
     * 对一个对象的方法进行引用
     
*/
     public  void  refObjectMethod() {
        Something something =  new  Something();

         //  将something对象的startsWith方法引用赋值::
        
//  该种方式是相当于通过右侧方法引用实现了函数式接口的实现.
        Converter<String, String> converter = something::startsWith;
        System.out.println(converter.convert("Lambda"));
    }

     /**
     * 自定义的一个JavaBean
     
*/
     public  class  Person {
         private  String firstName;
         private  String lastName;

         public  Person() {

        }

         public  Person(String fName, String lName) {
            firstName = fName;
            lastName = lName;
        }

         public  String getFirstName() {
             return  firstName;
        }

         public  String getLastName() {
             return  lastName;
        }
    }

     /**
     * 自定义了一个接口,接口内部有两个字符串参数的方法
     *
     *  @param  <P>
     
*/
     public  interface  PersonFactory<P  extends  Person> {
         public  P create(String firstName, String lastName);
    }

     /**
     * 通过::对构造方法进行引用
     * Person::new来创建一个Person类构造函数的引用。Java编译器会自动地选择合适的构造函数来匹配PersonFactory
     * .create函数的签名,并选择正确的构造函数形式
     
*/
     public  void  refConstructor() {
        PersonFactory<Person> factory = Person:: new ;

        Person p = factory.create("kobe", "bryant");

        System.out.println(p.getFirstName() + "_" + p.getLastName());
    }

     /**
     * 这里是想测试一下,如果接口内两个抽象方法,能否使用方法引用匹配,因为Person确实是有两个构造方法
     * 结论:不行,方法引用只能是functional
     * interface.(个人认为这样也是非常合理的->因为右侧的方法引用只能是确定的一个方法,不可能匹配对个抽象方法
     * --虽然构造方法可以有多个,但毕竟是
     * ::的特例,特例不能决定全部,而从实现的角度来看,会大大加大编译器的处理难度,而且只能是针对引用构造方法对多个抽象方法进行特殊处理
     * .——————当然同一个方法也有多种不同的重载形式
     * ,不过会大大加大编译器的处理难度-------另一个原因我认为可能是为了要和lambda表达式处理一致
     * .lambda表达式本来就是要处理简单,它不可能在一个表达式中实现多个抽象方法)
     
*/
     public  interface  PersonFactory2<P  extends  Person> {
         public  P create(String firstName, String lastName);

         public  P create();
    }

     //  这里出现编译错误:The target type of this expression must be a functional
    
//  interface
     public  void  refConstructor2() {
         //  PersonFactory2<Person> factory = Person::new;
    }
    
     public  static  void  main(String[] args) {
        MethodConstructorRef ref =  new  MethodConstructorRef();
        
        ref.refStaticMethod();
        ref.refObjectMethod();
        ref.refConstructor();
    }
}


    5. Lambda表达式访问范围
    
package  com.mavsplus.java8.turtorial.lambda;

import  com.mavsplus.java8.turtorial.lambda.LambdaFunctionalInterface.Converter;

/**
 * Lambda的范围
 * <p>
 * 对于lambdab表达式外部的变量,其访问权限的粒度与匿名对象的方式非常类似。你能够访问局部对应的外部区域的局部final变量_enclosing(
 * 注_针对局部变量, 而对于外部类变量[成员变量]则无此限制 by landon),以及成员变量和静态变量
 * 
 *  @author  landon
 *  @since  1.8.0_25
 
*/
public  class  LambdaRange {
     /**
     * Lambda访问Final局部变量
     
*/
     public  void  lambdaAccessLocalFinalVar() {
         final  int  num = 1;

         //  这里直接访问num
        Converter<Integer, String> converter = (from) -> String.valueOf(from + num);

         //  输出3
        System.out.println(converter.convert(2));
    }

     /**
     * Lambda访问非Final局部变量
     * <p>
     * 但是与匿名对象不同的是,变量num并不需要一定是final.
     
*/
     public  void  lambdaAccessLocalNonFinalVar() {
         int  num2 = 2;

         //  这里直接访问num2
        
//  num2在编译的时候被隐式地当做final变量来处理
        Converter<Integer, String> converter = (from) -> String.valueOf(from + num2);

         //  输出4
        System.out.println(converter.convert(2));
    }

     /**
     * Lambda尝试改变非final局部变量
     * <p>
     * 在lambda表达式内部企图改变num3的值也是不允许的(因为隐式做final处理)
     
*/
     public  void  lambdaModifyLocalNonFinalVar() {
         int  num3 = 3;

        Converter<Integer, String> converter = (from) -> String.valueOf(from + num3);
        converter.convert(3);

         //  加上这句赋值,修改了非final局部变量num3,则编译错误
        
//  报编译错误:Local variable num3 defined in an enclosing scope must be
        
//  final or effectively final
        
//  num3 = 4;
    }

     private  int  outerNum = 4;
     private  static  int  outerStaticNum = 5;

     /**
     * Lambda访问成员变量
     * <p>
     * 与局部变量不同,我们在lambda表达式的内部能获取到对成员变量或静态变量的读写权。这种访问行为在匿名对象里是非常典型的 ---此规则同匿名对象
     
*/
     public  void  lambdaAccessMemberAndStaticVar() {
         //  访问外部成员变量
        Converter<Integer, String> converter1 = (from) -> {
            outerNum = 8;
             return  String.valueOf(from);
        };

        converter1.convert(4);

         //  访问外部静态成员变量
        Converter<Integer, String> converter2 = (from) -> {
            outerStaticNum = 11;
             return  String.valueOf(from);
        };

        converter2.convert(5);
    }

     /**
     * lambda访问默认方法
     * <p>
     * 默认方法无法在lambda表达式内部被访问
     
*/
     public  void  lambdaAccessDefaultInterfaceMethod() {
         //  Formula是一个接口,接口中一个抽象的calculate方法和一个默认方法sqrt
        
//  这里编译错误,The method sqrt(int) is undefined for the type LambdaRange
        
//  从上面的编译错误可以看出,编译器根本是不知道sqrt这个方法的存在,它在当前类中也没有找到该方法的声明
        
//  这样做是非常合理的->编译器第一步是要先确认-> 右边的这个方法表达式是否存在(不可能先去接口里是不是默认方法->实现复杂度加大)
        
//  而用匿名类对象当然可以,因为匿名类就是实现了该接口并继承了默认方法,按照类继承的规则当然可以进行访问
        
//  Formula formula = (a) -> sqrt(a * 100);
    }

     public  static  void  main(String[] args) {
        LambdaRange range =  new  LambdaRange();

        range.lambdaAccessLocalFinalVar();
        range.lambdaAccessLocalNonFinalVar();

        System.out.println("pre_OuterNum:" + range.outerNum);
        System.out.println("pre_OuterStaticNum:" + outerStaticNum);

         //  此方法会改变成员变量和静态变量的值_lambda表达式对其进行了修改
        range.lambdaAccessMemberAndStaticVar();

        System.out.println("post_OuterNum:" + range.outerNum);
        System.out.println("post_OuterStaticNum:" + outerStaticNum);
    }
}

    6. Java8内置的函数式接口

package  com.mavsplus.java8.turtorial.lambda;

import  java.util.Comparator;
import  java.util.Objects;
import  java.util.Optional;
import  java.util.function.Consumer;
import  java.util.function.Function;
import  java.util.function.Predicate;
import  java.util.function.Supplier;

import  com.mavsplus.java8.turtorial.lambda.MethodConstructorRef.Person;

/**
 * Java8内置函数式接口
 * 
 * <pre>
 * JDK 1.8 API中包含了很多内置的函数式接口。有些是在以前版本的Java中大家耳熟能详的,例如Comparator接口,或者Runnable接口。
 * 对这些现成的接口进行实现,可以通过@FunctionalInterface 标注来启用Lambda功能支持。 
 * 此外,Java 8 API 还提供了很多新的函数式接口,来降低程序员的工作负担。有些新的接口已经在Google Guava库中很有名了。
 * 如果你对这些库很熟的话,你甚至闭上眼睛都能够想到,这些接口在类库的实现过程中起了多么大的作用.
 * </pre>
 * 
 * <p>
 * 可以搜索FunctionalInterface的引用,发现诸如Runnable,Comparator,Callbale等接口在JDK8中均加了该注解。
 * 另外可以看到在java.util中增加了一个function
 * package,即java.util.function,该package即内置了很多函数式接口.
 * 
 *  @author  landon
 *  @since  1.8.0_25
 
*/
public  class  LambdaBuildInFunctionalInterface {

     /**
     * Predicate是一个布尔类型的函数,该函数只有一个输入参数。Predicate接口包含了多种默认方法,用于处理复杂的逻辑动词(and,
     * or,negate_(not)),可以查看其源码
     * 
     * @FunctionalInterface public interface Predicate<T>{ boolean test(T t);
     
*/
     public  void  usePredicate() {
         //  实现了抽象方法test
        Predicate<String> predicate = (s) -> s.length() > 0;

         //  调用了test方法
        
//  输出:true
        System.out.println(predicate.test("landon"));

         //  default Predicate<T> negate() {return (t) -> !test(t);}
        
//  可以看到negate方法是一个默认方法,其实现是返回一个!test的Predicate接口
        
//  输出:false
        System.out.println(predicate.negate().test("landon"));

         //  Objects是Java7引入的util中的一个类
        
//  public static boolean nonNull(Object obj) { return obj != null;}
        Predicate<Boolean> nonNull = Objects::nonNull;
         //  public static boolean isNull(Object obj) {return obj == null;}
        Predicate<Boolean> isNull = Objects::isNull;

         //  输出:true
        System.out.println(nonNull.test( true ));
         //  输出:false
        System.out.println(nonNull.test( null ));
         //  输出:false
        System.out.println(isNull.test( false ));
         //  输出:true
        System.out.println(isNull.test( null ));

         //  这里看到,即使isEmpty方法是String的非静态方法,这里也可以通过::进行调用.
        Predicate<String> isEmpty = String::isEmpty;
        Predicate<String> isNotEmpty = isEmpty.negate();

         //  输出:true
        System.out.println(isEmpty.test(""));
         //  输出:false
        System.out.println(isEmpty.test("lambda"));
         //  输出:false
        System.out.println(isNotEmpty.test(""));
         //  输出:true
        System.out.println(isNotEmpty.test("lambda"));
    }

     /**
     * Function接口接收一个参数,并返回单一的结果。默认方法可以将多个函数串在一起(compse, andThen)
     * 
     * @FunctionalInterface public interface Function<T, R> {R apply(T t);
     
*/
     public  void  useFunction() {
         //  实现apply接口
        Function<String, Integer> toInteger = Integer::valueOf;
         //  default <V> Function<T, V> andThen(Function<? super R, ? extends V>
        
//  after) {Objects.requireNonNull(after);return (T t) ->
        
//  after.apply(apply(t));}

        
//  andthen方法将apply的的输出作为再一次apply的输入,toInteger的apply输出为Integer->
        
//  参数fucntion为Function<Integer,String>
        Function<String, String> backToString = toInteger.andThen(String::valueOf);

         //  输出:123
        System.out.println(backToString.apply("123"));
    }

     /**
     * Supplier接口产生一个给定类型的结果。与Function不同的是,Supplier没有输入参数。
     * 
     * @FunctionalInterface public interface Supplier<T> {T get();}
     
*/
     public  void  useSupplier() {

         //  编译错误: No enclosing instance of the type MethodConstructorRef is
        
//  accessible in scope
        
//  因为Person属于内部类,其正常的初始化方式应该是先初始化外部类,所以这里并非直接用::new,否则违背了内部类的规则
        
//  Supplier<Person> personSupplier = Person::new;

        Supplier<Person> personSupplier =  new  PersonSupplier();

        System.out.println(personSupplier.get().getFirstName());
        System.out.println(personSupplier.get().getLastName());

         //  这里直接用::new的方式
        Supplier<SupplierBean> beanSupplier = SupplierBean:: new ;

        System.out.println(beanSupplier.get().a);
    }

     //  注:因为Person是MethodConstructorRef的内部类,所以无法使用::new进行初始化
    
//  所以这里实现了Supplier接口,并采用内部类的方式进行初始化
     private  class  PersonSupplier  implements  Supplier<Person> {
        @Override
         public  Person get() {
            MethodConstructorRef ref =  new  MethodConstructorRef();
             return  ref. new  Person("java8", "tutorial");
        }
    }

     /**
     * 测试Supplier接口而实现的一个JavaBean
     
*/
     private  class  SupplierBean {
         public  int  a = 10086;
    }

     /**
     * Consumer代表了在一个输入参数上需要进行的操作
     * 
     * @FunctionalInterface public interface Consumer<T> {void accept(T t);
     
*/
     public  void  useConsumer() {
         //  实现了accept方法,该方法为void
        Consumer<Person> greeter = (p) -> System.out.println("Hello," + p.getFirstName());

         //  首先实例化外部类
        MethodConstructorRef ref =  new  MethodConstructorRef();
        greeter.accept(ref. new  Person("Java8", "Lambda"));
    }

     /**
     * 使用加了@FunctionalInterface的Comparator. Java 8 为这个接口添加了不同的默认方法
     
*/
     public  void  useNewComparator() {
         //  实现compare接口
        Comparator<Person> comparator = (p1, p2) -> p1.getFirstName().compareTo(p2.getFirstName());

         //  首先实例化外部类
        MethodConstructorRef ref =  new  MethodConstructorRef();

        Person p1 = ref. new  Person("tracy", "mcgrady");
        Person p2 = ref. new  Person("kobe", "bryant");

        System.out.println(comparator.compare(p1, p2));

         //  default Comparator<T> reversed() {return
        
//  Collections.reverseOrder(this);}
        
//  reversed方法为Comparator接口中的一个默认方法.
        System.out.println(comparator.reversed().compare(p1, p2));
    }

     /**
     * Optional不是一个函数式接口,而是一个精巧的工具接口,用来防止NullPointerEception产生。
     * Optional是一个简单的值容器,这个值可以是null,也可以是non-null。考虑到一个方法可能会返回一个non-null的值,
     * 也可能返回一个空值。为了不直接返回null,我们在Java 8中就返回一个Optional。
     * 
     * public final class Optional<T>
     
*/
     public  void  useOptional() {
         //  public static <T> Optional<T> of(T value) { return new
        
//  Optional<>(value);}
        Optional<String> optional = Optional.of("landon");

         //  public boolean isPresent() {return value != null;}
        System.out.println(optional.isPresent());
         //  public T get() {if (value == null) {throw new
        
//  NoSuchElementException("No value present");}return value;}
        System.out.println(optional.get());
         //  public T orElse(T other) {return value != null ? value : other;}
        System.out.println(optional.orElse("mavs"));

         //  public void ifPresent(Consumer<? super T> consumer) {if (value !=
        
//  null)consumer.accept(value);}
        optional.ifPresent((s) -> System.out.println(s.charAt(0)));
    }

     public  static  void  main(String[] args) {
        LambdaBuildInFunctionalInterface buildInFunctionalInterface =  new  LambdaBuildInFunctionalInterface();

        buildInFunctionalInterface.usePredicate();
        buildInFunctionalInterface.useFunction();
        buildInFunctionalInterface.useSupplier();
        buildInFunctionalInterface.useConsumer();
        buildInFunctionalInterface.useNewComparator();
        buildInFunctionalInterface.useOptional();
    }
}

你可能感兴趣的:(Java8之Lambda)