Создание строкового представления лямбда-выражения

В целях отладки я пытаюсь создать строковые представления лямбда-выражений (в частности, Predicates, хотя это было бы интересно и для других лямбда-выражений) в Java 8. Моя идея будет примерно такой:

public class Whatever {

    private static <T> String predicateToString(Predicate<T> predicate) {
        String representation = ... // do magic
        return representation;
    }

    public static void main(String[] args) {
        System.out.println(Whatever.<Integer>predicateToString(i -> i % 2 == 0));
    }

}

И вывод будет i -> i % 2 == 0 (или что-то логически эквивалентное). Метод toString(), похоже, бесполезен, на выходе получается что-то вроде com.something.Whatever$$Lambda$1/1919892312@5e91993f (чего, я думаю, следует ожидать, поскольку toString() не переопределяется).

Я не уверен, возможно ли что-то подобное, например. с отражением, я, конечно, пока ничего не смог найти на нем. Любые идеи?


person blalasaadri    schedule 16.05.2014    source источник
comment
Отладочные цели? Вы не можете просто посмотреть на источник?   -  person Sotirios Delimanolis    schedule 17.05.2014
comment
Для этого потребуется декомпилятор.   -  person SLaks    schedule 17.05.2014
comment
@SotiriosDelimanolis Конечно, я всегда могу посмотреть на источник. Но было бы неплохо иметь и печатное представление.   -  person blalasaadri    schedule 17.05.2014
comment
Тогда нельзя просто скопировать исходник?   -  person Jeroen Vannevel    schedule 17.05.2014
comment
@JeroenVannevel Вы имеете в виду просто каждый раз писать строковое представление вручную? Возможно, конечно. Но не очень элегантно.   -  person blalasaadri    schedule 17.05.2014
comment
У вас могут быть макросы в Java (#define). Это нехорошо, но вы можете определить предикат, который в то же время также создает строку того же содержания. Я бы не стал этого делать.   -  person Trilarion    schedule 17.05.2014
comment
@Trilarion Я посмотрю на это, но звучит довольно уродливо. Однако для меня новость, что Java поддерживает что-то похожее на макросы прекомпилятора C.   -  person blalasaadri    schedule 17.05.2014
comment
@blalasaadri Технически макросы не поддерживаются самой Java, но вы всегда можете запустить исходный код Java через препроцессор C, чтобы получить тот же эффект.   -  person awksp    schedule 17.05.2014
comment
@ user3580294 Верно. Но определенно не то, что я хотел бы сделать или заставить делать кого-то еще, кто использует мой код. ;-)   -  person blalasaadri    schedule 17.05.2014


Ответы (1)


Самое простое, что я мог придумать, это создать «именованный предикат», который дает вашим предикатам имя или описание, в основном все, что будет полезно в качестве toString:

public class NamedPredicate<T> implements Predicate<T> {
    private final String name;
    private final Predicate<T> predicate;

    public NamedPredicate(String name, Predicate<T> predicate) {
        this.name = name;
        this.predicate = predicate;
    }

    @Override
    public boolean test(T t) {
        return predicate.test(t);
    }

    @Override
    public String toString() {
        return name;
    }

    public static void main(String... args) {
        Predicate<Integer> isEven = new NamedPredicate<>("isEven", i -> i % 2 == 0);
        System.out.println(isEven); // prints isEven
    }
}

Возможно, предоставление вашим предикатам имен или описаний, подобных этому, также делает код, в котором вы их используете, более понятным:

Stream.of(1, 2, 3, 4)
        .filter(isEven)
        .forEach(System.out::println);

Более странная идея может состоять в том, чтобы получить «структурное» описание предиката, т. е. каков результат для некоторых заданных входных данных? Очевидно, что это будет работать лучше всего, когда входной набор конечен и мал (например, для перечислений, логических значений или какого-либо другого ограниченного набора), но я думаю, вы могли бы попробовать небольшой набор "случайных" целых чисел для целочисленных предикатов:

private static Map<Boolean, List<Integer>> testPredicate(Predicate<Integer> predicate) {
    return Stream.of(-35, -3, 2, 5, 17, 29, 30, 460)
            .collect(Collectors.partitioningBy(predicate));
}

Для isEven это вернет что-то вроде {false=[-35, -3, 5, 17, 29], true=[2, 30, 460]}, что, я думаю, не обязательно яснее, чем если бы вы вручную дали им описание, но, возможно, полезно для предикатов, которые не находятся под вашим контролем.

person andersschuller    schedule 16.05.2014
comment
Хотя это должно работать, красота синтаксиса лямбда-выражения здесь частично теряется, поскольку объект-оболочка NamedPredicate должен быть создан с помощью оператора new. - person blalasaadri; 17.05.2014
comment
@blalasaadri Верно. Я думаю, вы могли бы скрыть некоторые уродства, такие как new и <>, вместо этого создав статический фабричный метод (см., например, все фабричные методы коллекции Guava). Я также добавил еще одну идею для определения описания сорта из любого предиката постфактум — я не уверен, что это полезно, но, тем не менее, может быть интересно изучить! - person andersschuller; 17.05.2014
comment
Конечно, фабричный метод улучшит ситуацию. Все еще не идеально, но из-за отсутствия программного способа извлечения выражения это, вероятно, самое элегантное решение. - person blalasaadri; 18.05.2014
comment
Я попытался обобщить решение. Посмотрите на stackoverflow.com/a/42876841/1325574 общую реализацию. - person Sebastian; 18.03.2017