private <R> Optional<RowMapper<R>> createSpecializedRowMapper(StatementContext ctx,
                                               List<String> columnNames,
                                               List<ColumnNameMatcher> columnNameMatchers,
                                               List<String> unmatchedColumns, Function<T, R> postProcessor) {
        final List<FieldData> fields = new ArrayList<>();

        for (Class<?> aType = type; aType != null; aType = aType.getSuperclass()) {
            for (Field field : aType.getDeclaredFields()) {
                Nested nested = field.getAnnotation(Nested.class);
                if (Modifier.isStatic(field.getModifiers()) || !JdbiAnnotations.isMapped(field)) {
                    continue;
                }

                if (nested == null) {
                    String paramName = addPropertyNamePrefix(prefix, paramName(field));

                    findColumnIndex(paramName, columnNames, columnNameMatchers, () -> debugName(field))
                        .ifPresent(index -> {
                            QualifiedType<?> fieldType = QualifiedType.of(field.getGenericType())
                                .withAnnotations(ctx.getConfig(Qualifiers.class).findFor(field));
                            ColumnMapper<?> mapper = ctx.findColumnMapperFor(fieldType)
                                .orElse(getDefaultColumnMapper());
                            fields.add(new FieldData(field, new SingleColumnMapper<>(mapper, index + 1)));
                            unmatchedColumns.remove(columnNames.get(index));
                        });
                } else {
                    String nestedPrefix = addPropertyNamePrefix(prefix, nested.value());

                    if (anyColumnsStartWithPrefix(columnNames, nestedPrefix, columnNameMatchers)) {
                        Optional<? extends RowMapper<?>> nestedMapper;
                        if (field.getType().equals(Optional.class)) {
                            Class<?> rawType = GenericTypes.findGenericParameter(field.getGenericType(), Optional.class)
                                .map(GenericTypes::getErasedType)
                                .orElseThrow(() -> new IllegalArgumentException(
                                    format("Could not determine the type of the Optional field %s", field.getName())));
                            nestedMapper = nestedMappers
                                .computeIfAbsent(field, f -> new FieldMapper<>(rawType, nestedPrefix))
                                .createSpecializedRowMapper(ctx, columnNames, columnNameMatchers, unmatchedColumns, Optional::ofNullable);
                        } else {
                            nestedMapper = nestedMappers
                                .computeIfAbsent(field, f -> new FieldMapper<>(field.getType(), nestedPrefix))
                                .createSpecializedRowMapper(ctx, columnNames, columnNameMatchers, unmatchedColumns, Function.identity());
                        }

                        nestedMapper.ifPresent(mapper ->
                                fields.add(new FieldData(field, mapper)));
                    }
                }
            }
        }

        if (fields.isEmpty() && !columnNames.isEmpty()) {
            return Optional.empty();
        }

        fields.sort(Comparator.comparing(f -> f.propagateNull ? 1 : 0));

        final ReflectionMappers reflectionConfig = ctx.getConfig(ReflectionMappers.class);
        fields.forEach(fieldData ->
                reflectionConfig.makeAccessible(fieldData.field));

        final Constructor<T> constructor;
        try {
            constructor = reflectionConfig.makeAccessible(type.getDeclaredConstructor());
        } catch (ReflectiveOperationException e) {
            throw new IllegalArgumentException(format("A type, %s, was mapped which was not instantiable", type.getName()), e);
        }
        RowMapper<R> boundMapper = new BoundFieldMapper<>(constructor, fields, postProcessor);
        OptionalInt propagateNullColumnIndex = locatePropagateNullColumnIndex(columnNames, columnNameMatchers);

        if (propagateNullColumnIndex.isPresent()) {
            return Optional.of(new NullDelegatingMapper<>(propagateNullColumnIndex.getAsInt() + 1, boundMapper));
        } else {
            return Optional.of(boundMapper);
        }
    }
Returns a mapper for the given bean class

@param <T> the type to map
@param type the mapped class
@param prefix the column name prefix for each mapped field
@return a mapper for the given bean class
core/src/main/java/org/jdbi/v3/core/mapper/reflect/FieldMapper.java