«1. Обзор

Google Guava предоставляет библиотеки с утилитами, облегчающими разработку Java. В этом руководстве мы рассмотрим новые функции, представленные в выпуске Guava 18.

2. Служебный класс MoreObjects

В Guava 18 был добавлен класс MoreObjects, который содержит методы, не имеющие эквивалентов в java.util.Objects.

Начиная с версии 18, он содержит только реализации метода toStringHelper, которые можно использовать для создания собственных методов toString.

    toStringHelper(Class\u003c?\u003e clazz) toStringHelper(Object self) toStringHelper(String className)

Обычно toString() используется, когда вам нужно вывести некоторую информацию об объекте. Обычно он должен содержать сведения о текущем состоянии объекта. Используя одну из реализаций toStringHelper, вы можете легко создать полезное сообщение toString().

Предположим, у нас есть объект User, содержащий несколько полей, которые необходимо записать при вызове toString(). Мы можем использовать метод MoreObjects.toStringHelper(Object self), чтобы сделать это легко.

public class User {

    private long id;
    private String name;

    public User(long id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
            .add("id", id)
            .add("name", name)
            .toString();
    }
}

Здесь мы использовали метод toStringHelper(Object self). С помощью этой настройки мы можем создать пробного пользователя, чтобы увидеть результат, который получается при вызове toString().

User user = new User(12L, "John Doe");
String userState = user.toString();
// userState: User{ id=12,name=John Doe }

Две другие реализации вернут одну и ту же строку, если настроены одинаково:

toStringHelper(Class\u003c?\u003e clazz)

@Override
public String toString() {
    return MoreObjects.toStringHelper(User.class)
        .add("id", id)
        .add("name", name)
        .toString();
}

toStringHelper(String className)

@Override
public String toString() {
    return MoreObjects.toStringHelper("User")
        .add("id", id)
        .add("name", name)
        .toString();
}

Разница между эти методы очевидны, если вы вызываете toString() для расширений класса User. Например, если у вас есть два типа пользователей: администратор и игрок, они будут производить разные выходные данные.

public class Player extends User {
    public Player(long id, String name) {
        super(id, name);
    }
}

public class Administrator extends User {
    public Administrator(long id, String name) {
        super(id, name);
    }
}

Если вы используете toStringHelper(Object self) в классе User, то ваш Player.toString() вернет «Player{id=12, name=John Doe}». Однако, если вы используете toStringHelper(String className) или toStringHelper(Class\u003c?\u003e clazz), Player.toString() вернет «User{id=12, name=John Doe}». Указанное имя класса будет родительским классом, а не подклассом.

3. Новые методы в FluentIterable

3.1. Обзор

FluentIterable используется для работы с экземплярами Iterable в виде цепочки. Давайте посмотрим, как это можно использовать.

Предположим, у вас есть список объектов User, определенный в приведенных выше примерах, и вы хотите отфильтровать этот список, чтобы включить только пользователей в возрасте 18 лет и старше.

List<User> users = new ArrayList<>();
users.add(new User(1L, "John", 45));
users.add(new User(2L, "Michelle", 27));
users.add(new User(3L, "Max", 16));
users.add(new User(4L, "Sue", 10));
users.add(new User(5L, "Bill", 65));

Predicate<User> byAge = user -> user.getAge() >= 18;

List<String> results = FluentIterable.from(users)
                           .filter(byAge)
                           .transform(Functions.toStringFunction())
                           .toList();

Результирующий список будет содержать информацию для Джона, Мишель и Билла.

3.2. FluentIterable.of(E[])

С помощью этого метода. вы можете создать FluentIterable из массива Object.

User[] usersArray = { new User(1L, "John", 45), new User(2L, "Max", 15) } ;
FluentIterable<User> users = FluentIterable.of(usersArray);

Теперь вы можете использовать методы интерфейса FluentIterable.

3.3. FluentIterable.append(E…)

Вы можете создать новый FluentIterable из существующего FluentIterable, добавив к нему дополнительные элементы.

User[] usersArray = {new User(1L, "John", 45), new User(2L, "Max", 15)};

FluentIterable<User> users = FluentIterable.of(usersArray).append(
                                 new User(3L, "Sue", 23),
                                 new User(4L, "Bill", 17)
                             );

Как и ожидалось, размер результирующего FluentIterable равен 4.

