Roman Kryvolapov Engineering Blog

Google Guava в примери

Google Guava е библиотека с разширен набор от помощни инструменти за Java, която улеснява много аспекти от разработката. Тя предлага множество полезни класове и методи за работа с колекции, кеширане, функционално програмиране, обработка на низове и още много неща.

Основни възможности на Google Guava:

Колекции

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

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

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

Multiset:
Колекция, която позволява да се съхраняват няколко копия на един и същ елемент.
Примери: HashMultiset, TreeMultiset.

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

BiMap:
Двупосочна Map, в която на всеки ключ съответства уникална стойност и обратно.
Пример: HashBiMap.

Kotlin
import com.google.common.collect.HashBiMap
fun bimapExample() {
val biMap = HashBiMap.create<String, String>()
biMap["one"] = "1"
biMap["two"] = "2"
// Достъп до стойност по ключ
println(biMap["one"]) // 1
// Обратен достъп до ключ по стойност
println(biMap.inverse()["1"]) // one
}

Table:
Двумерна Map, еквивалентна на Map<R, Map<C, V>>.
Пример: HashBasedTable.

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

ClassToInstanceMap:
Map, в която ключовете са класове, а стойностите са екземпляри на тези класове.
Пример: MutableClassToInstanceMap.

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

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

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

RangeMap:
Map, чиито ключове са диапазони, а стойностите – обекти, свързани с тези диапазони.
Пример: TreeRangeMap.

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

Immutable Collections:
Неизменяеми версии на стандартните колекции за по-голяма сигурност и оптимизация.
Примери: ImmutableList, ImmutableSet, ImmutableMap, ImmutableMultimap, ImmutableTable.

Kotlin
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableSet
fun immutableCollectionsExample() {
val immutableList: List<String> = ImmutableList.of("apple", "banana", "cherry")
val immutableSet: Set<String> = ImmutableSet.of("apple", "banana", "cherry")
println(immutableList) // [apple, banana, cherry]
println(immutableSet) // [apple, banana, cherry]
// Тези колекции са неизменяеми, опитът да се добави елемент предизвиква грешка
// immutableList.add("pear") // UnsupportedOperationException
}

Работа с низове

Splitter:
Помощен инструмент за разделяне на низ по разделител с различни опции (пропускане на празни низове, изрязване на интервали и т. н.).

Kotlin
import com.google.common.base.Splitter
fun splitterExample() {
val input = "apple, banana, , orange,,"
val result = Splitter.on(',')
.trimResults() // Изрязваме интервалите
.omitEmptyStrings() // Пропускаме празните низове
.splitToList(input)
println(result) // [apple, banana, orange]
}

Joiner:
Помощен инструмент за обединяване на колекция от низове или други обекти в низ с посочен разделител.

Kotlin
import com.google.common.base.Joiner
fun joinerExample() {
val fruits = listOf("apple", null, "banana", "orange")
// Joiner ще пропуска null стойностите
val result = Joiner.on(", ")
.skipNulls()
.join(fruits)
println(result) // apple, banana, orange
}

CharMatcher:
Помощен инструмент за търсене и манипулации със символи в низ.

Kotlin
import com.google.common.base.CharMatcher
fun charMatcherExample() {
val input = "Hello123 World456"
// Премахваме всички цифри
val onlyLetters = CharMatcher.javaDigit().removeFrom(input)
println(onlyLetters) // Hello World
// Оставяме само цифрите
val onlyDigits = CharMatcher.javaDigit().retainFrom(input)
println(onlyDigits) // 123456
// Изрязваме интервалите и ги заменяме със запетаи
val trimmed = CharMatcher.whitespace().trimAndCollapseFrom(input, ',')
println(trimmed) // Hello123,World456
}

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

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

Кеширане

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

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

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

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

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

RemovalListener:
Обработчик за събитията за премахване на елементи от кеша.

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

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

Kotlin
import com.google.common.cache.CacheBuilder
import com.google.common.cache.Cache
fun cacheBuilderExample() {
// Създаваме кеш с максимален брой елементи и време на живот 5 минути
val cache: Cache<String, String> = CacheBuilder.newBuilder()
.maximumSize(200) // Ограничение по брой елементи
.expireAfterWrite(5, TimeUnit.MINUTES) // Елементите живеят 5 минути
.build()
// Добавяме елемент
cache.put("key1", "value1")
// Получаваме елемента
println(cache.getIfPresent("key1")) // value1
// След 5 минути елементът ще бъде премахнат автоматично
}

