Использование 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
, но такого метода с такой подписью не существует. Реальный Supplier
get
метод 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());
}