Перейти к содержимому

Google Guava в примерах

Google Guava — это библиотека с расширенным набором утилит для Java, которая упрощает многие аспекты разработки. Она предлагает множество полезных классов и методов для работы с коллекциями, кэшированием, функциональным программированием, обработкой строк и многого другого.

Основные возможности Google Guava:

Guava значительно расширяет стандартные коллекции Java, предоставляя новые структуры данных и утилиты для работы с ними.

Multimap:
Коллекция, которая позволяет хранить несколько значений для одного ключа.
Примеры: ArrayListMultimap, HashMultimap, LinkedListMultimap.

import com.google.common.collect.ArrayListMultimap
import com.google.common.collect.Multimap


fun multimapExample() {
    val multimap: Multimap<String, String> = ArrayListMultimap.create()
    multimap.put("fruit", "apple")
    multimap.put("fruit", "banana")
    multimap.put("vegetable", "carrot")
    println(multimap.get("fruit"))  // [apple, banana]
    println(multimap.get("vegetable"))  // [carrot]
}

Multiset:
Коллекция, которая позволяет хранить несколько копий одного и того же элемента.
Примеры: HashMultiset, TreeMultiset.

import com.google.common.collect.HashMultiset
import com.google.common.collect.Multiset


fun multisetExample() {
    val multiset: Multiset<String> = HashMultiset.create()
    multiset.add("apple")
    multiset.add("apple")
    multiset.add("banana")
    println(multiset.count("apple"))  // 2
    println(multiset.count("banana"))  // 1
}

BiMap:
Двунаправленная Map, где каждому ключу соответствует уникальное значение, и наоборот.
Пример: HashBiMap.

import com.google.common.collect.HashBiMap


fun bimapExample() {
    val biMap = HashBiMap.create<String, String>()
    biMap["one"] = "1"
    biMap["two"] = "2"
    // Доступ к значению по ключу
    println(biMap["one"])  // 1
    // Обратный доступ к ключу по значению
    println(biMap.inverse()["1"])  // one
}

Table:
Двумерная Map, эквивалентная Map>.
Пример: HashBasedTable.

import com.google.common.collect.HashBasedTable
import com.google.common.collect.Table


fun tableExample() {
    val table: Table<String, String, Int> = HashBasedTable.create()
    table.put("row1", "column1", 1)
    table.put("row1", "column2", 2)
    table.put("row2", "column1", 3)
    println(table.get("row1", "column1"))  // 1
    println(table.get("row1", "column2"))  // 2
    println(table.row("row1"))  // {column1=1, column2=2}
    println(table.column("column1"))  // {row1=1, row2=3}
}

ClassToInstanceMap:
Map, где ключи — это классы, а значения — экземпляры этих классов.
Пример: MutableClassToInstanceMap.

import com.google.common.collect.MutableClassToInstanceMap
import com.google.common.collect.ClassToInstanceMap


fun classToInstanceMapExample() {
    val classToInstanceMap: ClassToInstanceMap<Any> = MutableClassToInstanceMap.create()
    classToInstanceMap.putInstance(String::class.java, "Hello World")
    classToInstanceMap.putInstance(Int::class.java, 42)
    val strValue = classToInstanceMap.getInstance(String::class.java)
    val intValue = classToInstanceMap.getInstance(Int::class.java)
    println(strValue)  // Hello World
    println(intValue)  // 42
}

RangeSet:
Набор диапазонов чисел, предоставляющий операции для проверки пересечения и включения.
Пример: TreeRangeSet.

import com.google.common.collect.Range
import com.google.common.collect.TreeRangeSet


fun rangeSetExample() {
    val rangeSet = TreeRangeSet.create<Int>()
    rangeSet.add(Range.closed(1, 10))
    rangeSet.add(Range.closed(20, 30))
    println(rangeSet.contains(5))  // true
    println(rangeSet.contains(15))  // false
    println(rangeSet.contains(25))  // true
}

RangeMap:
Map, ключами которого являются диапазоны, а значениями — объекты, ассоциированные с этими диапазонами.
Пример: TreeRangeMap.

import com.google.common.collect.Range
import com.google.common.collect.TreeRangeMap


fun rangeMapExample() {
    val rangeMap = TreeRangeMap.create<Int, String>()
    rangeMap.put(Range.closed(1, 10), "Low")
    rangeMap.put(Range.closed(20, 30), "Medium")
    rangeMap.put(Range.closed(31, 40), "High")
    println(rangeMap.get(5))   // Low
    println(rangeMap.get(25))  // Medium
    println(rangeMap.get(35))  // High
}

Immutable Collections:
Неизменяемые версии стандартных коллекций для повышения безопасности и оптимизации.
Примеры: ImmutableList, ImmutableSet, ImmutableMap, ImmutableMultimap, ImmutableTable.

import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableSet


fun immutableCollectionsExample() {
    val immutableList: List<String> = ImmutableList.of("apple", "banana", "cherry")
    val immutableSet: Set<String> = ImmutableSet.of("apple", "banana", "cherry")
    println(immutableList)  // [apple, banana, cherry]
    println(immutableSet)  // [apple, banana, cherry]
    // Эти коллекции неизменяемы, попытка добавить элемент вызовет ошибку
    // immutableList.add("pear")  // UnsupportedOperationException
}

Splitter:
Утилита для разбиения строки по разделителю с различными опциями (пропуск пустых строк, обрезка пробелов и т. д.).

import com.google.common.base.Splitter


fun splitterExample() {
    val input = "apple, banana, , orange,,"
    val result = Splitter.on(',')
        .trimResults() // Обрезаем пробелы
        .omitEmptyStrings() // Пропускаем пустые строки
        .splitToList(input)  
    println(result)  // [apple, banana, orange]
}

Joiner:
Утилита для объединения коллекции строк или других объектов в строку с указанным разделителем.

import com.google.common.base.Joiner


fun joinerExample() {
    val fruits = listOf("apple", null, "banana", "orange")
    // Joiner будет пропускать null значения
    val result = Joiner.on(", ")
        .skipNulls()
        .join(fruits)
    println(result)  // apple, banana, orange
}

CharMatcher:
Утилита для поиска и манипуляций с символами в строке.

import com.google.common.base.CharMatcher


fun charMatcherExample() {
    val input = "Hello123 World456"
    // Удаляем все цифры
    val onlyLetters = CharMatcher.javaDigit().removeFrom(input)
    println(onlyLetters)  // Hello World
    // Оставляем только цифры
    val onlyDigits = CharMatcher.javaDigit().retainFrom(input)
    println(onlyDigits)  // 123456
    // Обрезаем пробелы и заменяем их на запятые
    val trimmed = CharMatcher.whitespace().trimAndCollapseFrom(input, ',')
    println(trimmed)  // Hello123,World456
}