Функционално програмиране

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

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

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

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

Suppliers:
Помощен инструмент за лениво и мемоизирано доставяне на стойности.

Kotlin
import com.google.common.base.Suppliers
import java.util.concurrent.TimeUnit
fun supplierExample() {
// Лениво изчисляване на низ с мемоизация за 10 секунди
val supplier = Suppliers.memoizeWithExpiration(
Suppliers.ofInstance("Hello, world!"),
10, TimeUnit.SECONDS
)
// Получаваме стойността
println(supplier.get()) // Hello, world!
// Стойността се кешира за 10 секунди, след което ще бъде преизчислена
}

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

Kotlin
import com.google.common.base.Optional
fun optionalExample() {
val optionalPresent: Optional<String> = Optional.of("Hello")
val optionalAbsent: Optional<String> = Optional.absent()
// Проверка за наличие на стойност
if (optionalPresent.isPresent) {
println(optionalPresent.get()) // Hello
}
// Връщане на стойност по подразбиране, ако няма такава
println(optionalAbsent.or("Default Value")) // Default Value
}

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

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

Работа с изключения

Throwables:
Помощни инструменти за работа с изключения (например преобразуване на checked изключения в runtime изключения).

Kotlin
import com.google.common.base.Throwables
fun throwablesExample() {
try {
// Изкуствено създаваме изключение
throw IllegalArgumentException("Illegal argument!")
} catch (e: Exception) {
// Извеждаме пълната информация за изключението
println(Throwables.getStackTraceAsString(e))
// Проверка и повторно хвърляне на изключението, ако е от определен тип
if (Throwables.getRootCause(e) is IllegalArgumentException) {
throw Throwables.propagate(e)
}
}
}

Retryer:
Помощен инструмент за повторно изпълнение на операции при възникване на изключения с поддръжка на стратегии за изчакване и спиране. Google Guava не съдържа вградена поддръжка на Retryer, но има популярни библиотеки, като Failsafe или Guava-Retrying, които могат да се използват за повторни опити. Ето пример за използване на библиотеката Guava-Retrying за повторен опит за изпълнение на операция:

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

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

Kotlin
import com.google.common.base.Throwables
fun propagateExample() {
try {
throwCheckedException()
} catch (e: Exception) {
// Преобразуваме checked изключението в unchecked изключение
throw Throwables.propagate(e)
}
}
fun throwCheckedException() {
throw Exception("Checked exception!")
}

Работа с примитиви

Google Guava предлага удобни помощни инструменти за работа с примитиви, като Ints, Longs, Doubles, Booleans, Bytes, както и поддръжка на цели числа без знак и на неизменяеми колекции за примитиви. Тези инструменти позволяват да се избегне работата с обвивките на примитивните типове и да се изпълняват различни операции по-ефективно.

Ints, Longs, Doubles, Booleans, Floats, Bytes:
Помощни инструменти за преобразуване и работа с примитивни типове.

