JavaRush /Java блог /Random /Кофе-брейк #239. Создание аннотаций с использованием Refl...

Кофе-брейк #239. Создание аннотаций с использованием Reflection API. Java 8: Iterator против ListIterator

Статья из группы Random

Создание аннотаций в Java с использованием Reflection API

Источник: Medium В этом руководстве продемонстрирован способ создания на Java аннотации для проверки электронной почты. Кофе-брейк #239. Создание аннотаций с использованием Reflection API. Java 8: Iterator против ListIterator - 1В повседневной работе на Java мы часто сталкиваемся с аннотациями, например, с @Override. Сами по себе аннотации представляют разновидность метаданных, которые можно добавлять в исходный код Java. Классы, методы, переменные, параметры и пакеты — все они могут быть аннотированы. В этой статье я покажу, как с помощью аннотации можно проверять электронную почту. Для этого сначала создадим аннотацию Email:

@Target({ ElementType.FIELD }) 
@Retention(RetentionPolicy.RUNTIME) 
public  @interface Email { 
}
Чтобы объявить аннотацию, используйте ключевое слово @interface. В Target было объявлено, что оно будет использоваться в полях (fields), а в Retention объявлено, что оно будет использоваться во время выполнения (runtime). Теперь воспользуемся этой аннотацией. Давайте создадим класс Person с двумя полями: age и email.

public class Person {
    private String name;

    @Email
    private String email;

    public Person(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }
}
Обратите внимание, что электронное письмо снабжено аннотацией @Email. Теперь создадим класс ValidateUtil, который будет проверять электронную почту с помощью Reflection API.

public class ValidateUtil {

    public static void validate(Object object) throws IllegalAccessException {

        Class<?> class = object.getClass();
        for(Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            if(field.isAnnotationPresent(Email.class)) {
                validateEmail(field, object);
            }
        }
    }

    private static void validateEmail(Field field, Object object) throws IllegalAccessException {
        Pattern pattern = Pattern.compile("^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$");
        String email = (String) field.get(object);
        if(isNotMatching(pattern, email)) {
            throw new InvalidEmailException("Email " + email + " is invalid");
        }
    }

    private static boolean isNotMatching(Pattern pattern, String value) {
        return !pattern.matcher(value).matches();
    }
}
В методе validate он берет объект, перебирает все поля, устанавливает их как доступные и проверяет, имеет ли поле аннотацию Email. Если аннотация есть, то он вызывает метод validateEmail. В методе validateEmail проверяется, соответствует ли электронное письмо шаблону. Если они не совпадают, будет выброшено исключение. Теперь давайте создадим PersonService для имитации службы, которая будет вызывать ValidateUtil с целью проверки электронной почты и последующего вызова репозитория для сохранения в базе данных.

public class PersonService {

    public String save(Person person) {
        try {
            ValidateUtil.validate(person);
            // вызываем репозиторий для сохранения в БД
            return "Successfully saved person";
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}
Чтобы проверить эту аннотацию, создадим модульный тест с помощью jUnit.

public class PersonServiceTest {

    private PersonService service = new PersonService();

    @Test
    public void mustReturnSuccessfullySavedPerson() {
        var person = new Person("Josimar", "josimar@gmail.com");
        var message = service.save(person);
        assertEquals("Successfully saved person", message);
    }

    @Test
    public void mustThrowException() {
        var person = new Person("Josimar", "gmail.com");
        var exception = assertThrows(RuntimeException.class, () -> service.save(person));
        assertEquals("Email gmail.com is invalid", exception.getMessage());
    }
}
Тесты выполняются успешно: первый использует рабочий адрес электронной почты, а второй использует недействительный адрес электронной почты. Мы проверяем адрес электронной почты с помощью аннотаций и возможностей Reflection API.

Java 8: Iterator против ListIterator

Источник: Medium Благодаря этой публикации вы сможете лучше понять различия между Iterator и ListIterator. Iterator — это полезный инструмент в программировании, который позволяет проходить набор элементов один за другим. Он упрощает работу со списками, массивами и другими коллекциями, помогая эффективно обрабатывать и изменять элементы. Итераторы обеспечивают удобный способ перебора данных и дают вам больше контроля и гибкости в ваших задачах. Интерфейс Iterator имеет следующие методы:

public interface Iterator<E> {
 boolean hasNext();//1

 E next(); //2

 default void remove() { //3
  throw new UnsupportedOperationException("remove");
 }

 default void forEachRemaining(Consumer<? super E> var1) { //4
  Objects.requireNonNull(var1);

  while (this.hasNext()) {
   var1.accept(this.next());
  }

 }
}
Мы знаем, что модификация Iterator запрещена. Если вы заметили, что для метода remove() выдается исключение UnsupportedOperationException. Это означает, что удаление не поддерживается. Мы также знаем, что Iterator всегда однонаправленный. Давайте теперь посмотрим на ListIterator:

package java.util;

public interface ListIterator<E> extends Iterator<E> {
 boolean hasNext(); //1

 E next(); //2

 boolean hasPrevious(); //3

 E previous(); //4

 int nextIndex(); //5

 int previousIndex(); //6

 void remove(); //7

 void set(E var1); //8

 void add(E var1); //9
}
ListIterator — это двунаправленный итератор. ListIterator позволяет эффективно вставлять, удалять и заменять элементы в процессе итерации. Но есть и некоторые ограничения: Метод remove() интерфейса ListIterator используется для удаления последнего элемента из списка, который возвращается методом next() или previous(). Вышеупомянутый метод может быть вызван только в том случае, если add(E) не был вызван. Допустим, я пытаюсь выполнить что-то вроде обхода списка в обратном порядке:

List<String> names = new ArrayList<>();
  names.add("Alice");
  names.add("Bob");
  names.add("Charlie");

ListIterator<String> listIterator = names.listIterator();

while (listIterator.hasPrevious()) {
            String name = listIterator.previous();
            if(!name.equals("Avans"))
            listIterator.add("Avans");//1
            if(name.equals("Alice"))
            listIterator.remove(); //2
        }
Как вы думаете, что произойдет в строке 2 после того, как я добавлю элемент в строку 1? Мы получим следующее исключение: Кофе-брейк #239. Создание аннотаций с использованием Reflection API. Java 8: Iterator против ListIterator - 2И еще один вопрос. Являются ли итераторы списка потокобезопасными? Нет. Итераторы, возвращаемые в классе ArrayList методами iterator() и listIterator(), являются отказоустойчивыми: если список структурно изменен в любое время после создания итератора (любым способом), кроме как с помощью собственных методов итератора remove или add, то итератор выдаст ConcurrentModificationException. Но если вам все же нужно создать потокобезопасную коллекцию, то вы можете использовать оболочку, например фабричный метод: Collections.synchronizedXXX(collection). Следует отметить, что использовать их итераторы лучше всего в блоке synchronized.
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