0

A class (from Elasticsearch Rest API) has a protected method that I want to invoke. The problem is that this method has method reference parameters :

protected <Req extends ActionRequest, Resp> Resp performRequestAndParseEntity(
            Req request,
            CheckedFunction<Req, Request, IOException> requestConverter,
            CheckedFunction<XContentParser, Resp, IOException> entityParser,
            Set<Integer> ignores, Header... headers) throws IOException {
    return performRequest(request, requestConverter, (response) ->
        parseEntity(response.getEntity(), entityParser), ignores, headers);
}

In the API, this method is called that way:

DeleteIndexResponse deleteIndexResponse = restHighLevelClient.performRequestAndParseEntity(
    deleteIndexRequest,
    Request::deleteIndex,
    DeleteIndexResponse::fromXContent,
    Collections.emptySet(),
    headers
);

Java tells me that "The target type of this expression must be a functional interface" for DeleteIndexRequest::deleteIndex and DeleteIndexResponse::fromXContent.

My (not working) solution:

java.lang.Class clazz = restHighLevelClient.getClass();
java.lang.reflect.Method performRequestAndParseEntity = clazz.getDeclaredMethod(
    "performRequestAndParseEntity",
    Object.class,
    org.elasticsearch.common.CheckedFunction.class,
    org.elasticsearch.common.CheckedFunction.class,
    java.util.Set.class,
    org.apache.http.Header.class
);
performRequestAndParseEntity.setAccessible(true);

org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse 
deleteIndexResponse = (org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse)
    performRequestAndParseEntity.invoke(
        restHighLevelClient,
        deleteByIndexRequest,
        org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest::deleteIndex,
        org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse::fromXContent,
        java.util.Collections.emptySet(),
        null
    )
;

EDIT

I also tried to cast deleteIndex (and fromXContent) to CheckedFunction, i.e. calling in invoke:

performRequestAndParseEntity.invoke(
    ...,
   ((org.elasticsearch.common.CheckedFunction) org.elasticsearch.client.Request::deleteIndex),
    ...
);

I also tried to define a variable outside the invoke call:

org.elasticsearch.common.CheckedFunction deleteIndexFunction =
    org.elasticsearch.client.Request::deleteIndex;

And:

org.elasticsearch.common.CheckedFunction deleteIndexFunction =
    (org.elasticsearch.common.CheckedFunction) org.elasticsearch.client.Request::deleteIndex;

For all, Java is telling that: "The type Request does not define deleteIndex(Object) that is applicable here", and the same for fromXContent

For information, deleteIndex is defined that way:

static Request deleteIndex(DeleteIndexRequest deleteIndexRequest) {
    ...
}

And CheckedFunction is:

@FunctionalInterface
public interface CheckedFunction<T, R, E extends Exception> {
    R apply(T t) throws E;
}

EDIT 2
I also tried to parametrize the CheckedFunction Object with similar same result ("The type Request does not define deleteIndex(DeleteIndexRequest) that is applicable here"):

org.elasticsearch.common.CheckedFunction<org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest, org.elasticsearch.client.Request, Exception> deleteIndexFunction =
        (org.elasticsearch.common.CheckedFunction<org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest, org.elasticsearch.client.Request, Exception>) org.elasticsearch.client.Request::deleteIndex;

I think that the problem is the deleteIndex function is not public.

1 Answers1

0

The signature of Method.invoke declares all arguments as Object regardless of what the actual argument types are. This means that Object is the type that the compiler is trying to turn your method reference into an implementation of, which can't work because Object is not a functional interface.

To fix this, cast each method reference to the type it's actually supposed to be - org.elasticsearch.common.CheckedFunction. The compiler will pick this up as the intended type of the method reference, allowing it to work.

Douglas
  • 5,017
  • 1
  • 14
  • 28
  • Thanks for your answer. I edited my question with those tests. With my last try (defining outside variables), `invoke` seems OK but the variable definition fails with "The type Request does not define deleteIndex(Object) that is applicable here" – Kévin Darty Oct 27 '17 at 23:35