Kotlin
import com.google.common.primitives.Ints
fun intsExample() {
val numbers = listOf(1, 2, 3, 4, 5)
// Преобразуване на списък в масив от int
val intArray = Ints.toArray(numbers)
println(intArray.contentToString()) // [1, 2, 3, 4, 5]
// Търсене на максималната и минималната стойност
println(Ints.max(1, 3, 5, 2)) // 5
println(Ints.min(1, 3, 5, 2)) // 1
// Проверка за наличие на число в масива
println(Ints.contains(intArray, 3)) // true
// Сравнение на два int
println(Ints.compare(5, 10)) // -1 (първият е по-малък от втория)
}
Kotlin
import com.google.common.primitives.Longs
fun longsExample() {
val longArray = longArrayOf(10L, 20L, 30L)
// Преобразуване на масив от long в списък
val longList = Longs.asList(*longArray)
println(longList) // [10, 20, 30]
// Преобразуване на списъка обратно в масив
val array = Longs.toArray(longList)
println(array.contentToString()) // [10, 20, 30]
}
Kotlin
import com.google.common.primitives.Doubles
fun doublesExample() {
val doubleArray = doubleArrayOf(1.1, 2.2, 3.3)
// Преобразуване на масив от double в списък
val doubleList = Doubles.asList(*doubleArray)
println(doubleList) // [1.1, 2.2, 3.3]
// Преобразуване на списъка обратно в масив
val array = Doubles.toArray(doubleList)
println(array.contentToString()) // [1.1, 2.2, 3.3]
// Сравнение на два double
println(Doubles.compare(3.14, 2.71)) // 1 (първият е по-голям от втория)
}
Kotlin
import com.google.common.primitives.Booleans
fun booleansExample() {
val booleanArray = booleanArrayOf(true, false, true)
// Преобразуване на масив от boolean в списък
val booleanList = Booleans.asList(*booleanArray)
println(booleanList) // [true, false, true]
// Преобразуване на списъка обратно в масив
val array = Booleans.toArray(booleanList)
println(array.contentToString()) // [true, false, true]
}
Kotlin
import com.google.common.primitives.Bytes
fun bytesExample() {
val byteArray = byteArrayOf(1, 2, 3)
// Преобразуване на масив от byte в списък
val byteList = Bytes.asList(*byteArray)
println(byteList) // [1, 2, 3]
// Преобразуване на списъка обратно в масив
val array = Bytes.toArray(byteList)
println(array.contentToString()) // [1, 2, 3]
}

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

Kotlin
import com.google.common.primitives.UnsignedInts
import com.google.common.primitives.UnsignedLongs
fun unsignedIntsExample() {
val unsignedInt = UnsignedInts.parseUnsignedInt("4294967295") // Максимална стойност на unsigned int
println(unsignedInt) // 4294967295 (но се съхранява като обикновен int)
val compared = UnsignedInts.compare(4000000000.toInt(), 3000000000.toInt())
println(compared) // 1 (първото е по-голямо от второто)
}
fun unsignedLongsExample() {
val unsignedLong = UnsignedLongs.parseUnsignedLong("18446744073709551615") // Максимална стойност на unsigned long
println(unsignedLong) // 18446744073709551615 (но се съхранява като обикновен long)
val compared = UnsignedLongs.compare(18000000000000000000L, 17000000000000000000L)
println(compared) // 1 (първото е по-голямо от второто)
}

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

Kotlin
import com.google.common.primitives.ImmutableIntArray
fun immutableIntArrayExample() {
// Създаване на неизменяем масив
val immutableIntArray = ImmutableIntArray.of(1, 2, 3, 4, 5)
println(immutableIntArray) // [1, 2, 3, 4, 5]
// Получаване на елемент
println(immutableIntArray.get(2)) // 3
// Опитът да се промени масивът предизвиква грешка
// immutableIntArray.set(0, 10) // UnsupportedOperationException
}

Итератори и генератори

Google Guava предоставя мощни помощни инструменти за работа с итератори и генератори на данни. Те улесняват филтрирането, преобразуването и комбинирането на колекции, като предлагат гъвкав и удобен API. Ще разгледаме няколко примера за използване на итератори и генератори на данни на Kotlin.

Iterators:
Помощни инструменти за работа с Java Iterator, например филтриране, преобразуване, обединяване и т. н.

Пример за филтриране на итератор:

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

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

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

PeekingIterator:
Итератор, който позволява да „надникнем“ към следващия елемент, без да го извличаме.

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

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

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

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

Kotlin
import com.google.common.collect.UnmodifiableIterator
fun unmodifiableIteratorExample() {
val numbers = listOf(1, 2, 3, 4, 5)
// Създаваме неизменяем итератор
val unmodifiableIterator: UnmodifiableIterator<Int> = object : UnmodifiableIterator<Int>() {
private val iter = numbers.iterator()
override fun hasNext(): Boolean = iter.hasNext()
override fun next(): Int = iter.next()
}
while (unmodifiableIterator.hasNext()) {
println(unmodifiableIterator.next()) // 1, 2, 3, 4, 5
}
// unmodifiableIterator.remove() // UnsupportedOperationException
}

FluentIterable:
Функционална работа с колекции. Макар че FluentIterable работи както с колекции, така и с итератори, той осигурява функционален стил на работа с тях, като обединява филтриране, преобразуване и други операции.

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

