Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 lambda and extension of interfaces with abstract class

Say I want to declare Spring's RowMapper, but not create a dynamic class, but implement an abstract class which implements RowMapper. This is my method signature:

SqlProcedure#declareRowMapper(RowMapper<?> rowMapper);

CustomRowMapper.java:

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

The old Java way would be to write:

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

Is it possible to achieve the same thing with lambda expressions? I would like to do something like this:

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

But then I would get a compile error saying a cannot be resolved. It's because Java sees this as an implementation of the RowMapper#mapRow method, not CustomRowMapper#mapRow.

How do I tell Java to use my CustomRowMapper instead of RowMapper via lambda expressions? Is this even possible?

like image 403
jeremija Avatar asked Jun 20 '14 09:06

jeremija


3 Answers

Lambdas can't implement abstract classes. Here is why.

There is workaround mentioned in the article, but it won't work in your case as you trying to reference object field. What you can do, besides sticking to old-fashion anonymous classes, is to think how to "inject" A into lamda, for example:

  • instantiate A object inside lambda;
  • create A ouside of lambda scope;
  • make A singletone of some sort.
like image 155
user2418306 Avatar answered Nov 02 '22 06:11

user2418306


You can extend the functional interface and create one of your own:

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

Then, you can pass a lambda, which is the implementation of CustomRowMapper#mapRow() method like this:

CustomRowMapper myCustomRowMapperLambda = (rs, rowNum) -> {
    a.doSomething(rs, rowNum);
    return new Object();
};
sqlProc.declareRowMapper(myCustomRowMapperLambda);
like image 26
Konstantin Yovkov Avatar answered Nov 02 '22 06:11

Konstantin Yovkov


This should work:

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

You would have to instantiate your Object A. Given the code you show this would not change a thing.

To answer to the more general question not solely relying on example : Even with the cast you won't access to private variable A, I tend to think that is due to how types of lamdbas are infered.

And in fact I don't think it is whisful for : A lambda should be stateless. (You can make it stateful with the context but it is not something I would recommend).

like image 27
benzonico Avatar answered Nov 02 '22 07:11

benzonico