Я экспериментировал с новым Lambdas в Java 8, и я ищу способ использовать отражение в классах лямбда, чтобы получить возвращаемый тип лямбда-функции. Меня особенно интересуют случаи, когда лямбда реализует общий суперинтерфейс. В приведенном ниже примере кода MapFunction<F, T>
является общим суперинтерфейсом, и я ищу способ узнать, какой тип привязан к общему параметру T
.
В то время как Java сбрасывает много общей информации типа после компилятора, подклассы (и анонимные подклассы) общих суперклассов и общих суперинтерфейсов сохраняли эту информацию типа. Благодаря отражению эти типы были доступны. В приведенном ниже примере (случай 1) отражение говорит мне, что реализация MyMapper
MapFunction
связывает java.lang.Integer
с параметром типового типа T
.
Даже для подклассов, которые сами по себе являются универсальными, существуют определенные способы выяснить, что связывается с общим параметром, если известны некоторые другие. Рассмотрим пример 2 в приведенном ниже примере, IdentityMapper
, где оба F
и T
привязаны к одному типу. Когда мы это знаем, мы знаем тип F
, если мы знаем тип параметра T
(что в моем случае мы делаем).
Вопрос в том, как я могу реализовать что-то подобное для Java 8 lambdas? Поскольку они на самом деле не являются регулярными подклассами общего суперинтерфейса, описанный выше метод не работает.
В частности, могу ли я понять, что parseLambda
связывает java.lang.Integer
с T
, а identityLambda
связывает то же самое с F
и T
?
PS: Теоретически можно декомпилировать лямбда-код, а затем использовать встроенный компилятор (например, JDT) и коснуться его вывода типа. Я надеюсь, что есть более простой способ сделать это: -)
/**
* The superinterface.
*/
public interface MapFunction<F, T> {
T map(F value);
}
/**
* Case 1: A non-generic subclass.
*/
public class MyMapper implements MapFunction<String, Integer> {
public Integer map(String value) {
return Integer.valueOf(value);
}
}
/**
* A generic subclass
*/
public class IdentityMapper<E> implements MapFunction<E, E> {
public E map(E value) {
return value;
}
}
/**
* Instantiation through lambda
*/
public MapFunction<String, Integer> parseLambda = (String str) -> { return Integer.valueOf(str); }
public MapFunction<E, E> identityLambda = (value) -> { return value; }
public static void main(String[] args)
{
// case 1
getReturnType(MyMapper.class); // -> returns java.lang.Integer
// case 2
getReturnTypeRelativeToParameter(IdentityMapper.class, String.class); // -> returns java.lang.String
}
private static Class<?> getReturnType(Class<?> implementingClass)
{
Type superType = implementingClass.getGenericInterfaces()[0];
if (superType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) superType;
return (Class<?>) parameterizedType.getActualTypeArguments()[1];
}
else return null;
}
private static Class<?> getReturnTypeRelativeToParameter(Class<?> implementingClass, Class<?> parameterType)
{
Type superType = implementingClass.getGenericInterfaces()[0];
if (superType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) superType;
TypeVariable<?> inputType = (TypeVariable<?>) parameterizedType.getActualTypeArguments()[0];
TypeVariable<?> returnType = (TypeVariable<?>) parameterizedType.getActualTypeArguments()[1];
if (inputType.getName().equals(returnType.getName())) {
return parameterType;
}
else {
// some logic that figures out composed return types
}
}
return null;
}