Преобразуване на типове

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

TypeToken:
Помощен инструмент за работа с типове, който поддържа генерици. TypeToken позволява да се работи с типове, особено с генерици, и да се извлича информация за тях по време на изпълнение. В стандартните Java и Kotlin системата от типове изтрива информацията за генериците по време на изпълнение, но с помощта на TypeToken може да се запази информацията за типа.

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

Converter:
Абстракция за преобразуване на един тип в друг с поддръжка на обратна конверсия. Converter позволява лесно да се преобразуват обекти от един тип в друг. Това е полезно, когато трябва да се определи преобразуване в двете посоки – напред и назад.

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

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

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

Работа с времето

Stopwatch:
Таймер за измерване на времето за изпълнение на операции.

Kotlin
import com.google.common.base.Stopwatch
import java.util.concurrent.TimeUnit
fun stopwatchExample() {
// Създаваме таймер
val stopwatch = Stopwatch.createStarted()
// Изпълняваме някаква операция
Thread.sleep(1000)
// Спираме таймера
stopwatch.stop()
// Получаваме резултата в милисекунди
println("Elapsed time: ${stopwatch.elapsed(TimeUnit.MILLISECONDS)} ms") // Например: Elapsed time: 1001 ms
// Таймерът може да се стартира отново
stopwatch.reset().start()
Thread.sleep(500)
stopwatch.stop()
println("Elapsed time after reset: ${stopwatch.elapsed(TimeUnit.MILLISECONDS)} ms") // Например: Elapsed time after reset: 500 ms
}

Timestamps:
Помощни инструменти за работа с отпечатъци на времето. Guava предлага и инструменти за работа с времеви отпечатъци и времеви интервали. Стандартен инструмент Timestamps в Guava обаче няма. Вместо него обикновено се използват класовете от пакета java.time (от Java 8). Въпреки това Stopwatch е основният инструмент на Guava за работа с времето.

Пример за работа с класовете за време от Java 8 (за работа с времеви отпечатъци):
Макар че това не е част от Google Guava, от Java 8 можем да използваме java.time за работа с времеви отпечатъци и това често се прави заедно с инструментите на Guava.

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

Работа с процеси

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

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

AbstractExecutionThreadService:
AbstractExecutionThreadService предоставя шаблон за създаване на процеси, които трябва да се изпълняват в отделни нишки. Този клас позволява лесно да се стартира услуга във фонова нишка и да се обработва нейното завършване.

Kotlin
import com.google.common.util.concurrent.AbstractExecutionThreadService
fun abstractExecutionThreadServiceExample() {
// Създаваме услуга, която работи в отделна нишка
val backgroundService: Service = object : AbstractExecutionThreadService() {
override fun run() {
println("Background service is running")
// Емулация на дълга операция
Thread.sleep(5000)
println("Background service has completed")
}
override fun startUp() {
println("Background service is starting up")
}
override fun shutDown() {
println("Background service is shutting down")
}
}
// Стартираме услугата
backgroundService.startAsync()
backgroundService.awaitRunning()
// Спираме услугата след работа
backgroundService.stopAsync()
backgroundService.awaitTerminated()
}

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

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

Конкурентно програмиране

ListenableFuture:
Разширение на стандартния Future, което поддържа добавяне на колбеци.

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

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

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

MoreExecutors:
Разширени помощни инструменти за работа с екзекютори (например преобразуване на екзекютор в ListenableFuture).

Kotlin
import com.google.common.util.concurrent.MoreExecutors
import java.util.concurrent.Executors
fun moreExecutorsExample() {
// Създаваме executor с автоматично завършване на работата, когато всички задачи приключат
val executor = MoreExecutors.getExitingExecutorService(Executors.newSingleThreadExecutor())
// Стартираме задача
executor.submit {
println("Task is running in exiting executor")
}
// Екзекюторът автоматично ще завърши работата си, когато всички задачи приключат
}

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

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

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

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

Помощни инструменти за работа с IO

Files:
Помощни инструменти за работа с файлове (копиране, преместване, четене, запис и т. н.).

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

