Search This Blog

Thursday, 24 September 2015

The Function Interface

I worked with using lambdas for Comparator interface in my first post on Lambdas. We saw how a Function interface could be used to convert an object of one type into an object of another.
This class is used to extract the compare key for the comparator method.
users.sort(Comparator.comparing(User::getAge));
The method reference will result in a Function instance that converts an User instance to a String instance (user.getAge())
The Collections framework  has utilized this concept in their computeIfAbsent method:
Below is a use case with the code
public static void main(String[] args) {      
      Function<Ticket, String> ticketKeyValFn = ticket -> ticket.toString();
      Map<Ticket, String> ticketMap = new HashMap<>();
      ticketMap.put(getSpecialTicket(), "For Purchase Operation");
      Ticket ticket = new Ticket();
      //this ticket is not in map
      System.out.println("Map contains ticket " + ticket + " ? " + ticketMap.containsKey(ticket));
      ticketMap.computeIfAbsent(ticket, ticketKeyValFn);
      //now ticket is in map
      System.out.println("Map contains ticket " + ticket + " ? " + ticketMap.containsKey(ticket));
            
   }
The output is as below:
Map contains ticket T[1159190947] ? false
Map contains ticket T[1159190947] ? true
If I simply wanted to generate values for ticket than the code would be simply:
String value = ticketKeyValFn.apply(new Ticket());
Thus we have seen that Functions serve as transformers converting one type to another. We could also use it for more complicate operations once we combine this with streams.

There are also some more utility methods provided in the Function interface. Consider the compose method which allows us to apply another function to the input, and then applies the code inside our function.
Ticket ticket = new Ticket();

//This is the before function
Function<Ticket, String> ticketKeyValFn = ticketInstance -> ticketInstance.toString();
String sampleOpt = ticketKeyValFn.apply(ticket);
System.out.println("Output of first function is " + sampleOpt);

Function<String, Long> cleanTicketValFn = inp -> Long.valueOf(inp.substring(2,inp.length()-1));
Long no = cleanTicketValFn.apply(sampleOpt);
System.out.println("Output of second function is " + no);

//If using composites
//cleanTicketValFn = the one that executes last - gets a string and returns a Long
Function<Ticket, Long> compositeFn = cleanTicketValFn.compose(ticketKeyValFn);
System.out.println("The ticket Id is " + compositeFn.apply(ticket));
The output:
Output of first function is T[617901222]
Output of second function is 617901222
The ticket Id is 617901222
As can be seen, I have defined two functions - one that transforms a Ticket into a String and the second that transforms a String into a Long. Using compose we have combined the two.
The transformation is as below:
input : Ticket
Stage 1: Ticket -> String ( the 'before' function)
Stage 2: String -> Long
output: Long
There is also a way to combine, by adding a post function - or a function that runs after our function:
Function<String, Long> toLongFunction = string -> Long.valueOf(string);
Function<Long, Integer> toIntegerCutoffFunction = longVal -> longVal.intValue();
      //Now combine the two
Function<String, Integer> compositeFunction = toLongFunction.andThen(toIntegerCutoffFunction);
      // Test call
System.out.println(compositeFunction.apply("1234"));
Here first the String -> Long transformation happens and then Long -> Integer transformation.

No comments:

Post a Comment