Functional Programming in Java - Part 1




Let us Create a simple Class which validates the email. But we will start from creating in the classical java way.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package advance.emailValidation;

import java.util.regex.Pattern;

/**
 * Created by ypokhrel on 12/27/2017.
 */
public class EmailValidator {
    private static final String EMAIL_PATTERN =
            "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
                    + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";

    final static Pattern emailPattern = Pattern.compile(EMAIL_PATTERN);

    static void testMail(String email){
        if(emailPattern.matcher(email).matches()){
            sendVerification(email);
        }else{
            logError(email);
        }
    }

    static void sendVerification(String s){
        System.out.println("Email verified and send to : " + s);
    }

    private static void logError(String s){
        System.out.println("Error Message logged : " + s);
    }

    public static void main(String[] args) {
        testMail("yubrajgmail.com");
    }
}

The above code is purely imperative. even if the testMail method has a pure effect it does not return anything but it mixes data with the processing effects. But what if we send the null as the email. Here we will get the NullPointerExcetion and the later will not even be executed.

So let's try to make it bit functional

1
2
3
4
5
6
7
8
9
static Function<String, Boolean> emailChecker = s -> emailPattern.matcher(s).matches();

    static void testMail(String email){
        if(emailChecker.apply(email)){
            sendVerification(email);
        }else{
            logError(email);
        }
    }

here we will still face the NullPointerException but until here we have done something new with Function Interface. Let's go little further. Now we will create a new Component to manage the result called Result

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package advance.emailValidation;

/**
 * Created by ypokhrel on 12/27/2017.
 */
public interface Result {
    public class Success implements Result{}
    public class Failure implements Result{
        private final String errorMessage;

        public Failure(String s){
            this.errorMessage = s;
        }

        public String getMessage(){
            return errorMessage;
        }
    }
}

and our previous class can be written as follow

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class EmailValidator {
    private static final String EMAIL_PATTERN =
            "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
                    + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";

    final static Pattern emailPattern = Pattern.compile(EMAIL_PATTERN);

    static Function<String, Result> emailChecker = s -> {
        if(s == null)
            return new Result.Failure("Email cannot be null");
        else if (s.length() == 0)
            return new Result.Failure("Email cannot be Empty");
        else if (emailPattern.matcher(s).matches())
            return new Result.Success();
        else
            return new Result.Failure("Email is invalid : "+ s);

    };

    static void validate(String email){
        Result result = emailChecker.apply(email);
        if (result instanceof Result.Success)
            sendVerification(email);
        else
            logError(((Result.Failure) result).getMessage());
    }

    static void sendVerification(String s){
        System.out.println("Email verified and send to : " + s);
    }

    private static void logError(String s){
        System.out.println("Error Message logged : " + s);
    }

    public static void main(String[] args) {
        validate("yubrajgmail.com");
        validate(null);
    }
}

This will now give us some level of satisfaction but still, its a lot of code, Casting and even more error messages. To make it more functional we can use new Interface which the only task is to execute the result message. For this let us create a new Interface called Executable

1
2
3
public interface Executable {
    void exec();
}

so our new EmailValidator can be written as
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class EmailValidator {
    private static final String EMAIL_PATTERN =
            "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
                    + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";

    final static Pattern emailPattern = Pattern.compile(EMAIL_PATTERN);

    static Function<String, Result> emailChecker = s ->
        s == null
                ? new Result.Failure("Email cannot be null")
                : s.length() == 0
                    ? new Result.Failure("Email cannot be Empty")
                    : emailPattern.matcher(s).matches()
                        ? new Result.Success()
                        : new Result.Failure("Email is invalid : "+ s);


    static Executable validate(String email){
        Result result = emailChecker.apply(email);
        return result instanceof Result.Success
                ? () -> sendVerification(email)
                : () -> logError(((Result.Failure) result).getMessage());
    }

    static void sendVerification(String s){
        System.out.println("Email verified and send to : " + s);
    }

    private static void logError(String s){
        System.out.println("Error Message logged : " + s);
    }

    public static void main(String[] args) {
        validate("yubrajgmail.com").exec();
        validate(null).exec();
    }
}

Now the validate method is much more functional because when an executable is called we can simply call it by using exec method. Also, we have replaced the if...else with a ternary operator which is much closer to the functional principle.

So that's all for now #happyCoding


Comments

Popular posts from this blog

Akka Actors in Java - Part 1

sealed keyword in Scala