Kotlin
import com.google.common.io.Files
import java.io.File
import java.nio.charset.StandardCharsets
fun filesExample() {
val file = File("example.txt")
// Записваме данни във файла
Files.asCharSink(file, StandardCharsets.UTF_8).write("Hello, world!")
// Четем данни от файла
val content = Files.asCharSource(file, StandardCharsets.UTF_8).read()
println("File content: $content") // Hello, world!
}

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

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

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

Kotlin
import com.google.common.io.Files
import java.io.File
fun fileMoveExample() {
val sourceFile = File("source.txt")
val targetFile = File("moved.txt")
// Преместваме файла
Files.move(sourceFile, targetFile)
println("File moved from ${sourceFile.name} to ${targetFile.name}")
}

CharStreams:
Помощни инструменти за работа с потоци от символи.

Пример за четене на данни от Reader:

Kotlin
import com.google.common.io.CharStreams
import java.io.StringReader
fun charStreamsExample() {
val reader = StringReader("Hello from StringReader!")
// Четем всички данни от Reader
val content = CharStreams.toString(reader)
println(content) // Hello from StringReader!
}

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

Kotlin
import com.google.common.io.CharStreams
import java.io.StringWriter
fun charStreamsWriteExample() {
val writer = StringWriter()
// Записваме данни във Writer
CharStreams.asWriter(writer).write("Hello to StringWriter!")
println(writer.toString()) // Hello to StringWriter!
}

ByteStreams:
Помощни инструменти за работа с байтови потоци.

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

Kotlin
import com.google.common.io.ByteStreams
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
fun byteStreamsExample() {
val input = ByteArrayInputStream("Hello in bytes!".toByteArray())
val output = ByteArrayOutputStream()
// Копираме данните от InputStream в OutputStream
ByteStreams.copy(input, output)
println(output.toString()) // Hello in bytes!
}

Пример за четене на всички данни от InputStream:

Kotlin
import com.google.common.io.ByteStreams
import java.io.ByteArrayInputStream
fun byteStreamsReadExample() {
val input = ByteArrayInputStream("Some byte data".toByteArray())
// Четем всички данни от InputStream
val byteData = ByteStreams.toByteArray(input)
println(String(byteData)) // Some byte data
}

Closer:
Опростява управлението на ресурси, като файлове и потоци, като осигурява автоматичното им затваряне както при грешка, така и при успешно завършване на работата.

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

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

Kotlin
import com.google.common.io.Resources
import java.nio.charset.StandardCharsets
fun resourcesExample() {
// Четем ресурс от classpath
val url = Resources.getResource("config.txt")
val content = Resources.toString(url, StandardCharsets.UTF_8)
println(content)
}

Помощни инструменти за работа с потоци (Streams)

Stream:
Разширени помощни инструменти за работа с потоци от данни.

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

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

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

Hashing (Хеширане)

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

Kotlin
import com.google.common.hash.Hashing
import java.nio.charset.StandardCharsets
fun hashingExample() {
val input = "Hello, World!"
// Използваме SHA-256 за хеширане на низа
val hash = Hashing.sha256()
.hashString(input, StandardCharsets.UTF_8)
.toString()
println("SHA-256 hash: $hash")
// Пример за изход: SHA-256 hash: c0535e4be2b79ffd93291305436bf889314e4a3d9a91fb217b0b14cba2999e66
}
Kotlin
import com.google.common.hash.Hashing
import java.nio.charset.StandardCharsets
fun murmurHashExample() {
val input = "Guava Hashing Example"
// Използваме MurmurHash3 за хеширане на низа
val hash = Hashing.murmur3_128()
.hashString(input, StandardCharsets.UTF_8)
.toString()
println("MurmurHash3 (128-bit) hash: $hash")
// Пример за изход: MurmurHash3 (128-bit) hash: 2f17b911e9d9b22fa10f0ab091d02e17
}

Hasher:
Позволява данните за хеширане да се добавят поетапно. Това е полезно, ако трябва да се хешират данни, постъпващи от няколко източника, или данни, които трябва да се хешират последователно.

Kotlin
import com.google.common.hash.Hashing
import java.nio.charset.StandardCharsets
fun hasherExample() {
val hasher = Hashing.sha256().newHasher()
// Добавяме данните за хеширане на части
hasher.putString("Hello", StandardCharsets.UTF_8)
hasher.putInt(12345)
hasher.putLong(9876543210L)
// Получаваме крайния хеш
val hash = hasher.hash().toString()
println("Combined SHA-256 hash: $hash")
// Пример за изход: Combined SHA-256 hash: 3627904d36e33402e1dba8e132ea9d222f3baaf3dcb9bfb144f50c3cf555160b
}

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

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

