Ссылки на Java 8: проверка методов во время компиляции

Я хотел бы использовать новые ссылки на методы Java 8 для обеспечения большей проверки некоторого кода во время компиляции.

Скажем, у меня есть метод validateMethod, который требует одного параметра: "метод", подлежащий проверке. Например:

validateMethod(foo, "methodA");

Здесь метод будет проверять, что foo#methodA() существует во время выполнения.

Используя ссылки на методы, я бы хотел:

validateMethod(foo::methodA);

Таким образом, существование метода будет проверяться во время компиляции.

Проблема в том, что, по-видимому, ссылки на методы имеют для функционального интерфейса. Например, это:

Object dummy = foo::methodA;

Генерирует ошибку: "Целевой тип этого выражения должен быть функциональным интерфейсом".

Если я создаю функциональный интерфейс, который имеет совместимую подпись с методом methodA, он работает:

@FunctionalInterface
public interface MyFunctionalInterface
{
    public String run();
}
MyFunctionalInterface dummy = foo::methodA;

Теперь существование foo#methodA() проверяется во время компиляции, чего я хочу!

Но...

Скажем, validateMethod не знает подписи метода, который он должен проверить. Можно ли еще реализовать его?

Предположим, что мы не заботимся о двусмысленности и перегруженных методах. Возможно ли в Java 8 реализовать какой-то метод, который приведет к проверке любой ссылки на метод?

Например:

public class Foo
{
    public String methodA()
    {
        return "methodA";
    }

    public String methodB(String str)
    {
        return "methodB";
    }

    public String methodC(String str, int nbr)
    {
        return "methodC";
    }
}

Foo foo = new Foo();
validateMethod(foo::methodA); // Compile
validateMethod(foo::methodB); // Compile
validateMethod(foo::methodC); // Compile
validateMethod(foo::methodD); // Error!

Можно ли реализовать validateMethod таким образом, чтобы любая ссылка на метод была принята, поэтому существование метода будет проверено во время компиляции?

Я пробовал:

public void validateMethod(Object obj){}

Но это не работает: "Целевой тип этого выражения должен быть функциональным интерфейсом"

Это будет работать:

@FunctionalInterface
public interface MyFunctionalInterface
{
    public String run();
}
public void validateMethod(MyFunctionalInterface param){}

Но только для methodA класса Foo, поскольку его подпись (без параметра) совместима с сигнатурой метода функционального интерфейса!

Можно ли реализовать функциональный интерфейс MyFunctionalInterface таким образом, чтобы любая ссылка метода была допустимым параметром и поэтому была бы проверена во время компиляции?

Какие-либо другие способы проверки существования метода во время компиляции?

Ответы

Ответ 1

Кажется, вы пытаетесь использовать ссылки на методы, которые на самом деле являются короткими руками для лямбда-выражений, как литералы метода, которые являются синтаксическими ссылками на методы ( как Foo.class - синтаксическая ссылка на экземпляр класса Foo). Эти два варианта не совпадают, и именно по этой причине вы сталкиваетесь с импедансом. Все, что вы пытаетесь, это злоупотребление языковой функцией, которая javac-компилятор полностью сопротивляется.

К сожалению, в Java нет литералов метода, поэтому вам придется описывать метод другими способами, например. Reflection, MethodHandles.Lookup и т.д. Я думаю, что очень легко придумать рефлексивную проверку для такого рода вещей или даже создать обработчик аннотации, чтобы проверить наличие данных методов во время компиляции.

Ответ 2

Вы можете попробовать что-то вроде следующего:

public class Validate {
    public String methodA() { return "methodA"; }
    public String methodB(String s) { return "methodB"; }
    public String methodC(String s, int n) { return "methodC"; }

    public static void main(String[] args) {
        Validate foo = new Validate();
        validateMethod(foo::methodA);
        validateMethod(foo::methodB);
        validateMethod(foo::methodC);
    }

    private interface Func0 { void method(); }
    private interface Func1<T> { void method(T t); }
    private interface Func2<T, U> { void method(T t, U u); }
    private interface Func3<T, U, V> { void method(T t, U u, V v); }

    public static void validateMethod(Func0 f) { }
    public static <T> void validateMethod(Func1<T> f) { }
    public static <T, U> void validateMethod(Func2<T, U> f) { }
    public static <T, U, V> void validateMethod(Func3<T, U, V> f) { }
}

Но вам нужно предоставить интерфейс и перегрузку validateMethod для каждой степени метода, который необходимо проверить. Кроме того, он не будет работать, если метод проверки будет перегружен, если вы не добавите явное выражение:

    // if there are two methodA's:
    public String methodA() { return "methodA"; }
    public String methodA(long x) { return "methodA"; }

        validateMethod(foo::methodA); // this doesn't work
        validateMethod((Func0)foo::methodA); // this does
        validateMethod((Func1<Long>)foo::methodA); // so does this

Ответ 3

будет interface Method { public Object runMethod(Object... args); } работать? единственная потенциальная проблема, которую я вижу, - это методы, которые имеют дело с примитивными типами, но, возможно, они могут быть автоматически обновлены до Double/Long, у них действительно нет запущенного компилятора java8.