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<R, Map<C, V>>.
Пример: 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.