BloomFilter:
вероятностна структура от данни, която се използва за проверка дали елемент принадлежи към множество, с малка вероятност за фалшиви положителни резултати. Този инструмент е полезен, когато се изисква да се спестява памет и може да се допусне малка вероятност за фалшиви положителни резултати.

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

Custom Hash Functions:
Създаване на собствена хеш функция. Ако вградените алгоритми за хеширане не са подходящи, може да се създаде собствена хеш функция чрез имплементиране на интерфейса HashFunction.

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

Математически инструменти

Ints, Longs, Doubles, BigIntegerMath, DoubleMath:
Помощни инструменти за математически операции с числа от различни типове.

RoundingMode:
Помощни инструменти за закръгляване на числа.

Работа с анотации

@Beta:
Маркировка за нестабилни API. Показва, че API може да се промени в бъдеще. Анотацията @Beta се използва за маркиране на класове, методи и API, които могат да се променят в бъдещи версии. Този API не се счита за стабилен и използването му в продукция се препоръчва да става внимателно, тъй като може да се промени без обратна съвместимост.

Kotlin
import com.google.common.annotations.Beta
@Beta
class BetaFeature {
fun newFeature() {
println("This feature is in beta and may change!")
}
}
fun betaFeatureExample() {
val feature = BetaFeature()
feature.newFeature()
}

@VisibleForTesting:
Достъпност за тестване. Показва, че методът или класът е предназначен за използване в тестове. Анотацията @VisibleForTesting се използва, за да се покаже, че определен метод или поле са достъпни само за тестове. Това може да е полезно за означаване на методи, които не са предназначени за използване извън тестовете, но са отворени за тестване.

Kotlin
import com.google.common.annotations.VisibleForTesting
class ProductionClass {
// Този метод е достъпен само за тестове
@VisibleForTesting
internal fun internalLogicForTests(): Int {
return 42
}
fun publicLogic(): String {
return "Public logic result"
}
}
fun testVisibilityExample() {
val productionClass = ProductionClass()
// Методът може да се извиква в тестове
val testResult = productionClass.internalLogicForTests()
println("Test logic result: $testResult") // Test logic result: 42
}

@Nullable:
Анотация за указване на възможност за null. Анотацията @Nullable показва, че методът може да връща null или че параметърът на метода може да приема стойност null. Това помага да се повишат сигурността и прозрачността на кода, като се показва къде може да възникне null.

Kotlin
import javax.annotation.Nullable
class NullableExample {
// Методът може да върне null
@Nullable
fun findValue(key: String): String? {
return if (key == "exists") "Found value" else null
}
}
fun nullableExample() {
val example = NullableExample()
val value = example.findValue("not_exists")
println("Value: $value") // Value: null
}

@CheckReturnValue:
Задължителна проверка на върнатата стойност. Анотацията @CheckReturnValue показва, че върнатата стойност на метода трябва да се провери и че пренебрегването ѝ може да доведе до грешка. Тази анотация е полезна за методи, чийто резултат е важно да се обработи.

Kotlin
import com.google.errorprone.annotations.CheckReturnValue
class CheckReturnValueExample {
@CheckReturnValue
fun computeValue(): Int {
return 42
}
}
fun checkReturnValueExample() {
val example = CheckReturnValueExample()
// Предполага се, че тази стойност трябва да бъде използвана
val result = example.computeValue()
println("Computed value: $result") // Computed value: 42
}

@CanIgnoreReturnValue:
Разрешение за пренебрегване на върнатата стойност. Анотацията @CanIgnoreReturnValue позволява върнатата стойност на метода да се пренебрегне. Тя е полезна в ситуации, когато върнатата стойност не е задължително да се използва.

Kotlin
import com.google.errorprone.annotations.CanIgnoreReturnValue
class IgnoreReturnValueExample {
@CanIgnoreReturnValue
fun performAction(): String {
return "Action performed"
}
}
fun ignoreReturnValueExample() {
val example = IgnoreReturnValueExample()
// Върнатата стойност може да се пренебрегне
example.performAction() // Стойността не е задължително да се използва
}