CaseFormat:
Преобразование между различными форматами регистров строк (например, snake_case, camelCase, UPPER_UNDERSCORE и др.).

import com.google.common.base.CaseFormat


fun caseFormatExample() {
    val input = "my_variable_name"
    // Преобразуем из snake_case в camelCase
    val camelCase = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, input)
    println(camelCase)  // myVariableName
    // Преобразуем из snake_case в UPPER_UNDERSCORE
    val upperUnderscore = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, input)
    println(upperUnderscore)  // MY_VARIABLE_NAME
}

Guava предоставляет мощный механизм кэширования через классы Cache и LoadingCache, который поддерживает различные стратегии удаления данных, такие как удаление по времени жизни или по количеству элементов

Cache:
Встроенная реализация кэша с поддержкой автоматического удаления данных по времени жизни или объему.

import com.google.common.cache.CacheBuilder
import com.google.common.cache.Cache


fun simpleCacheExample() {
    // Создаем кэш с максимальным количеством элементов 100 и временем жизни 10 минут
    val cache: Cache<String, String> = CacheBuilder.newBuilder()
        .maximumSize(100)
        .expireAfterWrite(10, java.util.concurrent.TimeUnit.MINUTES)
        .build()
    // Добавляем данные в кэш
    cache.put("key1", "value1")
    // Получаем данные из кэша
    val value = cache.getIfPresent("key1")
    println(value)  // value1
    // Если ключ отсутствует, возвращается null
    val missingValue = cache.getIfPresent("key2")
    println(missingValue)  // null
}

LoadingCache:
Кэш, который автоматически загружает данные, если их нет, с помощью функции загрузки.

import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
import java.util.concurrent.TimeUnit


fun loadingCacheExample() {
    // Создаем кэш, который автоматически загружает данные, если их нет
    val loadingCache: LoadingCache<String, String> = CacheBuilder.newBuilder()
        .maximumSize(100)
        .expireAfterWrite(10, TimeUnit.MINUTES)
        .build(object : CacheLoader<String, String>() {
            override fun load(key: String): String {
                return "Generated value for $key"
            }
        })
    // Получаем данные из кэша, если ключ отсутствует — данные генерируются автоматически
    println(loadingCache.get("key1"))  // Generated value for key1
    // Добавляем элемент вручную в кэш
    loadingCache.put("key2", "Manually added value")
    println(loadingCache.get("key2"))  // Manually added value
}

RemovalListener:
Обработчик для событий удаления элементов из кэша.

import com.google.common.cache.CacheBuilder
import com.google.common.cache.RemovalListener
import com.google.common.cache.RemovalNotification
import java.util.concurrent.TimeUnit


fun cacheWithRemovalListenerExample() {
    // Создаем кэш с обработчиком удаления элементов
    val cache = CacheBuilder.newBuilder()
        .maximumSize(100)
        .expireAfterWrite(10, TimeUnit.MINUTES)
        .removalListener(object : RemovalListener<String, String> {
            override fun onRemoval(notification: RemovalNotification<String, String>) {
                println("Removed key: ${notification.key}, value: ${notification.value}, cause: ${notification.cause}")
            }
        })
        .build<String, String>()
    // Добавляем и удаляем элементы из кэша
    cache.put("key1", "value1")
    cache.invalidate("key1")  // Принудительное удаление элемента
}

CacheBuilder:
Позволяет гибко настраивать кэш с ограничениями по времени жизни элементов, количеству, обработке конкуренции и т. д.

import com.google.common.cache.CacheBuilder
import com.google.common.cache.Cache


fun cacheBuilderExample() {
    // Создаем кэш с максимальным количеством элементов и временем жизни 5 минут
    val cache: Cache<String, String> = CacheBuilder.newBuilder()
        .maximumSize(200) // Ограничение по количеству элементов
        .expireAfterWrite(5, TimeUnit.MINUTES) // Элементы живут 5 минут
        .build()
    // Добавляем элемент
    cache.put("key1", "value1")
    // Получаем элемент
    println(cache.getIfPresent("key1"))  // value1
    // Через 5 минут элемент будет удален автоматически
}

Predicate:
Интерфейс для определения логического условия для объектов.

import com.google.common.base.Predicate
import com.google.common.collect.FluentIterable


fun predicateExample() {
    val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    // Определяем предикат для фильтрации четных чисел
    val isEven: Predicate<Int> = Predicate { it % 2 == 0 }
    // Используем FluentIterable для фильтрации чисел
    val evenNumbers = FluentIterable.from(numbers)
        .filter(isEven)
        .toList()
    println(evenNumbers)  // [2, 4, 6, 8, 10]
}

Function:
Интерфейс для преобразования одного объекта в другой.

import com.google.common.base.Function
import com.google.common.collect.FluentIterable


fun functionExample() {
    val numbers = listOf(1, 2, 3, 4, 5)
    // Определяем функцию для возведения числа в квадрат
    val squareFunction: Function<Int, Int> = Function { it * it }
    // Преобразуем список чисел
    val squaredNumbers = FluentIterable.from(numbers)
        .transform(squareFunction)
        .toList()
    println(squaredNumbers)  // [1, 4, 9, 16, 25]
}

Suppliers:
Утилита для ленивой и мемоизированной поставки значений.

import com.google.common.base.Suppliers
import java.util.concurrent.TimeUnit


fun supplierExample() {
    // Ленивый вычислитель строки с мемоизацией на 10 секунд
    val supplier = Suppliers.memoizeWithExpiration(
        Suppliers.ofInstance("Hello, world!"),
        10, TimeUnit.SECONDS
    )
    // Получаем значение
    println(supplier.get())  // Hello, world!
    // Значение кэшируется на 10 секунд, затем будет пересчитано
}

Optional:
Контейнер для объектов, который может содержать либо значение, либо быть пустым (альтернатива null).

import com.google.common.base.Optional


fun optionalExample() {
    val optionalPresent: Optional<String> = Optional.of("Hello")
    val optionalAbsent: Optional<String> = Optional.absent()
    // Проверка наличия значения
    if (optionalPresent.isPresent) {
        println(optionalPresent.get())  // Hello
    }
    // Возвращение значения по умолчанию, если его нет
    println(optionalAbsent.or("Default Value"))  // Default Value
}

FluentIterable:
Утилита для обработки коллекций с поддержкой методов функциональной композиции (фильтрация, преобразование и т. д.).

import com.google.common.collect.FluentIterable


