Вы знаете, запутанные циклы for и индексы для отслеживания, если / иначе и переключения случаев, проверки на ноль / проверки, преобразование / копирование / удаление / сортировка коллекций, обработка исключений… список продолжается вместе с постоянно растущими номерами строк и нагрузкой на обслуживание ,
На ум приходит отличная цитата Тони Хоара.
Существует два способа конструирования программного обеспечения: один из них заключается в том, чтобы сделать его настолько простым, чтобы в нем явно не было недостатков, а другой способ — сделать его настолько сложным, чтобы в нем не было очевидных недостатков.
Другими словами: дьявол кроется в деталях.
В Apache Commons есть некоторые из самых замечательных библиотек, дополняющих API JDK, но этот пост не о Commons. Речь идет о Google Guava, который во многих отношениях похож на Commons. Он предоставляет единую библиотеку для часто используемых повседневных задач, таких как обработка коллекций, обработка строк, параллелизм, ввод-вывод, примитивы, исключения и т. Д.
В Гуаве так много хороших вещей, и у меня не будет времени, чтобы просмотреть всю библиотеку, но вот хотя бы несколько примеров того, что она может сделать.
Объекты
Объекты позволяют легко реализовать хэш-код / равнозначные значения, не перегружая ваши классы (автогенерация Eclipse, на мой вкус, немного многословна).
Классы, которые реализуют toString, действительно приятны для использования при отладке и ведении журнала, но могут быть очень трудной реализацией. Objects.toStringHelper делает это действительно простым, а также помогает поддерживать согласованный формат для печати объектов.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
public class Item { private String id; private String name; public Item(String id, String name) { this .id = id; this .id = name; } public String getId() { return id; } public String getName() { return name; } @Override public int hashCode() { return Objects.hashCode(getId(), getName()); } @Override public String toString() { return Objects.toStringHelper( this ).add( "id" , getId()).add( "name" , getName()).toString(); } @Override public boolean equals(Object o) { if (!(o instanceof Item)) { return false ; } Item other = (Item) o; return Objects.equal(getId(), other.getId()) && Objects.equal(getName(), other.getName()); } } |
Печать этого класса выводит что-то вроде этого.
1
|
Item{id= 1 , name=Book} |
Throwables
Обтекание исходного объекта исключения не всегда подходит, потому что оно может вызвать ClassNotFoundException в клиентском коде, если происходит обмен данными между не связанными загрузчиками классов или если они сериализуются по проводам. Throwables может отделить эту зависимость, позволяя удаленным клиентам видеть трассировку стека путем преобразования его в строку.
1
2
3
4
5
|
try { // throws implementation specific exception } catch (InternalException e) { throw new ApiException( "reason" , Throwables.getStackTraceAsString(e)); } |
итерируемыми
Конкатенация двух отдельных коллекций и выполнение операций над результатом может вызвать много беспорядка. Итераторы на помощь. Найдите минутку и подумайте, как может выглядеть код без Iterables.concat.
1
2
3
|
for (Item item : Iterables.concat(books, electronics)) { // do something useful } |
Multimaps
Multimap похож на карту, но позволяет хранить несколько значений для каждого ключа. Следующий пример представляет собой вариант безопасного гетерогенного контейнера с использованием мультикарты для реализации каталога товаров.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
public class ProductCatalogue { private Multimap<Class,? extends Item>, Item> catalogue = ArrayListMultimap.create(); public void add(Item item) { catalogue.put(item.getClass(), item); } public <T extends Item> Collection<Item> list(Class<T> clazz) { return catalogue.get(clazz); } } ProductCatalogue catalogue = new ProductCatalogue(); catalogue.add( new Book( "1" , "Book1" )); catalogue.add( new Movie( "2" , "Movie1" )); // only get books System.out.println( "Books " + catalogue.list(Book. class )); // only get movies System.out.println( "Movies " + catalogue.list(Movie. class )); |
BiMap
BiMap реализует двустороннюю связь один к одному между ключом и значением Карты. Вот пример использования языкового кода для получения языка и наоборот.
1
2
3
4
5
6
|
BiMap<String, String> languageCodes = HashBiMap.create(); languageCodes.put( "en" , "English" ); languageCodes.put( "fr" , "French" ); languageCodes.put( "zh" , "Chinese" ); assert "English" == languageCodes.get( "en" ); assert "en" == languageCodes.inverse().get( "English" ); |
Предпосылками
Большинство классов имеют ограничения на значения, данные им в конструкторе и методах. Недопустимые значения следует наращивать как можно скорее, выполняя явные проверки достоверности перед выполнением. Лучше быстро потерпеть неудачу, чем позже, с неожиданным исключением или хуже, молча вычислить неверный результат.
1
2
3
4
5
|
public Item(String id, String name) { this .id = Preconditions.checkNotNull(id, "id must not be null" ); this .name = Preconditions.checkNotNull(name, "name must not be null" ); Preconditions.checkArgument(name.length() > 6 , "name must be longer than 6 chars" ); } |
Ограничения
Ограничения аналогичны предварительным условиям в том смысле, что они могут ограничивать значения, добавляемые в коллекцию. Это значительно упрощает использование коллекций и делает их более понятными, поскольку ограничения отделены от бизнес-кода.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public class Voyage { private Country targetcountry; private int capacity; private List<Cargo> items = Constraints.constrainedList( new ArrayList<Cargo>(), new Constraint<Cargo>() { @Override public Cargo checkElement(Cargo cargo) { Preconditions.checkNotNull(cargo); Preconditions.checkArgument(targetcountry.allows(cargo)); Preconditions.checkArgument(cargo.getUnits() gt; 0 ); return cargo; } }); public void load(List<Cargo> cargos) { items.addAll(cargos); } } |
Предикаты и функции
Предикаты оценивают, является ли что-то истинным или ложным, но также могут быть объединены в более сложные оценки с использованием «и», «или», «не» и «в».
Что обычно требует цикла for и кучу операторов if, теперь может быть сведено к одной строке. Насколько это сладко?
01
02
03
04
05
06
07
08
09
10
|
Predicate<Item> heavyItemPolicy = new Predicate<Item>() { @Override public boolean apply(Item item) { if (item.getWeight() > 1000 ){ return true ; } return false ; } }; Collection<Item> heavyItems = Collections2.filter(order, heavyItemPolicy); |
Вы также можете использовать Maps.filterKeys или Iterables.filter аналогичным образом. Но имейте в виду, что удаление из модификации является двунаправленным. например, удаление из источника влияет на результат и наоборот.
Функции, с другой стороны, это способ преобразования одного объекта в другой. Например, конвертировать параллелизм в порядке элементов.
1
2
3
4
5
6
7
|
Function currencyConverter = new Function<Double, Item>() { @Override public Double apply(Item item) { return item.getPrice() * ANOTHER_CURRENCY; } } Collection<Double> prices = Collections2.transform(order, currencyConverter); |
Вы также можете использовать Maps.transformValues или Iterables.transform аналогичным образом.
API запросов
Я уже давно думаю о том, как создавать простые, но мощные поддельные объекты . Но я не хочу, чтобы подделки сами превращались в бремя обслуживания, поэтому их легко реализовать. Моя интуиция подсказывает мне, что мне нужна система управления состоянием общего назначения, чтобы это работало. И поэтому, используя предикаты, я создал небольшой интерфейс запросов, взаимодействующий с хранилищем в памяти.
1
2
3
4
5
|
InMemoryStorage storage = new InMemoryStorage(); // add a few Item.class objects to storage Criteria middleprice = field( "price" ).is(largerThan( 100 )).and(lessThan( 200 )); Criteria expired = field( "expires" ).is(after(currentDate)); Collection<Item> result = storage.select(middleprice.and(not(expired))).from(Item. class ); |
Я на самом деле доволен результатом — коротким, компактным, понятным и безопасным.
Я не буду вдаваться в подробности, но, пожалуйста, ознакомьтесь с реализацией Criteria и InMemoryStorage , а также с тестами .
Я надеюсь, что эти примеры побудят вас изучить Guava и использовать его, чтобы сделать ваш код более читабельным, надежным и поддерживаемым.
И, наконец, я действительно надеюсь, что многие из этих средств скоро достигнут стандартной Java.
Ссылка: Дьявол в деталях от нашего партнера JCG Кристоффера Шегрена в блоге Deep Hacks .
Статьи по Теме :