---
title: "Google Guava у прикладах"
url: "https://romankryvolapov.com/uk/google-guava-in-examples/"
description: "Можливості Google Guava у прикладах на Kotlin: Multimap, BiMap, Table, кеш, робота з рядками, хешування, IO та конкурентність."
language: uk
updated: 2026-01-21
---
Google Guava — це бібліотека з розширеним набором утиліт для Java, яка спрощує багато аспектів розробки. Вона пропонує безліч корисних класів і методів для роботи з колекціями, кешуванням, функціональним програмуванням, обробкою рядків і багато чого іншого.

Основні можливості Google Guava:

## Колекції

Guava значно розширює стандартні колекції Java, надаючи нові структури даних та утиліти для роботи з ними.

**Multimap:**\
Колекція, яка дозволяє зберігати кілька значень для одного ключа.\
**Приклади:** ArrayListMultimap, HashMultimap, LinkedListMultimap.

```kotlin
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.

```kotlin
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.

```kotlin
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.

```kotlin
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.

```kotlin
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.

```kotlin
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.

```kotlin
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.

```kotlin
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:**\
Утиліта для розбиття рядка за роздільником з різними опціями (пропуск порожніх рядків, обрізання пробілів тощо).

```kotlin
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:**\
Утиліта для об'єднання колекції рядків або інших об'єктів у рядок із зазначеним роздільником.

```kotlin
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:**\
Утиліта для пошуку та маніпуляцій із символами в рядку.

```kotlin
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 та ін.).

```kotlin
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:**\
Вбудована реалізація кешу з підтримкою автоматичного видалення даних за часом життя або обсягом.

```kotlin
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:**\
Кеш, який автоматично завантажує дані, якщо їх немає, за допомогою функції завантаження.

```kotlin
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:**\
Обробник для подій видалення елементів із кешу.

```kotlin
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:**\
Дозволяє гнучко налаштовувати кеш з обмеженнями за часом життя елементів, кількістю, обробкою конкуренції тощо.

```kotlin
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:**\
Інтерфейс для визначення логічної умови для об'єктів.

```kotlin
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:**\
Інтерфейс для перетворення одного об'єкта на інший.

```kotlin
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:**\
Утиліта для лінивого та мемоїзованого постачання значень.

```kotlin
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).

```kotlin
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:**\
Утиліта для обробки колекцій з підтримкою методів функціональної композиції (фільтрація, перетворення тощо).

```kotlin
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 винятків).

```kotlin
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 для повторної спроби виконання операції:

```kotlin
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():

```kotlin
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:**\
Утиліти для перетворення та роботи з примітивними типами.

```kotlin
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 (перший менший за другий)
}
```

```kotlin
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]
}
```

```kotlin
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 (перший більший за другий)
}
```

```kotlin
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]
}
```

```kotlin
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:**\
Підтримка беззнакових цілих чисел.

```kotlin
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.

```kotlin
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, наприклад, фільтрація, перетворення, об'єднання тощо.

Приклад фільтрації ітератора:

```kotlin
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
    }
}
```

Приклад перетворення ітератора:

```kotlin
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:**\
Ітератор, який дозволяє "зазирнути" на наступний елемент без його вилучення.

```kotlin
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:**\
Шаблон для спрощення створення користувацьких ітераторів.

```kotlin
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 гарантує, що елементи не можуть бути змінені під час ітерації.

```kotlin
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 працює як з колекціями, так і з ітераторами, він забезпечує функціональний стиль роботи з ними, поєднуючи фільтрацію, перетворення та інші операції.

```kotlin
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 можна зберігати інформацію про тип.

```kotlin
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 дозволяє легко перетворювати об'єкти з одного типу на інший. Це корисно, коли потрібно визначити перетворення в обох напрямках — туди й назад.

```kotlin
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), пропонуючи утиліти для їх перетворення з рядків і навпаки.

```kotlin
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:**\
Таймер для вимірювання часу виконання операцій.

```kotlin
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.

```kotlin
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 надає шаблон для створення процесів, які мають виконуватися в окремих потоках. Цей клас дозволяє легко запускати сервіс у фоновому потоці та обробляти його завершення.

```kotlin
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:**\
Надає можливість керувати кількома сервісами одночасно, відстежувати їхні стани та коректно завершувати їхню роботу.

```kotlin
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, що підтримує додавання колбеків.

```kotlin
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, включно з композицією та комбінуванням кількох майбутніх значень.

```kotlin
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).

```kotlin
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. Це зручно для керування асинхронними завданнями з колбеками.

```kotlin
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:**\
Використовується для обмеження швидкості виконання операцій, наприклад, для контролю кількості запитів за секунду.

```kotlin
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()}")
    }
}
```

## Утиліти для роботи з IO

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

Приклад читання та запису файлів:

```kotlin
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!
}
```

Приклад копіювання файлу:

```kotlin
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}")
}
```

Приклад переміщення файлу:

```kotlin
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:

```kotlin
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:

```kotlin
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:**\
Утиліти для роботи з байтовими потоками.

Приклад копіювання даних з одного потоку в інший:

```kotlin
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:

```kotlin
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:**\
Спрощує керування ресурсами, такими як файли та потоки, забезпечуючи їх автоматичне закриття у разі помилки або успішного завершення роботи.

```kotlin
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.

```kotlin
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)
}
```

## Утиліти для роботи з потоками (Streams)

**Stream:**\
Розширені утиліти для роботи з потоками даних.

```kotlin
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 дозволяє об'єднати два потоки в один за допомогою функції:

```kotlin
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 (Хешування)

**Hashing:**\
Підтримка різних хеш-функцій (MD5, SHA-256, MurmurHash та ін.).

```kotlin
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
}
```

```kotlin
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:**\
Дозволяє поетапно додавати дані для хешування. Це корисно, якщо потрібно хешувати дані, що надходять із кількох джерел, або дані, які необхідно хешувати послідовно.

```kotlin
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:**\
Представляє результат хешування, який можна перетворити на рядок, масив байтів або використати для порівняння.

```kotlin
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:**\
імовірнісна структура даних, яка використовується для перевірки належності елемента до множини з невеликою ймовірністю хибних спрацювань. Цей інструмент корисний, коли потрібно економити пам'ять і можна допустити невелику ймовірність хибних спрацювань.

```kotlin
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.

```kotlin
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 не вважається стабільним, і його використання в продакшені рекомендується з обережністю, оскільки він може змінитися без зворотної сумісності.

```kotlin
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 використовується для того, щоб показати, що певний метод або поле доступні лише для тестів. Це може бути корисно для позначення методів, які не призначені для використання поза тестами, але є відкритими для тестування.

```kotlin
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.

```kotlin
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 вказує, що значення, яке повертає метод, має бути перевірене, і його ігнорування може призвести до помилки. Ця анотація корисна для методів, результат яких важливо обробити.

```kotlin
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 дозволяє ігнорувати значення, яке повертає метод. Вона корисна в ситуаціях, коли значення, що повертається, необов'язково має використовуватися.

```kotlin
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.

```kotlin
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.

```kotlin
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.

```kotlin
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.

```kotlin
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.

```kotlin
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:** \
перевіряє, що початковий і кінцевий індекси перебувають у допустимому діапазоні і що початковий індекс не більший за кінцевий.

```kotlin
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 ](https://t.me/RomanKryvolapov)
