Probably, any programmer has seen the code dazzling with a large number of repetitions and implementing low-level actions right in the middle of business logic. For example, in the middle of a method that prints a report, there may be such a fragment of code concatenating strings:
StringBuilder sb = new StringBuilder(); for (Iterator<String> i = debtors.iterator(); i.hasNext();) { if (sb.length() != 0) { sb.append(", "); } sb.append(i.next()); } out.println("Debtors: " + sb.toString());
It is clear that this code could be more straightforward, for example, in Java 8 you can write this:
out.println("Debtors: " + String.join(", ", debtors));
In this way, it is much clearer what is happening. Google Guava is a set of open-source libraries for Java that helps get rid of these common code patterns. Since Guava appeared long before Java 8, Guava also has a way to concatenate strings: Joiner.on (",") .join (debtors).
Very basic utility
Let's look at a simple class that implements a standard set of basic Java methods. I suggest not to delve particularly into the implementation of the hashCode, equals, toString and compareTo methods (I just generated the first three of them in Eclipse) so as not to waste time, but just look at the amount of code.
')
class Person implements Comparable<Person> { private String lastName; private String middleName; private String firstName; private int zipCode;
Now let's look at similar code using Guava and new methods from Java 8:
class Person implements Comparable<Person> { private String lastName; private String middleName; private String firstName; private int zipCode;
As you can see, the code has become cleaner and more concise. It uses MoreObjects and ComparisonChain from Guava and the Objects class from Java 8. If you are using Java 7 or an older version, you can use the Objects class from Guava - it has hashCode and equal methods, similar to the used hash and equals methods from the java class. lang.Objects. Previously, toStringHelper was also in the Objects class, but with the advent of Java 8 in Guava 18 in the Objects class, the @Deprecated tag was hung on all methods, and those methods that are not available in Java 8 were transferred to MoreObjects so that there was no name conflict - Guava developing, and its developers are not shy to get rid of outdated code.
I note that this version of the class is slightly different from the original one: I assumed that the patronymic may not be completed, in which case we will not see it as a result of toString, and compareTo will assume that non-patronymic individuals should go after those who have a patronymic (in this case, ordering occurs first by surname and first name, and only then by patronymic name).
Another example of very basic utilities is preconditions. For some reason, Java only has Objects.requireNotNull (since Java 7).
Briefly about preconditions:
Method name in Preconditions class | Generated exception |
---|
checkArgument (boolean) | IllegalArgumentException |
checkNotNull (T) | NullPointerException |
checkState (boolean) | IllegalStateException |
checkElementIndex (int index, int size) | IndexOutOfBoundException |
checkPositionIndex (int index, int size) | IndexOutOfBoundException |
Why they are needed, you can
read on the Oracle site .
New collections
It often happens that you can see this kind of code in the middle of business logic:
Map<String, Integer> counts = new HashMap<>(); for (String word : words) { Integer count = counts.get(word); if (count == null) { counts.put(word, 1); } else { counts.put(word, count + 1); } }
Or this code:
List<String> values = map.get(key); if (values == null) { values = new ArrayList<>(); map.put(key, values); } values.add(value);
(in the last passage, the input is map, key, and value). These two examples demonstrate working with collections when collections contain modified data (in this case, numbers and lists, respectively). In the first case, the map (map) essentially describes a multiset, i.e. set with repeating elements, and in the second case, the display is a multi-display. Such abstractions are in Guava. Let's rewrite examples using these abstractions:
Multiset<String> counts = HashMultiset.create(); for (String word : words) { counts.add(word); }
and
map.put(key, value);
(here map is Multimap <String, String>). I note that Guava allows you to customize the behavior of such multimaps - for example, we may want sets of values ​​to be stored as sets, or we may want lists, for the display itself we may want a linked list, hash or tree — all the necessary implementations in Guava are available. Table is a collection that eliminates the same duplicate code, but for the case of storing mappings inside mappings. Here are examples of new collections that simplify life:
Multiset | “Many” that may have duplicates |
Multimap | “Display”, which may have duplicates |
Bimap | Supports “reverse mapping” |
Table | Associates an ordered key pair with a value |
ClassToInstanceMap | Displays a type on an instance of this type (eliminates type conversions) |
Rangeset | Set of ranges |
Rangemap | A set of mappings of non-intersecting ranges to nonzero values |
Collection decorators
To create collection decorators — both those already in the Java Collections Framework and those defined in Guava — there are corresponding classes, such as ForwardingList, ForwardingMap, ForwardingMiltiset.
Fixed collections
There are also immutable collections in Guava; They may not be directly related to clean code, but they significantly simplify debugging and interaction between different parts of the application. They:
- safe to use in “unfriendly code”;
- they can save time and memory, since they are not oriented towards the possibility of change ( analysis showed that all non-infinite collections are more efficient than their counterparts);
- can be used as constants, and it can be expected that they will not be exactly changed.
There are positive differences compared to the Collections.unmodifiableSpecific collection methods that create wrappers, so you can expect the collection to be unchanged only if it is no longer referenced; the collection leaves the overhead of the ability to change both in speed and in memory.
A couple of simple examples:
public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of( "red", "green", "blue"); class Foo { final ImmutableSet<Bar> bars; Foo(Set<Bar> bars) { this.bars = ImmutableSet.copyOf(bars);
Implementing Iterators
PeekingIterator | Just wraps the iterator, adding a peek () method to it to get the value of the next element. Created by calling Iterators.peekingIterator (Iterator) |
AbstractIterator | Eliminates the need to implement all the methods of the iterator - just enough to implement protected T computeNext () |
AbstractSequentialIterator | It is similar to the previous one, but calculates the next element based on the previous one: you need to implement the protected T method computeNext (T previous) |
Functional and collection utilities
Guava provides interfaces such as Function <A, R> and Predicate, and utility classes Functions, Predicates, FluentIterable, Iterables, Lists, Sets and others. I remind you that Guava appeared long before Java 8, and therefore Optional and the Function <A, R> and Predicate interfaces inevitably appeared in it, which, however, are useful only in limited cases, because without lambda the function code with predicates and functions is in most cases will be much more cumbersome than the usual imperative, but in some cases it allows you to keep it concise. A simple example:
Predicate<MyClass> nonDefault = not(equalTo(DEFAULT_VALUE)); Iterable<String> strings1 = transform(filter(iterable, nonDefault), toStringFunction()); Iterable<String> strings2 = from(iterable).filter(nonDefault).transform(toStringFunction());
Here static methods from Functions (toStringFunction), Predicates (not, equalTo), Iterables (transform, filter) and FluentIterable (from) are imported. In the first case, the static methods Iterable are used to construct the result, in the second - FluentIterable.
Input Output
To abstract byte and character streams, abstract classes such as ByteSource, ByteSink, CharSoure, and CharSink are defined. They are created, as a rule, with the help of the Resources and Files facades. There is also a considerable set of methods for working with input and output streams, such as conversion, reading, copying and concatenation (see classes CharSource, ByteSource, ByteSink). Examples:
About everything little by little
Lists | Creating various types of lists, incl. Lists.newCopyOnWriteArrayList (iterable), Lists.reverse (list) / * view! /, Lists.transform (fromList, function) / * lazy view! * / |
Sets | Conversion from Map <SomeClass, Boolean> to Set <SomeClass> (view!), Working with sets in the mathematical sense (intersection, union, difference) |
Iterables | Simple methods of type any, all, contains, concat, filter, find, limit, isEmpty, size, toArray, transform. For some reason, in Java 8, many of these methods apply only to collections, but not to Iterable in general. |
Bytes, Ints, UnsignedInteger, etc. | Work with unsigned numbers and arrays of primitive types (there are corresponding utility classes for each primitive type). |
ObjectArrays | In fact, only two types of methods are concatenation of arrays (for some reason it is not in the standard Java library) and the creation of masses for a given class or class of an array (for some reason, the Java library has only a similar method for copying). |
Joiner splitter | Flexible classes for combining or stringing rows from or to Iterable, List, or Map. |
Strings, MoreObjects | Of the unmentioned, the most commonly used methods are Strings.emptyToNull (String), Strings.isNullOrEmpty (String), Strings.nullToEmpty (String), and MoreObjects.firstNonNull (T, T) |
Closer, Throwables | Try-with-resources emulation, multi-catch (useful only for Java 6 and older), working with stack tracing and throwing exceptions. |
com.google.common.net | The class names speak for themselves: InternetDomainName, InetAddresses, HttpHeaders, MediaType, UrlEscapers |
com.google.common.html and com.google.common.xml | HtmlEscapers and XmlEscapers |
Range | Range. |
Eventbus | Powerful implementation of the publisher-subscriber pattern. Subscribers are registered in EventBus, whose “responsive” methods are annotated, and when you call an event, EventBus finds subscribers who are able to perceive this kind of event and notifies them of the event. |
IntMath, LongMath, BigIntegerMath, DoubleMath | Many useful functions for working with numbers. |
Classpath | There is no cross-platform way to browse classes on the classpath in Java And Guava provides an opportunity to go through the classes of a package or project. |
Typetoken | Due to the erasure of types, we cannot manipulate generic types during program execution. TypeToken allows you to manipulate such types. |
More examples
Hashing:
HashFunction hf = Hashing.md5(); HashCode hc = hf.newHasher() .putLong(id) .putString(name, Charsets.UTF_8) .putObject(person, personFunnel) .hash();
Caching:
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterWrite(10, TimeUnit.MINUTES) .removalListener(MY_LISTENER) .build( new CacheLoader<Key, Graph>() { public Graph load(Key key) throws AnyException { return createExpensiveGraph(key); } });
Dynamic proxy:
Foo foo = Reflection.newProxy(Foo.class, invocationHandler)
To create a dynamic proxy without Guava, the following code is usually written:
Foo foo = (Foo) Proxy.newProxyInstance( Foo.class.getClassLoader(), new Class<?>[] {Foo.class}, invocationHandler);
That's all the code you read.