fun fluentIterableExample() {
    val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    // Комбинируем фильтрацию и трансформацию с помощью FluentIterable
    val processedNumbers = FluentIterable.from(numbers)
        .filter { it % 2 == 0 } // Фильтруем только четные числа
        .transform { it * 2 }  // Умножаем каждое на 2
        .toList()
    println(processedNumbers)  // [4, 8, 12, 16, 20]
}

Throwables:
Утилиты для работы с исключениями (например, приведение checked исключений к runtime исключениям).

import com.google.common.base.Throwables


fun throwablesExample() {
    try {
        // Искусственно создаем исключение
        throw IllegalArgumentException("Illegal argument!")
    } catch (e: Exception) {
        // Выводим полную информацию об исключении
        println(Throwables.getStackTraceAsString(e))  
        // Проверка и повторное выбрасывание исключения, если оно определенного типа
        if (Throwables.getRootCause(e) is IllegalArgumentException) {
            throw Throwables.propagate(e)
        }
    }
}

Retryer:
Утилита для повторного выполнения операций при возникновении исключений с поддержкой стратегий ожидания и остановки. Google Guava не содержит встроенной поддержки Retryer, но есть популярные библиотеки, такие как Failsafe или Guava-Retrying, которые можно использовать для повторных попыток. Вот пример использования библиотеки Guava-Retrying для повторной попытки выполнения операции:

import com.github.rholder.retry.Retryer
import com.github.rholder.retry.RetryerBuilder
import com.github.rholder.retry.StopStrategies
import com.github.rholder.retry.WaitStrategies
import java.util.concurrent.TimeUnit


fun retryerExample() {
    // Создаем Retryer с максимальным количеством попыток 3 и ожиданием 2 секунды между попытками
    val retryer: Retryer<String> = RetryerBuilder.newBuilder<String>()
        .retryIfException() // Повторять при возникновении любого исключения
        .withWaitStrategy(WaitStrategies.fixedWait(2, TimeUnit.SECONDS)) // Ждем 2 секунды между попытками
        .withStopStrategy(StopStrategies.stopAfterAttempt(3)) // Останавливаемся после 3 попыток
        .build()
    try {
        // Выполняем операцию с повторными попытками
        val result = retryer.call {
            // Логика, которая может выбросить исключение
            if (Math.random() > 0.7) {
                "Success"
            } else {
                throw RuntimeException("Failed attempt")
            }
        }
        println("Operation succeeded: $result")
    } catch (e: Exception) {
        println("Operation failed after retries: ${e.message}")
    }
}

Checked exceptions to Unchecked exceptions:
Guava помогает в преобразовании checked-исключений в unchecked через Throwables.propagate():

import com.google.common.base.Throwables


fun propagateExample() {
    try {
        throwCheckedException()
    } catch (e: Exception) {
        // Преобразуем checked-исключение в unchecked-исключение
        throw Throwables.propagate(e)
    }
}


fun throwCheckedException() {
    throw Exception("Checked exception!")
}

Google Guava предлагает удобные утилиты для работы с примитивами, такие как Ints, Longs, Doubles, Booleans, Bytes, а также поддержку беззнаковых целых чисел и неизменяемых коллекций для примитивов. Эти утилиты позволяют избежать работы с обертками примитивных типов и выполнять различные операции более эффективно.

Ints, Longs, Doubles, Booleans, Floats, Bytes:
Утилиты для преобразования и работы с примитивными типами.

import com.google.common.primitives.Ints


fun intsExample() {
    val numbers = listOf(1, 2, 3, 4, 5)
    // Преобразование списка в массив int
    val intArray = Ints.toArray(numbers)
    println(intArray.contentToString())  // [1, 2, 3, 4, 5]
    // Поиск максимального и минимального значения
    println(Ints.max(1, 3, 5, 2))  // 5
    println(Ints.min(1, 3, 5, 2))  // 1
    // Проверка наличия числа в массиве
    println(Ints.contains(intArray, 3))  // true
    // Сравнение двух int
    println(Ints.compare(5, 10))  // -1 (первый меньше второго)
}
import com.google.common.primitives.Longs


fun longsExample() {
    val longArray = longArrayOf(10L, 20L, 30L)
    // Преобразование массива long в список
    val longList = Longs.asList(*longArray)
    println(longList)  // [10, 20, 30]
    // Преобразование списка обратно в массив
    val array = Longs.toArray(longList)
    println(array.contentToString())  // [10, 20, 30]
}
import com.google.common.primitives.Doubles


fun doublesExample() {
    val doubleArray = doubleArrayOf(1.1, 2.2, 3.3)
    // Преобразование массива double в список
    val doubleList = Doubles.asList(*doubleArray)
    println(doubleList)  // [1.1, 2.2, 3.3]
    // Преобразование списка обратно в массив
    val array = Doubles.toArray(doubleList)
    println(array.contentToString())  // [1.1, 2.2, 3.3]
    // Сравнение двух double
    println(Doubles.compare(3.14, 2.71))  // 1 (первый больше второго)
}
import com.google.common.primitives.Booleans

fun booleansExample() {
    val booleanArray = booleanArrayOf(true, false, true)
    // Преобразование массива boolean в список
    val booleanList = Booleans.asList(*booleanArray)
    println(booleanList)  // [true, false, true]
    // Преобразование списка обратно в массив
    val array = Booleans.toArray(booleanList)
    println(array.contentToString())  // [true, false, true]
}
import com.google.common.primitives.Bytes


fun bytesExample() {
    val byteArray = byteArrayOf(1, 2, 3)
    // Преобразование массива byte в список
    val byteList = Bytes.asList(*byteArray)
    println(byteList)  // [1, 2, 3]
    // Преобразование списка обратно в массив
    val array = Bytes.toArray(byteList)
    println(array.contentToString())  // [1, 2, 3]
}

UnsignedInts, UnsignedLongs:
Поддержка беззнаковых целых чисел.

import com.google.common.primitives.UnsignedInts
import com.google.common.primitives.UnsignedLongs


fun unsignedIntsExample() {
    val unsignedInt = UnsignedInts.parseUnsignedInt("4294967295")  // Максимальное значение unsigned int
    println(unsignedInt)  // 4294967295 (но хранится как обычный int)
    val compared = UnsignedInts.compare(4000000000.toInt(), 3000000000.toInt())
    println(compared)  // 1 (первое больше второго)
}


fun unsignedLongsExample() {
    val unsignedLong = UnsignedLongs.parseUnsignedLong("18446744073709551615")  // Максимальное значение unsigned long
    println(unsignedLong)  // 18446744073709551615 (но хранится как обычный long)
    val compared = UnsignedLongs.compare(18000000000000000000L, 17000000000000000000L)
    println(compared)  // 1 (первое больше второго)
}

Immutable collections for primitives:
Например, ImmutableIntArray.

import com.google.common.primitives.ImmutableIntArray


fun immutableIntArrayExample() {
    // Создание неизменяемого массива
    val immutableIntArray = ImmutableIntArray.of(1, 2, 3, 4, 5)
    println(immutableIntArray)  // [1, 2, 3, 4, 5]
    // Получение элемента
    println(immutableIntArray.get(2))  // 3
    // Попытка изменить массив вызовет ошибку
    // immutableIntArray.set(0, 10)  // UnsupportedOperationException
}

Google Guava предоставляет мощные утилиты для работы с итераторами и генераторами данных. Эти утилиты облегчают фильтрацию, преобразование и комбинацию коллекций, предоставляя гибкий и удобный API. Рассмотрим несколько примеров использования итераторов и генераторов данных на Kotlin.

Iterators:
Утилиты для работы с Java Iterator, например, фильтрация, преобразование, объединение и т. д.

Пример фильтрации итератора:

import com.google.common.collect.Iterators


fun iteratorFilterExample() {
    val numbers = listOf(1, 2, 3, 4, 5).iterator()
    // Фильтруем итератор, оставляя только четные числа
    val evenNumbers = Iterators.filter(numbers) { it % 2 == 0 }
    while (evenNumbers.hasNext()) {
        println(evenNumbers.next())  // 2, 4
    }
}

Пример преобразования итератора:

import com.google.common.collect.Iterators
import com.google.common.base.Function


fun iteratorTransformExample() {
    val numbers = listOf(1, 2, 3, 4, 5).iterator()
    // Преобразуем итератор, возводя числа в квадрат
    val squaredNumbers = Iterators.transform(numbers, Function { it * it })
    while (squaredNumbers.hasNext()) {
        println(squaredNumbers.next())  // 1, 4, 9, 16, 25
    }
}

PeekingIterator:
Итератор, который позволяет «заглянуть» на следующий элемент без его извлечения.

import com.google.common.collect.Iterators
import com.google.common.collect.PeekingIterator


fun peekingIteratorExample() {
    val numbers = listOf(1, 2, 3, 4, 5).iterator()
    val peekingIterator: PeekingIterator<Int> = Iterators.peekingIterator(numbers)
    while (peekingIterator.hasNext()) {
        println("Current: ${peekingIterator.next()}")
        if (peekingIterator.hasNext()) {
            println("Next (peek): ${peekingIterator.peek()}")
        }
    }
}

AbstractIterator:
Шаблон для упрощения создания пользовательских итераторов.

import com.google.common.collect.AbstractIterator


fun abstractIteratorExample() {
    val customIterator = object : AbstractIterator<Int>() {
        var count = 0
        override fun computeNext(): Int? {
            count++
            return if (count <= 5) count else endOfData() // Завершаем, когда count > 5
        }
    }
    while (customIterator.hasNext()) {
        println(customIterator.next())  // 1, 2, 3, 4, 5
    }
}

UnmodifiableIterator:
Итератор, который не позволяет изменения. UnmodifiableIterator гарантирует, что элементы не могут быть изменены во время итерации.

import com.google.common.collect.UnmodifiableIterator

fun unmodifiableIteratorExample() {
    val numbers = listOf(1, 2, 3, 4, 5)

    // Создаем неизменяемый итератор
    val unmodifiableIterator: UnmodifiableIterator<Int> = object : UnmodifiableIterator<Int>() {
        private val iter = numbers.iterator()

        override fun hasNext(): Boolean = iter.hasNext()

        override fun next(): Int = iter.next()
    }

    while (unmodifiableIterator.hasNext()) {
        println(unmodifiableIterator.next())  // 1, 2, 3, 4, 5
    }

    // unmodifiableIterator.remove()  // UnsupportedOperationException
}

FluentIterable:
Функциональная работа с коллекциями. Хотя FluentIterable работает как с коллекциями, так и с итераторами, он обеспечивает функциональный стиль работы с ними, объединяя фильтрацию, преобразование и другие операции.

import com.google.common.collect.FluentIterable


fun fluentIterableExample() {
    val numbers = listOf(1, 2, 3, 4, 5)
    // Комбинируем фильтрацию и преобразование с помощью FluentIterable
    val result = FluentIterable.from(numbers)
        .filter { it % 2 == 0 } // Фильтруем четные числа
        .transform { it * 2 }   // Умножаем каждое на 2
        .toList()
    println(result)  // [4, 8]
}

Google Guava предоставляет мощные утилиты для преобразования типов, такие как TypeToken и Converter. Они позволяют работать с дженериками, преобразовывать объекты между различными типами и управлять безопасностью типов во время выполнения.

TypeToken:
Утилита для работы с типами, поддерживающая дженерики. TypeToken позволяет работать с типами, особенно с дженериками, и извлекать информацию о них во время выполнения. В стандартной Java/Kotlin система типов стирает информацию о дженериках во время выполнения, но с помощью TypeToken можно сохранять информацию о типе.

import com.google.common.reflect.TypeToken


fun typeTokenExample() {
    // Создаем TypeToken для List<String>
    val typeToken = object : TypeToken<List<String>>() {}
    // Получаем информацию о типе
    val type = typeToken.type
    println(type)  // java.util.List<java.lang.String>
    // Проверяем тип во время выполнения
    val listOfStrings: Any = listOf("a", "b", "c")
    if (typeToken.rawType.isAssignableFrom(listOfStrings.javaClass)) {
        println("listOfStrings является List<String>")
    }
}

Converter:
Абстракция для преобразования одного типа в другой с поддержкой обратной конверсии. Converter позволяет легко преобразовывать объекты из одного типа в другой. Это полезно, когда нужно определить преобразование в обоих направлениях — туда и обратно.

import com.google.common.base.Converter


fun converterExample() {
    // Определяем преобразователь между String и Integer
    val stringToIntegerConverter = object : Converter<String, Int>() {      
        override fun doForward(string: String): Int {
            return string.toInt()
        }
        override fun doBackward(integer: Int): String {
            return integer.toString()
        }
    }
    // Преобразуем строку в число
    val intValue = stringToIntegerConverter.convert("123")
    println(intValue)  // 123
    // Преобразуем число обратно в строку
    val stringValue = stringToIntegerConverter.reverse().convert(456)
    println(stringValue)  // "456"
}

Enums:
Преобразование строк в перечисления (и обратно). Guava также упрощает работу с перечислениями (enums), предлагая утилиты для их преобразования из строк и обратно.

import com.google.common.base.Enums


enum class Color {
    RED, GREEN, BLUE
}


