Using method references with parameters

I play with lambdas and realized that I want to try creating a simple db / object mapper as part of the training.

Yes, there are many frameworks that already do this, but it has more to do with learning, and the problem I came across is technical.

At first I wanted to define all the display logic in an enumeration.

It started simple and simple with a few field names:

enum ThingColumn {
    id, language;
}

This allows me to create the following method (implementation is not relevant) that gives the api a compilation command using columns:

public Collection<Thing> findAll(ThingColumn... columns);

After that, I would like to define more rules in the enumeration, in particular, how the results are displayed from the class java.sql.ResultSetto mine Thing.

Running simple, I created a functional interface:

@FunctionalInterface
static interface ThingResultMapper {
    void map(Thing to, ResultSet from, String column) ;
}

and added it to the listing:

enum ThingColumn {
    id((t, rs, col) -> t.setId(rs.getLong(col))), 
    language((t, rs, col) ->t.setLanguage(rs.getString(col)));

    ThingColumn(ThingResultMapper mapper){..}
}

mapResultSetRow, lambdas ResultSet:

public Thing mapResultSetRow(ResultSet rs, ThingColumn... fields) {
    Thing t = new Thing();
    Stream.of(fields)
        .forEach(f -> f.getMapper().map(t, rs, f.name()));
    return t;
}

findAll mapResultSetRow ResultSet. .

. , , . :

enum ThingColumn {
    id(ResultSet::getLong, Thing::setId), 
    language(ResultSet::getString, Thing::setLanguage);
}

, , , , /. , :

enum ThingColumn {
    id(ResultSet::getLong); // <<- compile error
    ThingColumn(Function<String,?> resultSetExtractor) {..}
}

: Cannot make a static reference to the non-static method getLong(String) from the type ResultSet.

, , , , , labmda .

: Java 8 ( , ) , .

:)

?

+4
1

, SQLException. . -, :

@FunctionalInterface
static interface ThingResultMapper {
    void map(Thing to, ResultSet from, String column) throws SQLException;
}

-, getMapper map , :

enum ThingColumn {
    id((t, rs, col) -> t.setId(rs.getLong(col))), 
    language((t, rs, col) ->t.setLanguage(rs.getString(col)));

    private ThingResultMapper mapper;

    ThingColumn(ThingResultMapper mapper){
        this.mapper = mapper;
    }

    public void map(Thing to, ResultSet from) {
        try {
            mapper.map(to, from, name());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

:

public Thing mapResultSetRow(ResultSet rs, ThingColumn... fields) {
    Thing t = new Thing();
    Stream.of(fields).forEach(f -> f.map(t, rs));
    return t;
}

, (Long, String ..). , ResultSet::getLong .. :

@FunctionalInterface
static interface ResultGetter<T> {
    T get(ResultSet from, String column) throws SQLException;
}

ResultSet (this , ResultSet.getLong - ) . , .

Thing BiConsumer<Thing, T>. (, !). BiConsumer<Thing, ResultSet>, map.

(mapResultSetRow , ):

@FunctionalInterface
static interface ResultGetter<T> {
    T get(ResultSet from, String column) throws SQLException;
}

enum ThingColumn {
    id(ResultSet::getLong, Thing::setId), 
    language(ResultSet::getString, Thing::setLanguage);

    private final BiConsumer<Thing, ResultSet> mapper;

    <T> ThingColumn(ResultGetter<T> getter, BiConsumer<Thing, T> setter) {
        this.mapper = (t, rs) -> {
            try {
                setter.accept(t, getter.get(rs, name()));
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        };
    }

    public void map(Thing to, ResultSet from) {
        this.mapper.accept(to, from);
    }
}
+4

All Articles