Java企业教程系列

使用Java8的Lambda实现Monad

  Monad是函数语言(Cojure或Scala)中的设计模式概念, 那么现在为什么在Java中变得如此重要?因为Java从版本8以后引入了新的Lambda特性,Lambda或闭包是函数语言的特征,它允许你使用代码块作为一个变量,并且让你将它传递。

首先我们假设一个不使用Monad的场景:空值Null检查:

public static class Userdetails{

    public Address address;

    public Name name;

    

}

 

public static class Name{

    public String firstName;

    public String lastName;      

}

 

public static class Address{

    public String houseNumber;

    public Street street;

    public City city;

}

 

public static class Street{

    public String name;      

}

 

public static class City{

    public String name;      

}

现在你要从UserDetails访问其street接到名称,有可能是空的,不用Monad你也许是如下进行检查:

if(user == null )

    return null;

else if(user.address == null)

    return null;

else if(user.address.street == null)

    return null;

else

    return user.address.street.name;

这是非常琐碎和噪音的。

让我们创建一个Option类代表一个可选值,然后有一个围绕其运行lambda的map方法,该方法返回另外一个可选值。如果围绕的值是一个null, 它会返回一个Option,其中包含null, 这样避免空指针出现,注意map方法需要使用一个lambda作为参数,但是我们需要首先创建一个接口SingleArgExpression 支持它。因为在Java8中使用函数接口表达lambda:

 

public interface SingleArgExpression<P, R> {

    

    public R function(P param);

 

}

准备好了函数接口,这样我们可以将SingleArgExpression的lambda作为参数传递了,下面是Option.java,关键是其中的map方法,该方法参数是SingleArgExpression的lambda。

public class Option<T> {

    T value;

    public Option(T value){

        this.value = value;

    }

//核心重要方法map 如果是空就不应用lambda,否则就可以使用lambda

    public <E> Option<E> map(SingleArgExpression<T,E> mapper){

        if(value == null){

            return new Option<E>(null);

        }else{

            return new Option<E>(mapper.function(value));

        }

    }  

 

    @Override

    public boolean equals(Object rhs){

        if(rhs instanceof Option){

            Option o = (Option)rhs;

            if(value == null)

                return (o.value==null);

            else{

                return value.equals(o.value);

            }

        }else{

            return false;

        }

    }

 

    @Override

    public int hashCode(){

        return value==null? 0 : value.hashCode();

    }

 

    public T get(){

        return value;

    }

}

下面我们重新编写Userdetails如下:

public class OptionExample{

    public static class Userdetails{

        public Option<Address> address = new Option<>(null);

        public Option<Name> name = new Option<>(null);

    }

 

    public static class Name{

        public Option<String> firstName = new Option<String>(null);

        public Option<String> lastName = new Option<String>(null);      

    }

 

    public static class Address{

        public Option<String> houseNumber;

        public Option<Street> street;

        public Option<City> city;

    }

 

    public static class Street{

        public Option<String> name;      

    }

 

    public static class City{

        public Option<String> name;      

    }

 

    public static void main(String [] args){

        Option<Userdetails> userOpt =  new Option<>(new Userdetails());

 

        //现在变得很简单了。

        String streetName = userOpt.flatMap(user -> user.address).map(address -> address.street).map(street -> street.name).get();

        System.out.println(streetName);

    }

 

}

核心代码如下:

String streetName = userOpt.flatMap(user -> user.address).map(address -> address.street).map(street -> street.name).get();

这样就不用担心返回空值抛出空指针错误了。

现在UserDetails的所有方法都是返回一个Option了,这确保方法的使用者了解返回值可能会为空。

为了避免核心代码中每次都调用get(), 我们使用类似flatMap替代map:

public <E> Option<E> flatMap(SingleArgExpression<T, Option<E>> mapper){

    if(value == null){

        return new Option<E>(null);

    }

    return  mapper.function(value);

    

}

上面方法其实是一个过滤器,它让我们放入一个if条件在map链中, 这样只有当条件为真是获得一个值,注意这也是空安全null-safe。

 

Closure Lambda和Monad

Java 8教程

使用Java8的Lambda实现模板模式

函数编程