Ссылки на 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.