fun enumExample() {
    // Преобразуем строку в значение перечисления
    val color = Enums.getIfPresent(Color::class.java, "RED").orNull()
    println(color)  // RED
    // Получаем строковое представление перечисления
    val colorName = Enums.stringConverter(Color::class.java).convert(Color.GREEN)
    println(colorName)  // GREEN
}

Stopwatch:
Таймер для измерения времени выполнения операций.

import com.google.common.base.Stopwatch
import java.util.concurrent.TimeUnit


fun stopwatchExample() {
    // Создаем таймер
    val stopwatch = Stopwatch.createStarted()
    // Выполняем некоторую операцию
    Thread.sleep(1000)
    // Останавливаем таймер
    stopwatch.stop()
    // Получаем результат в миллисекундах
    println("Elapsed time: ${stopwatch.elapsed(TimeUnit.MILLISECONDS)} ms")  // Пример: Elapsed time: 1001 ms
    // Можно перезапустить таймер
    stopwatch.reset().start()
    Thread.sleep(500)
    stopwatch.stop()
    println("Elapsed time after reset: ${stopwatch.elapsed(TimeUnit.MILLISECONDS)} ms")  // Пример: Elapsed time after reset: 500 ms
}

Timestamps:
Утилиты для работы с отметками времени. Guava также предлагает утилиты для работы с временными метками и временными интервалами. Однако стандартной утилиты Timestamps в Guava нет. Вместо этого обычно используются классы из пакета java.time (начиная с Java 8). Тем не менее, Stopwatch является основным инструментом Guava для работы с временем.

Пример работы с классами времени из Java 8 (для работы с временными метками):
Хотя это не часть Google Guava, начиная с Java 8, мы можем использовать java.time для работы с временными метками, и это часто используется совместно с утилитами Guava.

import java.time.LocalDateTime
import java.time.format.DateTimeFormatter


fun timestampExample() {
    // Получаем текущую временную метку
    val currentTime = LocalDateTime.now()
    println("Current timestamp: $currentTime")  // Пример: Current timestamp: 2023-10-01T15:45:32.123
    // Форматирование временной метки
    val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
    val formattedTime = currentTime.format(formatter)
    println("Formatted timestamp: $formattedTime")  // Пример: Formatted timestamp: 2023-10-01 15:45:32
}

Google Guava предоставляет абстракции для управления сервисами и процессами через пакеты com.google.common.util.concurrent и класс Service. Эти утилиты особенно полезны для управления жизненным циклом процессов или сервисов, которые имеют стадии запуска, выполнения и остановки, что делает их пригодными для долгоживущих процессов, таких как серверы, фоновая обработка задач и другие длительные операции.

Service:
Абстракция для представления сервисов с поддержкой жизненного цикла (инициализация, остановка и т. д.). Предоставляет методы для управления жизненным циклом процессов: запуск, остановка, мониторинг состояния.

AbstractExecutionThreadService:
AbstractExecutionThreadService предоставляет шаблон для создания процессов, которые должны выполняться в отдельных потоках. Этот класс позволяет легко запускать сервис в фоновом потоке и обрабатывать его завершение.

import com.google.common.util.concurrent.AbstractExecutionThreadService


fun abstractExecutionThreadServiceExample() {
    // Создаем сервис, работающий в отдельном потоке
    val backgroundService: Service = object : AbstractExecutionThreadService() {
        override fun run() {
            println("Background service is running")
            // Эмуляция долгой операции
            Thread.sleep(5000)
            println("Background service has completed")
        }
        override fun startUp() {
            println("Background service is starting up")
        }
        override fun shutDown() {
            println("Background service is shutting down")
        }
    }
    // Запускаем сервис
    backgroundService.startAsync()
    backgroundService.awaitRunning()
    // Останавливаем сервис после работы
    backgroundService.stopAsync()
    backgroundService.awaitTerminated()
}

ServiceManager:
Предоставляет возможность управлять несколькими сервисами одновременно, отслеживать их состояния и корректно завершать их работу.

import com.google.common.util.concurrent.AbstractExecutionThreadService
import com.google.common.util.concurrent.Service
import com.google.common.util.concurrent.ServiceManager


fun serviceManagerExample() {
    // Создаем два сервиса
    val service1: Service = object : AbstractExecutionThreadService() {
        override fun run() {
            println("Service 1 is running...")
            Thread.sleep(2000)
        }
        override fun shutDown() {
            println("Service 1 is shutting down...")
        }
    }
    val service2: Service = object : AbstractExecutionThreadService() {
        override fun run() {
            println("Service 2 is running...")
            Thread.sleep(3000)
        }
        override fun shutDown() {
            println("Service 2 is shutting down...")
        }
    }
    // Используем ServiceManager для управления двумя сервисами
    val serviceManager = ServiceManager(listOf(service1, service2))
    // Добавляем обработчик для уведомления о завершении всех сервисов
    serviceManager.addListener(object : ServiceManager.Listener() {
        override fun stopped() {
            println("All services have stopped.")
        }
        override fun failure(service: Service) {
            println("Service failed: ${service.javaClass.name}")
        }
    }, Runnable::run)
    // Запускаем все сервисы
    serviceManager.startAsync()
    serviceManager.awaitHealthy()  // Ожидание запуска всех сервисов
    // Останавливаем все сервисы
    serviceManager.stopAsync()
    serviceManager.awaitStopped()  // Ожидание остановки всех сервисов
}

ListenableFuture:
Расширение стандартного Future, поддерживающее добавление коллбеков.

import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors
import com.google.common.util.concurrent.ListenableFutureTask
import java.util.concurrent.Executors


fun listenableFutureExample() {
    // Создаем executor для выполнения задач
    val executor = Executors.newSingleThreadExecutor()
    // Создаем ListenableFutureTask для асинхронной операции
    val futureTask: ListenableFuture<String> = ListenableFutureTask.create<String> {
        Thread.sleep(2000)  // Эмуляция долгой задачи
        "Task result"
    }
    // Добавляем коллбек для обработки результата
    futureTask.addListener({
        try {
            println("Task completed with result: ${futureTask.get()}")
        } catch (e: Exception) {
            println("Error during task execution: ${e.message}")
        }
    }, MoreExecutors.directExecutor())
    // Запускаем задачу
    executor.submit(futureTask)
}

Futures:
Утилиты для работы с ListenableFuture, включая композицию и комбинирование нескольких будущих значений.

import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFutureTask
import java.util.concurrent.Executors


