简体   繁体   中英

Java8 streams collectors.ToMap Method reference failing

Can someone tell me what is the issue in following example code. For sake of example, We can assume all columns are available in resultSet and everything is a String.

Last line in below code is failing.

ResultSet rs = JdbcTemplate.query(......)

List<String> columnName= <some predefined fixed values> ;


Map<String,String> columnAndValueMap 
                = columnName
                      .stream()
                      .collect(Collectors.toMap(
                                   Function.identity(),
                                   rs::getString)); //Cannot resolve method 'getString'

Collectors.toMap expects a Function<String, String> as its second argument (the value mapper function).

Function<String, String> is a functional interface that has this single abstract method:

String apply(String arg);

However, the ResultSet.getString method has the following signature:

String getString(String columnLabel) throws SQLException;

As SQLException is a checked exception, it makes the ResultSet.getString method incompatible with the Function.apply method, hence you're getting that error.


As shown by @vphilipnyc in their answer , you can use a for loop with a try/catch block to handle your scenario. Or, if you want to stick to a more functional approach, you might declare your own functional interface, that adapts a SQLException throwing function to a common java.util.function.Function (via inheritance):

@FunctionalInterface
public interface SQLFunction<T, R> extends Function<T, R> {

    R applySQL(T t) throws SQLException;

    @Override
    default R apply(T t) {
        try {
            return applySQL(t);
        } catch (SQLException e) {
            throw new RuntimeException(e); // or your own unchecked exception
        }
    }

    static <T, R> Function<T, R> adapt(SQLFunction<T, R> f) {
        return f;
    }
}

Then, you could use it this way:

Map<String, String> columnAndValueMap = columnName.stream()
    .collect(Collectors.toMap(
        Function.identity(),
        SQLFunction.adapt(rs::getString)));

As @fps mentioned, the rs.getString() method throws a SQLException as described in its signature.

Assuming you are seeking to create a map with column names as keys and result set strings as values, you can do:

List<String> columnNames = List.of("columnA", "columnB");

Map<String, Object> map = columnNames.stream().collect(
        Collectors.toMap(Function.identity(), s -> {
            try {
                return Optional.ofNullable(rs.getString(s));
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            return s;
        }));

This is a bit messy since you need to catch the SQLException during the stream operations. Further, an encouraged practice is to use Optional since the getString() method can return a null. (Understood that you are assuming that there will be no nulls. Your IDE may highlight the lack of Optional as a warning.)

You may instead be better off using a simple for loop and surround it with a try/catch:

Map<String, String> map = new HashMap<>(columnNames.size());
for (String columnName : columnNames) {
    try {
        map.put(columnName, rs.getString(columnName));
    } catch (SQLException throwables) {
        throwables.printStackTrace(); 
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM