Google Guava у прикладах
Google Guava — це бібліотека з розширеним набором утиліт для Java, яка спрощує багато аспектів розробки. Вона пропонує безліч корисних класів і методів для роботи з колекціями, кешуванням, функціональним програмуванням, обробкою рядків і багато чого іншого.
Основні можливості Google Guava:
Колекції
Guava значно розширює стандартні колекції Java, надаючи нові структури даних та утиліти для роботи з ними.
Multimap:
Колекція, яка дозволяє зберігати кілька значень для одного ключа.
Приклади: ArrayListMultimap, HashMultimap, LinkedListMultimap.
import com.google.common.collect.ArrayListMultimapimport 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.HashMultisetimport 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.HashBasedTableimport 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.MutableClassToInstanceMapimport 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.Rangeimport 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.Rangeimport 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.ImmutableListimport 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.CacheBuilderimport 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.CacheBuilderimport com.google.common.cache.CacheLoaderimport com.google.common.cache.LoadingCacheimport 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.CacheBuilderimport com.google.common.cache.RemovalListenerimport com.google.common.cache.RemovalNotificationimport 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.CacheBuilderimport 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.Predicateimport 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.Functionimport 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.Suppliersimport 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.Retryerimport com.github.rholder.retry.RetryerBuilderimport com.github.rholder.retry.StopStrategiesimport com.github.rholder.retry.WaitStrategiesimport 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.UnsignedIntsimport 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.Iteratorsimport 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.Iteratorsimport 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.Stopwatchimport 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.LocalDateTimeimport 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.AbstractExecutionThreadServiceimport com.google.common.util.concurrent.Serviceimport 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.ListenableFutureimport com.google.common.util.concurrent.MoreExecutorsimport com.google.common.util.concurrent.ListenableFutureTaskimport 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.Futuresimport com.google.common.util.concurrent.ListenableFutureTaskimport 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.MoreExecutorsimport 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.ListeningExecutorServiceimport com.google.common.util.concurrent.MoreExecutorsimport 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()}") }}Утиліти для роботи з IO
Files:
Утиліти для роботи з файлами (копіювання, переміщення, читання, запис тощо).
Приклад читання та запису файлів:
import com.google.common.io.Filesimport java.io.Fileimport 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.Filesimport 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.Filesimport 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.CharStreamsimport 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.CharStreamsimport 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.ByteStreamsimport java.io.ByteArrayInputStreamimport 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.ByteStreamsimport 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.Closerimport java.io.FileReaderimport 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.Resourcesimport 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:
Розширені утиліти для роботи з потоками даних.
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.Streamsimport 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 та ін.).
import com.google.common.hash.Hashingimport 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.Hashingimport 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.Hashingimport 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.Hashingimport 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.BloomFilterimport 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.AbstractHasherimport com.google.common.hash.HashFunctionimport 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
@Betaclass 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.