fun futuresExample() {
    // Создаем executor для выполнения задач
    val executor = Executors.newFixedThreadPool(2)
    // Создаем две асинхронные задачи
    val futureTask1 = ListenableFutureTask.create<String> {
        Thread.sleep(1000)
        "Result from Task 1"
    }
    val futureTask2 = ListenableFutureTask.create<String> {
        Thread.sleep(2000)
        "Result from Task 2"
    }
    // Запускаем задачи
    executor.submit(futureTask1)
    executor.submit(futureTask2)
    // Комбинируем результаты двух задач
    val combinedFuture = Futures.allAsList(futureTask1, futureTask2)
    // Добавляем коллбек для обработки результата
    combinedFuture.addListener({
        try {
            val results = combinedFuture.get()
            println("All tasks completed with results: $results")
        } catch (e: Exception) {
            println("Error: ${e.message}")
        }
    }, MoreExecutors.directExecutor())
}

MoreExecutors:
Расширенные утилиты для работы с экзекьюторами (например, преобразование экзекьютора в ListenableFuture).

import com.google.common.util.concurrent.MoreExecutors
import java.util.concurrent.Executors


fun moreExecutorsExample() {
    // Создаем executor с автоматическим завершением работы при завершении всех задач
    val executor = MoreExecutors.getExitingExecutorService(Executors.newSingleThreadExecutor())
    // Запускаем задачу
    executor.submit {
        println("Task is running in exiting executor")
    }
    // Экзекьютор автоматически завершит свою работу, когда все задачи завершены
}

ListeningExecutorService:
Расширяет ExecutorService и поддерживает создание задач, которые возвращают ListenableFuture. Это удобно для управления асинхронными задачами с коллбеками.

import com.google.common.util.concurrent.ListeningExecutorService
import com.google.common.util.concurrent.MoreExecutors
import java.util.concurrent.Executors


fun listeningExecutorServiceExample() {
    // Создаем ListeningExecutorService
    val executorService: ListeningExecutorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(2))
    // Запускаем асинхронную задачу
    val future = executorService.submit<String> {
        Thread.sleep(1000)
        "Async result"
    }
    // Добавляем коллбек для обработки результата
    future.addListener({
        try {
            println("Task completed with result: ${future.get()}")
        } catch (e: Exception) {
            println("Error: ${e.message}")
        }
    }, MoreExecutors.directExecutor())
}

RateLimiter:
Используется для ограничения скорости выполнения операций, например, для контроля количества запросов в секунду.

import com.google.common.util.concurrent.RateLimiter


fun rateLimiterExample() {
    // Создаем RateLimiter с лимитом в 2 операции в секунду
    val rateLimiter = RateLimiter.create(2.0)
    // Выполняем операции с ограничением по скорости
    for (i in 1..5) {
        rateLimiter.acquire()  // Блокируем выполнение, пока лимит не позволит продолжить
        println("Executing task $i at ${System.currentTimeMillis()}")
    }
}

Files:
Утилиты для работы с файлами (копирование, перемещение, чтение, запись и т. д.).

Пример чтения и записи файлов:

import com.google.common.io.Files
import java.io.File
import java.nio.charset.StandardCharsets


fun filesExample() {
    val file = File("example.txt")
    // Записываем данные в файл
    Files.asCharSink(file, StandardCharsets.UTF_8).write("Hello, world!")
    // Читаем данные из файла
    val content = Files.asCharSource(file, StandardCharsets.UTF_8).read()
    println("File content: $content")  // Hello, world!
}

Пример копирования файла:

import com.google.common.io.Files
import java.io.File


fun fileCopyExample() {
    val sourceFile = File("source.txt")
    val targetFile = File("target.txt")
    // Копируем файл
    Files.copy(sourceFile, targetFile)
    println("File copied from ${sourceFile.name} to ${targetFile.name}")
}

Пример перемещения файла:

import com.google.common.io.Files
import java.io.File


fun fileMoveExample() {
    val sourceFile = File("source.txt")
    val targetFile = File("moved.txt")
    // Перемещаем файл
    Files.move(sourceFile, targetFile)
    println("File moved from ${sourceFile.name} to ${targetFile.name}")
}

CharStreams:
Утилиты для работы с потоками символов.

Пример чтения данных из Reader:

import com.google.common.io.CharStreams
import java.io.StringReader


fun charStreamsExample() {
    val reader = StringReader("Hello from StringReader!")
    // Читаем все данные из Reader
    val content = CharStreams.toString(reader)
    println(content)  // Hello from StringReader!
}

Пример записи данных в Writer:

import com.google.common.io.CharStreams
import java.io.StringWriter


fun charStreamsWriteExample() {
    val writer = StringWriter()
    // Записываем данные в Writer
    CharStreams.asWriter(writer).write("Hello to StringWriter!")
    println(writer.toString())  // Hello to StringWriter!
}

ByteStreams:
Утилиты для работы с байтовыми потоками.

Пример копирования данных из одного потока в другой:

import com.google.common.io.ByteStreams
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream


fun byteStreamsExample() {
    val input = ByteArrayInputStream("Hello in bytes!".toByteArray())
    val output = ByteArrayOutputStream()
    // Копируем данные из InputStream в OutputStream
    ByteStreams.copy(input, output)
    println(output.toString())  // Hello in bytes!
}

Пример чтения всех данных из InputStream:

import com.google.common.io.ByteStreams
import java.io.ByteArrayInputStream


fun byteStreamsReadExample() {
    val input = ByteArrayInputStream("Some byte data".toByteArray())
    // Читаем все данные из InputStream
    val byteData = ByteStreams.toByteArray(input)
    println(String(byteData))  // Some byte data
}

Closer:
Упрощает управление ресурсами, такими как файлы и потоки, обеспечивая их автоматическое закрытие в случае ошибки или успешного завершения работы.

import com.google.common.io.Closer
import java.io.FileReader
import java.io.FileWriter


fun closerExample() {
    val closer = Closer.create()
    try {
        // Открываем ресурсы
        val reader = closer.register(FileReader("input.txt"))
        val writer = closer.register(FileWriter("output.txt"))
        // Читаем данные и записываем их
        val data = reader.readText()
        writer.write(data)
    } catch (e: Exception) {
        println("Error occurred: ${e.message}")
        throw e
    } finally {
        closer.close()  // Автоматически закрывает все ресурсы
    }
}

Resources:
Guava также предоставляет утилиты для работы с ресурсами в виде файлов, которые находятся в classpath.

import com.google.common.io.Resources
import java.nio.charset.StandardCharsets


fun resourcesExample() {
    // Читаем ресурс из classpath
    val url = Resources.getResource("config.txt")
    val content = Resources.toString(url, StandardCharsets.UTF_8)
    println(content)
}

Stream:
Расширенные утилиты для работы с потоками данных.

import com.google.common.collect.Streams