Други помощни инструменти

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

Инструментът Preconditions предоставя следните основни методи:

checkArgument:
използва се за проверка на условия, свързани с подадените аргументи. Ако условието не е изпълнено, се хвърля IllegalArgumentException.

Kotlin
import com.google.common.base.Preconditions
fun checkArgumentExample(age: Int) {
// Проверка дали възрастта е по-голяма или равна на 18
Preconditions.checkArgument(age >= 18, "Age must be 18 or older, but was %s", age)
println("Valid age: $age")
}
fun main() {
checkArgumentExample(20) // Valid age: 20
// checkArgumentExample(15) // IllegalArgumentException: Age must be 18 or older, but was 15
}

checkNotNull:
проверява дали подаденият аргумент не е равен на null. Ако аргументът е null, се хвърля NullPointerException.

Kotlin
import com.google.common.base.Preconditions
fun checkNotNullExample(name: String?) {
// Проверка дали името не е равно на null
val validName = Preconditions.checkNotNull(name, "Name cannot be null")
println("Valid name: $validName")
}
fun main() {
checkNotNullExample("Alice") // Valid name: Alice
// checkNotNullExample(null) // NullPointerException: Name cannot be null
}

checkState:
използва се за проверка на състоянието на обект. Ако условието не е изпълнено, се хвърля IllegalStateException.

Kotlin
import com.google.common.base.Preconditions
class StateExample(private var isInitialized: Boolean) {
fun doSomething() {
// Проверка дали обектът е инициализиран
Preconditions.checkState(isInitialized, "Object must be initialized before use")
println("Doing something")
}
fun initialize() {
isInitialized = true
}
}
fun main() {
val example = StateExample(false)
// example.doSomething() // IllegalStateException: Object must be initialized before use
example.initialize()
example.doSomething() // Doing something
}

checkElementIndex:
проверява дали индексът на елемента е в рамките на допустимия диапазон (от 0 до размера на списъка/масива). Ако индексът излиза извън границите, се хвърля IndexOutOfBoundsException.

Kotlin
import com.google.common.base.Preconditions
fun checkElementIndexExample(index: Int, list: List<String>) {
// Проверка дали индексът е в рамките на списъка
Preconditions.checkElementIndex(index, list.size, "Invalid index")
println("Element at index $index: ${list[index]}")
}
fun main() {
val list = listOf("apple", "banana", "cherry")
checkElementIndexExample(1, list) // Element at index 1: banana
// checkElementIndexExample(5, list) // IndexOutOfBoundsException: Invalid index (5) must be less than size (3)
}

checkPositionIndex:
проверява позиция в колекция или масив. Позицията е индекс, който може да сочи към началото или към края на колекцията. Ако позицията е невалидна, се хвърля IndexOutOfBoundsException.

Kotlin
import com.google.common.base.Preconditions
fun checkPositionIndexExample(position: Int, list: List<String>) {
// Проверка на позицията
Preconditions.checkPositionIndex(position, list.size, "Invalid position")
println("Valid position: $position")
}
fun main() {
val list = listOf("apple", "banana", "cherry")
checkPositionIndexExample(3, list) // Valid position: 3 (сочи към края на списъка)
// checkPositionIndexExample(5, list) // IndexOutOfBoundsException: Invalid position (5) must be less than size (3)
}

checkPositionIndexes:
проверява дали началният и крайният индекс са в допустимия диапазон и дали началният индекс не е по-голям от крайния.

Kotlin
import com.google.common.base.Preconditions
fun checkPositionIndexesExample(start: Int, end: Int, list: List<String>) {
// Проверка на диапазона от индекси
Preconditions.checkPositionIndexes(start, end, list.size)
println("Valid range: from $start to $end")
}
fun main() {
val list = listOf("apple", "banana", "cherry", "date")
checkPositionIndexesExample(0, 2, list) // Valid range: from 0 to 2
// checkPositionIndexesExample(2, 5, list) // IndexOutOfBoundsException: End index (5) must not be greater than size (4)
}

Objects:
Помощни инструменти за работа с обекти (сравнение, генериране на hashCode и т. н.).

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

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

Copyright: Roman Kryvolapov