Лямбда Java 8 и расширение интерфейсов с абстрактным классом

Скажем, я хочу объявить Spring RowMapper, но не создавать динамический класс, а реализовать абстрактный класс, который реализует RowMapper. Это моя подпись метода:

SqlProcedure#declareRowMapper(RowMapper<?> rowMapper);

CustomRowMapper.java:

public abstract class CustomRowMapper<T> implements RowMapper<T> {
    protected A a = new A();
}

Старый способ Java состоял бы в том, чтобы написать:

sqlProc.declareRowMapper(new CustomRowMapper<Object>() {
    @Override
    public Object mapRow(ResultSet rs, int rowNum) {
        a.doSomething(rs, rowNum);
        return new Object();
    }
});

Можно ли добиться того же с помощью лямбда-выражений? Я хотел бы сделать что-то вроде этого:

sqlProc.declareRowMapper((rs, rowNum) -> {
    a.doSomething(rs, rowNum);
    return new Object();
});

Но тогда я получил бы ошибку компиляции, говорящую a cannot be resolved. Это потому, что Java рассматривает это как реализацию метода RowMapper#mapRow, а не CustomRowMapper#mapRow.

Как указать Java использовать мой CustomRowMapper вместо RowMapper с помощью лямбда-выражений? Это вообще возможно?


person jeremija    schedule 20.06.2014    source источник


Ответы (3)


Вы можете расширить функциональный интерфейс и создать свой собственный:

@FunctionalInterface
public interface CustomRowMapper<T> implements RowMapper<T> {
    static A a = new A();
}

Затем вы можете передать лямбду, которая является реализацией метода CustomRowMapper#mapRow() следующим образом:

CustomRowMapper myCustomRowMapperLambda = (rs, rowNum) -> {
    a.doSomething(rs, rowNum);
    return new Object();
};
sqlProc.declareRowMapper(myCustomRowMapperLambda);
person Konstantin Yovkov    schedule 20.06.2014
comment
Спасибо - это выглядит как приемлемый ответ. Я также понял, что если в CustomRowMapper есть только один объект (например, A в моем примере), возможно, будет яснее объявить его перед объявлением средства сопоставления строк, например: A a = new A(); sqlProc.declareRowMapper((rs, rowNum) -> { a.doSomething(rs, rowNum); ... });, чтобы избежать ненужного создания экземпляров, если есть много строк для сопоставления. - person jeremija; 20.06.2014
comment
@nosid, спасибо за заметку. Это неявно статично, но да... чтобы избежать путаницы, я добавил ключевое слово. - person Konstantin Yovkov; 20.06.2014
comment
Конечно, a будет одним общим экземпляром для всех ваших лямбда-выражений, поэтому вы можете быть осторожны с этим решением. обратите внимание, что в вашем исходном коде A не является статическим, поэтому каждый экземпляр CustomRowMapper имеет свой собственный уникальный экземпляр A... - person Mostafa Zeinali; 24.02.2015
comment
Обратите внимание, что лямбда-выражения не ничего не наследуют от функционального интерфейса. Поскольку a является переменной public static, к ней можно получить доступ из кода лямбда-выражения, но она должна либо быть квалифицирована как CustomRowMapper.a, либо вам нужно добавить import static CustomRowMapper.a;, чтобы разрешить неквалифицированный доступ. Таким образом, нет смысла помещать a в interface, для лямбда-выражения это не лучше, чем в любом другом месте, что, в свою очередь, делает весь пользовательский interface устаревшим… - person Holger; 27.08.2015

Лямбды не могут реализовывать абстрактные классы. и вот почему.

В статье упоминается обходной путь, но он не будет работать в вашем случае, поскольку вы пытаетесь сослаться на поле объекта. Что вы можете сделать, кроме того, что придерживаетесь старомодных анонимных классов, так это подумать, как "внедрить" A в лямбду, например:

  • создать экземпляр объекта внутри лямбды;
  • создать A вне лямбда-области;
  • сделать синглтон какой-то.
person user2418306    schedule 20.06.2014

Это должно работать:

sqlProc.declareRowMapper((rs, rowNum) -> {
    new A().doSomething(rs, rowNum);
    return new Object();
});

Вам нужно будет создать экземпляр вашего объекта A. Учитывая код, который вы показываете, это ничего не изменит.

Чтобы ответить на более общий вопрос, не полагаясь исключительно на пример: даже с приведением вы не получите доступ к частной переменной A, я склонен думать, что это связано с тем, как выводятся типы лямбдов.

И на самом деле я не думаю, что это желательно для: лямбда должна быть без гражданства. (Вы можете сделать это с учетом контекста, но я бы не рекомендовал это делать).

person benzonico    schedule 20.06.2014
comment
Это не отвечает на вопрос. @jeremija спрашивает, можно ли передать пользовательскую реализацию функционального интерфейса без обходных путей, подобных этому. :) - person Konstantin Yovkov; 20.06.2014