fun streamsExample() {
    val list1 = listOf(1, 2, 3)
    val list2 = listOf(4, 5, 6)
    // Объединяем два списка в один поток
    val combinedStream = Streams.concat(list1.stream(), list2.stream())
    // Преобразуем поток в список
    val combinedList = combinedStream.toList()
    println(combinedList)  // [1, 2, 3, 4, 5, 6]
}

Утилита Streams.zip позволяет объединить два потока в один с помощью функции:

import com.google.common.collect.Streams
import java.util.stream.Stream


fun streamsZipExample() {
    val stream1 = Stream.of(1, 2, 3)
    val stream2 = Stream.of("a", "b", "c")
    // Объединяем два потока, создавая пары (число, строка)
    val zippedStream = Streams.zip(stream1, stream2) { number, letter -> "$number$letter" }
    // Преобразуем поток в список
    val result = zippedStream.toList()
    println(result)  // [1a, 2b, 3c]
}

Hashing:
Поддержка различных хеш-функций (MD5, SHA-256, MurmurHash и др.).

import com.google.common.hash.Hashing
import java.nio.charset.StandardCharsets


fun hashingExample() {
    val input = "Hello, World!"
    // Используем SHA-256 для хеширования строки
    val hash = Hashing.sha256()
        .hashString(input, StandardCharsets.UTF_8)
        .toString()
    println("SHA-256 hash: $hash")
    // Пример вывода: SHA-256 hash: c0535e4be2b79ffd93291305436bf889314e4a3d9a91fb217b0b14cba2999e66
}
import com.google.common.hash.Hashing
import java.nio.charset.StandardCharsets


fun murmurHashExample() {
    val input = "Guava Hashing Example"
    // Используем MurmurHash3 для хеширования строки
    val hash = Hashing.murmur3_128()
        .hashString(input, StandardCharsets.UTF_8)
        .toString()
    println("MurmurHash3 (128-bit) hash: $hash")
    // Пример вывода: MurmurHash3 (128-bit) hash: 2f17b911e9d9b22fa10f0ab091d02e17
}

Hasher:
Позволяет поэтапно добавлять данные для хеширования. Это полезно, если нужно хешировать данные, поступающие из нескольких источников, или данные, которые необходимо хешировать последовательно.

import com.google.common.hash.Hashing
import java.nio.charset.StandardCharsets

fun hasherExample() {
    val hasher = Hashing.sha256().newHasher()

    // Добавляем данные для хеширования по частям
    hasher.putString("Hello", StandardCharsets.UTF_8)
    hasher.putInt(12345)
    hasher.putLong(9876543210L)

    // Получаем итоговый хеш
    val hash = hasher.hash().toString()

    println("Combined SHA-256 hash: $hash")
    // Пример вывода: Combined SHA-256 hash: 3627904d36e33402e1dba8e132ea9d222f3baaf3dcb9bfb144f50c3cf555160b
}

HashCode:
Представляет результат хеширования, который можно преобразовать в строку, массив байтов или использовать для сравнения.

import com.google.common.hash.Hashing
import java.nio.charset.StandardCharsets


fun hashCodeExample() {
    val hashCode = Hashing.sha256()
        .hashString("Guava HashCode Example", StandardCharsets.UTF_8)
    // Преобразуем хеш в строку
    println("Hash as string: ${hashCode.toString()}")
    // Преобразуем хеш в байтовый массив
    val byteArray = hashCode.asBytes()
    println("Hash as byte array: ${byteArray.contentToString()}")
}

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

import com.google.common.hash.BloomFilter
import com.google.common.hash.Funnels


fun bloomFilterExample() {
    // Создаем фильтр Блума для строк с ожидаемой пропускной способностью 1000 и вероятностью ложных срабатываний 0.01
    val bloomFilter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8), 1000, 0.01)
    // Добавляем данные в фильтр
    bloomFilter.put("Guava")
    bloomFilter.put("BloomFilter")
    bloomFilter.put("Hashing")
    // Проверяем наличие элементов
    println("Might contain 'Guava': ${bloomFilter.mightContain("Guava")}")  // true
    println("Might contain 'Hashing': ${bloomFilter.mightContain("Hashing")}")  // true
    println("Might contain 'Unknown': ${bloomFilter.mightContain("Unknown")}")  // false (вероятность ложного срабатывания 0.01)
}

Custom Hash Functions:
Создание собственной хеш-функции. Если встроенные алгоритмы хеширования не подходят, можно создать собственную хеш-функцию, реализуя интерфейс HashFunction.

import com.google.common.hash.AbstractHasher
import com.google.common.hash.HashFunction
import com.google.common.hash.HashCode


fun customHashFunctionExample() {
    val customHashFunction = object : HashFunction {
        override fun newHasher() = object : AbstractHasher() {
            private var sum = 0
            override fun putInt(i: Int): AbstractHasher {
                sum += i
                return this
            }
            override fun putString(input: CharSequence, charset: java.nio.charset.Charset): AbstractHasher {
                sum += input.length
                return this
            }
            override fun hash(): HashCode {
                return HashCode.fromInt(sum)
            }
        }
        override fun bits(): Int = 32
    }
    val hasher = customHashFunction.newHasher()
    hasher.putInt(123)
    hasher.putString("custom hash function", StandardCharsets.UTF_8)
    val hash = hasher.hash()
    println("Custom hash code: ${hash.asInt()}")
    // Пример вывода: Custom hash code: 143
}

Ints, Longs, Doubles, BigIntegerMath, DoubleMath:
Утилиты для математических операций с числами различных типов.

RoundingMode:
Утилиты для округления чисел.

@Beta:
Пометка о нестабильных API. Указывает, что API может измениться в будущем. Аннотация @Beta используется для пометки классов, методов и API, которые могут измениться в будущих версиях. Этот API не считается стабильным, и его использование в продакшене рекомендуется с осторожностью, так как он может измениться без обратной совместимости.

import com.google.common.annotations.Beta


@Beta
class BetaFeature {
    fun newFeature() {
        println("This feature is in beta and may change!")
    }
}


fun betaFeatureExample() {
    val feature = BetaFeature()
    feature.newFeature()
}

@VisibleForTesting:
Доступность для тестирования. Указывает, что метод или класс предназначен для использования в тестах. Аннотация @VisibleForTesting используется для того, чтобы показать, что определенный метод или поле доступны только для тестов. Это может быть полезно для обозначения методов, которые не предназначены для использования вне тестов, но являются открытыми для тестирования.

import com.google.common.annotations.VisibleForTesting


class ProductionClass {
    // Этот метод доступен только для тестов
    @VisibleForTesting
    internal fun internalLogicForTests(): Int {
        return 42
    }
    fun publicLogic(): String {
        return "Public logic result"
    }
}


