11

Problem Description: I want to be able to use an ArrayList of Functions passed in from another class (where the Functions have been defined in that other class). If the list of Functions, which may have different input and return types, are defined in one class, I want to be able to pass an ArrayList of some of them, with possible duplicates, as a parameter to some other class's constructor or method and perform operations using them.

Code Description: The code below is a greatly simplified example which is not intended to make much sense from a design perspective. The focus of the problem is the method getResult() within SomeClass and generally how to use an ArrayList of Functions once you have them.

Attempt to solve the problem: The getResult() method implementation is an example of one of many attempts to use the Function list. Again, please don't mind the design of the code. It was just done that way to try to make the problem example as short as possible.

Simple tester class

package com.Testing;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Function;

public class Tester {

    public static void main(String[] args)
    {
        // Some functions
        Function<Integer, Integer> increment = (Integer input) -> {
            return input + 1;
        };

        Function<Integer, Integer> decrement = (Integer input) -> {
            return input - 1;
        };

        Function<Double, Double> timesPi = (Double input) -> {
            return input * 3.14;
        };

        // list of Functions
        List<Function> availableMathOperations = new ArrayList<>();
        List<Function> selectedMathOperations = new ArrayList<>();

        // populate master list
        availableMathOperations.add(increment);
        availableMathOperations.add(decrement);
        availableMathOperations.add(timesPi);

        // Populate random selection list //
        //    generate random binary number
        Random randomGenerator = new Random();
        int randomNumber = randomGenerator.nextInt(availableMathOperations.size() * 2);
        boolean[] bits = new boolean[availableMathOperations.size()];
        for (int j = 0; j < availableMathOperations.size(); j++) {
            bits[availableMathOperations.size() - 1 - j] = (1 << j & randomNumber) != 0;
        }

        // add math operations to selectedMathOperations based on binary number
        for (int j = 0; j < bits.length; j++) {
            if (bits[j]){
                selectedMathOperations.add(availableMathOperations.get(j));
            }
        }

        SomeClass someClass = new SomeClass(selectedMathOperations, 1.23);

    }
}

Other class

package com.Testing;

import java.util.List;
import java.util.function.Function;

public class SomeClass {

    List<Function> operations;
    double initialValue;

    public SomeClass(List<Function> newOperations, double newInitialValue){
        operations = newOperations;
        initialValue = newInitialValue;
    }

    public double getResult(){
        double result = 0.0;

        // problem method
        // perform the random list of operations using the initial value initially
        for(int i = 0; i < operations.size(); i++){
            if(i == 0)
                result = operations.get(i)(initialValue);
            else
                result += operations.get(i)(result);
        }
        return result;
    }

}
2
  • 1
    And what is the question? What is wrong with the code? What do you want it to do that it doesn't do? Commented May 16, 2015 at 9:55
  • 3
    You are using raw functions here (I mean, how you declared your Lists). So if you call myFunction.apply() from the List, it will takes an Object as parameter and return an Object... So use generics here too. Commented May 16, 2015 at 9:55

2 Answers 2

10

The method of a java.util.function.Function object is apply. You need to call it like this:

operations.get(i).apply(initialValue)

However you use raw Function and therefore the result could be Object and you'd need to convert it to the appropriate type. Also you can't use the + (or the +=) operator with it. I'd suggest restricting the parameter types with Number:

List<Function<Number, ? extends Number>> operations = Arrays.asList(
        num ->  num.intValue() + 1,
        num -> num.intValue() - 1,
        num -> num.doubleValue() * 3.14
        ); // just an example list here 

public double getResult() {
    double result = 0.0;

    for (int i = 0; i < operations.size(); i++) {
        if (i == 0) {
            result = operations.get(i).apply(initialValue).doubleValue();
        } else {
            result += operations.get(i).apply(result).doubleValue();
        }
    }
    return result;
}
Sign up to request clarification or add additional context in comments.

4 Comments

How would this be done if one of the Functions accepted a List<List<T>> and returned a List<List<T>>?
@John: What are you trying to do with these functions? You certainly can't do arithmetic operations with lists.
Right, the original question is just a simplified version of the real project. Regardless of the fact that the example just deals with math operations, how would you use a function that had a list of lists of generic objects as its return type and parameter?
@John: You'd use Function<List<List<T>>, List<List<T>>> and use a object f of that type like this: List<List<T>> functionResult = f.apply(parameter);, where parameter is a List<List<T>> object. Maybe you should read the javadoc of Function. You use functional interfaces just the same way you'd use any other instance of that type, even if you created those objects using lambda expressions. Hint: If input and output type are the same, UnaryOperator can be used too.
0

I'm not convinced I understand correctly, but I think the problem you're facing is how to call the functions in your List? The JavaDoc for the Function interface states that it has a single non-abstract method, apply() that you call to use the Function, as follows:

public double getResult(){
    double result = 0.0;

    for(int i = 0; i < operations.size(); i++){
        if(i == 0)
            result = operations.get(i).apply(initialValue);
        else
            result += operations.get(i).apply(result);
    }
    return result;
}

As an aside, that method could be tidied up a bit to make it simpler:

public double getResult() {
    double result = initialValue;

    //Not sure if this if-statement is a requirement, but it is to replicate
    //the functionality in the question
    if (operations.isEmpty()) {
        return 0.0;
    }

    for (Operation operation : operations) {
        result = operation.apply(result);
    }

    return result;
}

And as others have said in the comments, you should use generics when passing around your List objects (I guess you'll have to use something like List<Function<? extends Number, ? extends Number>)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.