3.4. FluentIterable.append(Iterable\u003c? extends E\u003e)

Этот метод ведет себя так же, как и в предыдущем примере, но позволяет добавить все содержимое любой существующей реализации Iterable в FluentIterable.

User[] usersArray = { new User(1L, "John", 45), new User(2L, "Max", 15) };

List<User> usersList = new ArrayList<>();
usersList.add(new User(3L, "Diana", 32));

FluentIterable<User> users = FluentIterable.of(usersArray).append(usersList);

Как и ожидалось, размер результирующего FluentIterable равен 3.

3.5. FluentIterable.join(Joiner)

Метод FluentIterable.join(…) создает строку, представляющую все содержимое FluentIterable, объединенное заданной строкой.

User[] usersArray = { new User(1L, "John", 45), new User(2L, "Max", 15) };
FluentIterable<User> users = FluentIterable.of(usersArray);
String usersString = users.join(Joiner.on("; "));

Переменная usersString будет содержать выходные данные вызова метода toString() для каждого элемента FluentIterable, разделенные символом «;». Класс Joiner предоставляет несколько вариантов соединения строк.

4. Hashing.crc32c

Хеш-функция — это любая функция, которую можно использовать для сопоставления данных произвольного размера с данными фиксированного размера. Он используется во многих областях, таких как криптография и проверка на наличие ошибок в передаваемых данных.

Метод Hashing.crc32c возвращает HashFunction, реализующую алгоритм CRC32C.

int receivedData = 123;
HashCode hashCode = Hashing.crc32c().hashInt(receivedData);
// hashCode: 495be649

5. InetAddresses.decrement(InetAddress)

Этот метод возвращает новый InetAddress, который будет «на один меньше», чем его вход.

InetAddress address = InetAddress.getByName("127.0.0.5");
InetAddress decrementedAddress = InetAddresses.decrement(address);
// decrementedAddress: 127.0.0.4

6. Новые исполнители в MoreExecutors

6.1. Обзор потоков

«В Java вы можете использовать несколько потоков для выполнения работы. Для этого в Java есть классы Thread и Runnable.

ConcurrentHashMap<String, Boolean> threadExecutions = new ConcurrentHashMap<>();
Runnable logThreadRun = () -> threadExecutions.put(Thread.currentThread().getName(), true);

Thread t = new Thread(logThreadRun);
t.run();

Boolean isThreadExecuted = threadExecutions.get("main");

Как и ожидалось, isThreadExecuted будет иметь значение true. Также вы можете видеть, что этот Runnable будет работать только в основном потоке. Если вы хотите использовать несколько потоков, вы можете использовать разные Исполнители для разных целей.

ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(logThreadRun);
executorService.submit(logThreadRun);
executorService.shutdown();

Boolean isThread1Executed = threadExecutions.get("pool-1-thread-1");
Boolean isThread2Executed = threadExecutions.get("pool-1-thread-2");
// isThread1Executed: true
// isThread2Executed: true

В этом примере вся отправленная работа выполняется в потоках ThreadPool.

Guava предоставляет различные методы в своем классе MoreExecutors.

6.2. MoreExecutors.directExecutor()

Это облегченный исполнитель, который может запускать задачи в потоке, вызывающем метод execute.

Executor executor = MoreExecutors.directExecutor();
executor.execute(logThreadRun);

Boolean isThreadExecuted = threadExecutions.get("main");
// isThreadExecuted: true

6.3. MoreExecutors.newDirectExecutorService()

Этот метод возвращает экземпляр ListeningExecutorService. Это более тяжелая реализация Executor, которая имеет много полезных методов. Он похож на устаревший метод sameThreadExecutor() из предыдущих версий Guava.

Этот ExecutorService будет запускать задачи в потоке, вызывающем метод execute().

ListeningExecutorService executor = MoreExecutors.newDirectExecutorService();
executor.execute(logThreadRun);

Этот исполнитель имеет много полезных методов, таких как invokeAll, invokeAny, awaitTermination, submit, isShutdown, isTerminated, shutdown, shutdownNow.

7. Заключение

В Guava 18 появилось несколько дополнений и улучшений к растущей библиотеке полезных функций. Это стоит рассмотреть для использования в вашем следующем проекте. Примеры кода в этой статье доступны в репозитории GitHub.