fun testVisibilityExample() {
    val productionClass = ProductionClass()
    // Можно вызывать метод в тестах
    val testResult = productionClass.internalLogicForTests()
    println("Test logic result: $testResult")  // Test logic result: 42
}

@Nullable:
Аннотация для указания возможности null. Аннотация @Nullable указывает, что метод может возвращать null или что параметр метода может принимать значение null. Это помогает повысить безопасность и прозрачность кода, показывая, где может возникнуть null.

import javax.annotation.Nullable


class NullableExample {
    // Метод может вернуть null
    @Nullable
    fun findValue(key: String): String? {
        return if (key == "exists") "Found value" else null
    }
}


fun nullableExample() {
    val example = NullableExample()
    val value = example.findValue("not_exists")
    println("Value: $value")  // Value: null
}

@CheckReturnValue:
Обязательная проверка возвращаемого значения. Аннотация @CheckReturnValue указывает, что возвращаемое значение метода должно быть проверено, и его игнорирование может привести к ошибке. Эта аннотация полезна для методов, результат которых важно обработать.

import com.google.errorprone.annotations.CheckReturnValue


class CheckReturnValueExample {
  
    @CheckReturnValue
    fun computeValue(): Int {
        return 42
    }
}


fun checkReturnValueExample() {
    val example = CheckReturnValueExample()
    // Предполагается, что это значение должно быть использовано
    val result = example.computeValue()
    println("Computed value: $result")  // Computed value: 42
}

@CanIgnoreReturnValue:
Разрешение игнорирования возвращаемого значения. Аннотация @CanIgnoreReturnValue позволяет игнорировать возвращаемое значение метода. Она полезна в ситуациях, когда возвращаемое значение необязательно должно использоваться.

import com.google.errorprone.annotations.CanIgnoreReturnValue


class IgnoreReturnValueExample {

    @CanIgnoreReturnValue
    fun performAction(): String {
        return "Action performed"
    }
}


fun ignoreReturnValueExample() {
    val example = IgnoreReturnValueExample()
    // Возвращаемое значение можно игнорировать
    example.performAction()  // Значение не обязательно использовать
}

Preconditions:
Утилиты для проверки условий, используемые для валидации аргументов методов. Google Guava предоставляет утилиту Preconditions, которая помогает выполнять проверки аргументов методов и состояний программы. Эти проверки повышают читаемость и надежность кода, поскольку позволяют выбрасывать исключения с информативными сообщениями в случае некорректных данных.

Утилита Preconditions предоставляет следующие основные методы:

checkArgument:
используется для проверки условий, касающихся переданных аргументов. Если условие не выполняется, выбрасывается IllegalArgumentException.

import com.google.common.base.Preconditions


fun checkArgumentExample(age: Int) {
    // Проверка, что возраст больше или равен 18
    Preconditions.checkArgument(age >= 18, "Age must be 18 or older, but was %s", age)
    println("Valid age: $age")
}


fun main() {
    checkArgumentExample(20)  // Valid age: 20
    // checkArgumentExample(15)  // IllegalArgumentException: Age must be 18 or older, but was 15
}

checkNotNull:
проверяет, что переданный аргумент не равен null. В случае, если аргумент null, выбрасывается NullPointerException.

import com.google.common.base.Preconditions


fun checkNotNullExample(name: String?) {
    // Проверка, что имя не равно null
    val validName = Preconditions.checkNotNull(name, "Name cannot be null")
    println("Valid name: $validName")
}


fun main() {
    checkNotNullExample("Alice")  // Valid name: Alice
    // checkNotNullExample(null)  // NullPointerException: Name cannot be null
}

checkState:
используется для проверки состояния объекта. Если условие не выполняется, выбрасывается IllegalStateException.

import com.google.common.base.Preconditions


class StateExample(private var isInitialized: Boolean) {
    fun doSomething() {
        // Проверка, что объект инициализирован
        Preconditions.checkState(isInitialized, "Object must be initialized before use")

        println("Doing something")
    }
    fun initialize() {
        isInitialized = true
    }
}


fun main() {
    val example = StateExample(false)
    // example.doSomething()  // IllegalStateException: Object must be initialized before use
    example.initialize()
    example.doSomething()  // Doing something
}

checkElementIndex:
проверяет, что индекс элемента находится в пределах допустимого диапазона (от 0 до размера списка/массива). Если индекс выходит за пределы, выбрасывается IndexOutOfBoundsException.

import com.google.common.base.Preconditions


fun checkElementIndexExample(index: Int, list: List<String>) {
    // Проверка, что индекс находится в пределах списка
    Preconditions.checkElementIndex(index, list.size, "Invalid index")
    println("Element at index $index: ${list[index]}")
}


fun main() {
    val list = listOf("apple", "banana", "cherry")
    checkElementIndexExample(1, list)  // Element at index 1: banana
    // checkElementIndexExample(5, list)  // IndexOutOfBoundsException: Invalid index (5) must be less than size (3)
}

checkPositionIndex:
проверяет позицию в коллекции или массиве. Позиция — это индекс, который может указывать на начало или конец коллекции. Если позиция невалидна, выбрасывается IndexOutOfBoundsException.

import com.google.common.base.Preconditions


fun checkPositionIndexExample(position: Int, list: List<String>) {
    // Проверка позиции
    Preconditions.checkPositionIndex(position, list.size, "Invalid position")
    println("Valid position: $position")
}


fun main() {
    val list = listOf("apple", "banana", "cherry")
    checkPositionIndexExample(3, list)  // Valid position: 3 (указание на конец списка)
    // checkPositionIndexExample(5, list)  // IndexOutOfBoundsException: Invalid position (5) must be less than size (3)
}

checkPositionIndexes:
проверяет, что начальный и конечный индексы находятся в допустимом диапазоне и что начальный индекс не больше конечного.

import com.google.common.base.Preconditions


fun checkPositionIndexesExample(start: Int, end: Int, list: List<String>) {
    // Проверка диапазона индексов
    Preconditions.checkPositionIndexes(start, end, list.size)
    println("Valid range: from $start to $end")
}


fun main() {
    val list = listOf("apple", "banana", "cherry", "date")
    checkPositionIndexesExample(0, 2, list)  // Valid range: from 0 to 2
    // checkPositionIndexesExample(2, 5, list)  // IndexOutOfBoundsException: End index (5) must not be greater than size (4)
}

Objects:
Утилиты для работы с объектами (сравнение, генерация hashCode и т. д.).

MoreObjects:
Дополнительные утилиты для работы с объектами, включая toStringHelper.

ComparisonChain:
Утилита для создания цепочек сравнений объектов, которая упрощает реализацию методов compareTo.

Copyright: Roman Kryvolapov