Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"V is not a functional interface" error

I'm writing an elasticsearch plugin based on this tutorial: Creating an elasticsearch plugin, the basics

Here I'm getting "V is not a functional interface" error:

@Override
    public Map<String, AnalysisModule.AnalysisProvider<TokenFilterFactory>> getTokenFilters() {
        return Collections.singletonMap("jettro", MyTokenFilterFactory::new);
    }

Code for MyTokenFilterFactory:

public class MyTokenFilterFactory extends AbstractTokenFilterFactory {

public MyTokenFilterFactory(IndexSettings indexSettings, String name, Settings settings) {
    super(indexSettings, name, settings);
}

@Override
public TokenStream create(TokenStream tokenStream) {
    return new TokenFilter(tokenStream);
}
like image 651
Nariman Esmaiely Fard Avatar asked Jun 30 '26 21:06

Nariman Esmaiely Fard


1 Answers

The definition of AnalysisModule.AnalysisProvider is

public interface AnalysisProvider<T> {
    T get(IndexSettings indexSettings, Environment environment,
          String name, Settings settings) throws IOException;
    // irrelevant default methods omitted
}

To bind a constructor to this interface via method reference, it must have exactly the same signature, i.e. you would have to change the declaration to

public class MyTokenFilterFactory extends AbstractTokenFilterFactory {

    public MyTokenFilterFactory(IndexSettings indexSettings, Environment environment,
                                String name, Settings settings) {
        super(indexSettings, name, settings);
    }

    @Override
    public TokenStream create(TokenStream tokenStream) {
        return new TokenFilter(tokenStream);
    }
}

ignoring that additional parameter. If you don’t want ordinary callers to provide an unneeded parameter, you can overload the constructor

public class MyTokenFilterFactory extends AbstractTokenFilterFactory {

    public MyTokenFilterFactory(IndexSettings indexSettings, Environment environment,
                                String name, Settings settings) {
        super(indexSettings, name, settings);
    }
    public MyTokenFilterFactory(IndexSettings indexSettings,
                                String name, Settings settings) {
        super(indexSettings, name, settings);
    }

    @Override
    public TokenStream create(TokenStream tokenStream) {
        return new TokenFilter(tokenStream);
    }
}

Or you only keep your original constructor only declaring the required parameters and change the method reference to a lambda expression:

public Map<String, AnalysisModule.AnalysisProvider<TokenFilterFactory>> getTokenFilters() {
    return Collections.singletonMap("jettro",
                                    (is, env, n, s) -> new MyTokenFilterFactory(is, n, s));
}

Note that the misleading compiler error message is mostly likely due to the complex interaction between the type inference at the singletonMap invocation and the method reference. If you use an explicit type for the singletonMap invocation, i.e.

public Map<String, AnalysisModule.AnalysisProvider<TokenFilterFactory>> getTokenFilters() {
    return Collections.
        <String, AnalysisModule.AnalysisProvider<TokenFilterFactory>>
        singletonMap("jettro", MyTokenFilterFactory::new);
}

you’ll immediately get a meaningful error message about the mismatching constructor signature. As a general rule of thumb, if you get weird error messages with the newer Java language constructs, try to insert explicit types until you get understandable error messages. After fixing the problems, you may remove again the then-unnecessary explicit types.

like image 162
Holger Avatar answered Jul 04 '26 17:07

Holger