Search code examples
javajava-stream

A stream "cannot resolve method" that exists


Why doesn't the problematic line work? The IDE says

Cannot resolve method 'identify(String[])'"

but it's right. there

public class PersonApp {
    public static void main(String[] args) {
        if (areValidArgs(args)) {
            Operations.findAppropriateOperation(args).execute();
        }
    }

    public static boolean areValidArgs(String[] args) {
        return args.length > 0;
    }
}
@Component
public class Operations {
    private static Operation[] operations; // let Spring inject all beans of type Operation
    
    @Autowired
    public void setOperations(Operation[] operations) {
        Operations.operations = operations;
    } 

    public static Operation findAppropriateOperation(String[] args) {
        Optional<Operation> operationOptional = Arrays.stream(operations)
                .findFirst(op -> op.identify(args)); // this line is problematic
/*
If I change the statement above to

        Optional<Operation> operationOptional = Arrays.stream(operations)
                .filter(op -> op.identify(args))
                .findFirst();

it works, but I can't stop thinking why the initial code didn't
*/
        return operationOptional
                .orElseThrow(() -> new IllegalArgumentException("Unsupported argument"));
    }
}
@Component
@RequiredArgsConstructor
public abstract class Operation {
    protected final PersonService personService;

    public abstract boolean identify(String[] args); // here!
    public abstract void execute();
}
// sample operation

@Component
public class CreatePersonTableOperation extends Operation {
    public CreatePersonTableOperation(PersonService personService) {
        super(personService);
    }

    @Override
    public boolean identify(String[] args) {
        return args.length == 1 && args[0].equals("1");
    }

    @Override
    public void execute() {
        personService.createPersonTable();
    }
}

I thought maybe Arrays.stream() returns a stream of Objects (for example, stream.toArray() returns an array of Objects so streams and arrays don't get along very well), but I checked the docs, and it doesn't

public static <T> Stream<T> stream(T[] array)

Returns a sequential Stream with the specified array as its source.

Type Parameters:

T - The type of the array elements

Parameters:

array - The array, assumed to be unmodified during use

Returns:

a Stream for the array


Solution

  • While void Operation#identify(String[]) exists, Optional<T> Stream#findFirst(Predicate<? super T>) does not.

    Stream#findFirst() is parameterless method.

    Since there is no parameter, the Java compiler cannot infer the type of the lambda op parameter. It can only do that if there is a SAM type available to match against.

    If you want to find the first item based on a given predicate, you have to filter the stream first:

    Optional<Operation> operationOptional = Arrays.stream(operations)
            .filter(op -> op.identify(args))
            .findFirst();