Java 8 lambda et extension des interfaces avec la classe abstraite


Disons que je veux déclarer le RowMapper de Spring, mais pas créer une classe dynamique, mais implémenter une classe abstraite qui implémente RowMapper. Ceci est ma signature de méthode:

SqlProcedure#declareRowMapper(RowMapper<?> rowMapper);

CustomRowMapper.java:

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

L'ancienne façon Java serait d'écrire:

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

Est-il possible de réaliser la même chose avec des expressions lambda? Je voudrais faire quelque chose comme ceci:

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

Mais alors j'obtiendrais une erreur de compilation disant a cannot be resolved. C'est parce que Java voit cela comme un mise en œuvre de la méthode RowMapper#mapRow, pas CustomRowMapper#mapRow.

Comment dire à Java d'utiliser mon CustomRowMapper au lieu de RowMapper via des expressions lambda? Est-ce même possible?

Author: Konstantin Yovkov, 2014-06-20

3 answers

Vous pouvez étendre l'interface fonctionnelle et créer l'une des vôtres:

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

Ensuite, vous pouvez passer un lambda, qui est l'implémentation de la méthode CustomRowMapper#mapRow() comme ceci:

CustomRowMapper myCustomRowMapperLambda = (rs, rowNum) -> {
    a.doSomething(rs, rowNum);
    return new Object();
};
sqlProc.declareRowMapper(myCustomRowMapperLambda);
 9
Author: Konstantin Yovkov, 2014-06-21 14:01:14

Les lambdas ne peuvent pas implémenter les classes abstraites. Voici pourquoi.

Il y a une solution de contournement mentionnée dans l'article, mais cela ne fonctionnera pas dans votre cas lorsque vous essayez de référencer le champ object. Ce que vous pouvez faire, en plus de vous en tenir aux classes anonymes à l'ancienne, est de penser à "injecter" A dans lamda, par exemple:

  • instancier un objet dans lambda;
  • créer un ouside de portée lambda;
  • faire un singletone de quelque sorte.
 12
Author: user2418306, 2014-06-20 10:43:49

Cela devrait fonctionner:

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

Vous devrez instancier votre objet A. Étant donné le code que vous montrez, cela ne changerait rien.

Pour répondre à la question plus générale en ne se fondant pas uniquement sur l'exemple : Même avec le casting, vous n'aurez pas accès à la variable privée A, j'ai tendance à penser que cela est dû à la façon dont les types de lamdbas sont déduits.

Et en fait je ne pense pas que ce soit whisful pour : Un lambda devrait être apatride. (Vous pouvez le rendre avec état avec le contexte mais ce n'est pas le cas quelque chose que je recommande).

 3
Author: benzonico, 2014-06-20 10:04:54