Использование Java-поставщика / функции для передачи метода в статический метод

У меня есть пара классов:

public class TextContent {
    private String externalId;
}

public class ImageContent {
    private String externalImageId;
}

public static void validateImageInput(List<ImageContent> imageAssets, String requestId) {
    if(CollectionUtils.isEmpty(imageAssets)) {
        throw some Error;
    }

    Set<String> uniqueIds = imageAssets.stream().map(ImageContent::externalImageId).collect(Collectors.toSet());
    if(uniqueIds.size() != imageAssets().size()) {
        throw some Error;
    }

    //Do some processing
}

public static void validateTextInput(List<TextContent> textAssets, String requestId) {
    if(CollectionUtils.isEmpty(textAssets)) {
        throw some Error;
    }

    Set<String> uniqueIds = textAssets.stream().map(ImageContent::externalId).collect(Collectors.toSet());
    if(uniqueIds.size() != textAssets().size()) {
        throw some Error;
    }

    //Do some processing
}

Как видите, часть проверки одинакова для обоих этих классов. И я хотел попытаться сделать это распространенным методом. Для этого:

public static void validateInput(List<?> assets, String requestId, Supplier<String> mapper) {
    if(CollectionUtils.isEmpty(assets)) {
        throw some error;
    }

    Set<String> uniqueIds = assets.stream().map(x -> mapper.get()).collect(Collectors.toSet());
    if(uniqueIds.size() != assets().size()) {
        throw some Error;
    }
}

а затем позвоните с помощью:

public static void validateAllInputs(List<ImageContent> imageAssets, List<TextContent> textAssets, String requestId) {
    validateInput(imageAssets, requestId, ImageContent::externalImageId);
    validateInput(textAssets, requestId, TextContent::externalId);
    doSomeProcessingWithText(textAssets, requestId);
    doSomeProcessingWithImage(imageAssets, requestId);
}

Но я получаю сообщение об ошибке Non static method cannot be referenced from static context.

Изменить: другой вариант, который я пытался использовать Function то есть я прохожу в <TextContent, String> mapper и в моем потоке я использую .map(x -> mapper.apply(x), Однако, когда я пытаюсь передать его в функцию validateInputs(textAsset, requestId, TextContent::externalId) Я получаю ту же ошибку Non static method cannot be referenced from static context.

3 ответа

Решение

Сделано это с помощью дженериков:

public static <T> void validateInput(List<T> assets, String requestId, Function<T, String> mapper) {
    if(CollectionUtils.isEmpty(assets)) {
        throw some error;
    }

    Set<String> uniqueIds = assets.stream().map(mapper).collect(Collectors.toSet());
    if(uniqueIds.size() != assets().size()) {
        throw some Error;
    }
}

А затем позвоните, используя:

validateInput(imageAssets, requestId, ImageContent::externalImageId);

Попробуйте это,

validateInput(imageAssets, requestId, new ImageContent()::getExternalImageId);

Вот причина этого. Ссылки на методы для нестатических методов требуют, чтобы экземпляр работал. Но у вас нет этого экземпляра здесь. Вы буквально не имеете неявного объекта здесь, следовательно, объект должен быть предоставлен явно. Это именно то, что я сделал. Проверьте этот пост для получения дополнительной информации об этом.

Обновление: что касается вашего последнего комментария, я не имею ни малейшего представления о том, что вы делаете в этом методе. Но этот синтаксис можно изменить так:

Set<String> uniqueIds = assets.stream().map(ignored -> mapper.get()).collect(Collectors.toSet());

В соответствии с тем, как вы написали его, используя ссылки на методы, это ошибка компилятора.

The type Supplier<String> does not define get(capture#2-of ?) that is applicable here

Это означает, что вы передаете захваченное значение, которое вы неявно отобразили в get метод Supplier, но такого метода с такой подписью не существует. Реальный Supplierget метод no-args, Хотя сообщение об ошибке компилятора может быть немного загадочным.

Обновление: исправление исключения NullPointerException

Измени свой ImageContent как это.

public class ImageContent {
    private final String externalImageId;

    public ImageContent(String externalImageId) {
        super();
        this.externalImageId = externalImageId;
    }

    public String getExternalImageId() {
        return externalImageId;
    }

}

Затем измените свой код так,

validateInput(imageAssets, requestId, new ImageContent("test")::getExternalImageId);

Тогда это должно работать. Дело в том, что вы не инициализируете значение. Возможно, вы используете анти-паттерн Java beans с геттерами и сеттерами. Вместо этого передайте значение через конструктор и сделайте класс неизменным.

Я бы предложил, чтобы оба типа реализовывали один и тот же интерфейс:

public interface Content {
    public String getExternalId();

    public static String externalId(Content content) {
        return content == null ? null : content.getExternalId();
    }
}

Затем измените validateInput подпись к:

public static void validateInput(List<? extends Content> assets, String requestId) {
    if(CollectionUtils.isEmpty(assets)) {
        throw some error;
    }

    Set<String> uniqueIds = assets.stream().map(Content::externalId).collect(Collectors.toSet());
    if(uniqueIds.size() != assets.size()) {
        throw some error;
    }

    //Do some processing
}

И если вы все еще хотите извлечь общий метод здесь, вы можете написать такой:

public static <E> void commonValidate(List<E> assets, String requestId, Function<E, String> mapper) {
    Set<String> uniqueIds = assets.stream().map(mapper).collect(Collectors.toSet());
}
Другие вопросы по тегам