Clean code with google guava jee conf

Post on 10-May-2015

5,619 views 2 download

Tags:

description

The Guava project contains several of Google’s core libraries that we rely on in our Java-based projects: collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, and so forth. There will be the slides presenting most useful and interesting features of Guava (v.12) that makes stuff simpler, better and code cleaner. We will cover most of the com.google.common.base.* classes and basic use of functions in collection and Google collections and few other features that are part of Guava and I find them very useful. Some of you will think that there is an overlap with Apache commons – and it’s true, but Guava is built with expectation that there is a Function and a Predicate class as well as various builders which makes it really cool and simple for many use cases.

transcript

Guava is…

Guava is… a fruit

https://en.wikipedia.org/wiki/Guava

What is Google Guava?

annotations

basic utilities

collections

concurrency

comparison

strings

primitives

ranges

functional idioms

I/o

caching

hashing

event bus

math

reflection

net/http

What in this presentation?

annotations

basic utilities

collections

concurrency

comparison

strings

primitives

ranges

functional idioms

I/O

caching

hashing

event bus

math

reflection

net/http

Why Guava? The goal is for you to write less code ...

Make code more readable, cleaner and simpler

Helps developers to focus on business logic rather than writing java utilities

Saves time, resources and improve productivity

Popular API and active development

Mainstream

Basically... a painkiller and natural extension for Java

I Could've Invented That

ref. Effective Java

Item 47, "Know and use the libraries“

Guava Releases About every 3 months, with significant new

functionality and fixes

Release 14.0.1 was released on March 15, 2013

All releases are either major or patch releases

Never minor

Let’s get some Guava!

@annotations

Guava Basic Utilities

String Utilities Preconditions

Objects Helper

Avoiding Nulls

StopWatch

String Utilities

Joiner

Splitter

CharMatcher

Joining Strings Who here has ever written this utility?

public class StringUtil { public static String join( String separator, Iterable<String> pieces) { // any of ~5 common implementations goes here } }

Joining Strings Converts collections into single String object

Joining an Iterable, Iterator, varargs/array

Return a String, or append to an Appendable

Throws a NPE on null objects, unless:

.skipNulls()

.useForNull(String)

It also works with Maps

We could be looking at 18 to 48 different methods

Joining Strings (cont.)

List<String> fruits = Arrays.asList( "apple", null, "orange", null, null, "guava"); String joined = Joiner.on(", ").skipNulls().join(fruits); >> "apple, orange, guava"

Joining Strings (cont.)

List<String> fruits = Arrays.asList( "apple", null, "orange", null, null, "guava"); String joined = Joiner.on(", ").skipNulls().join(fruits); >> "apple, orange, guava" String joined = Joiner.on(",").useForNull("banana").join(fruits); >> "apple, banana, orange, banana, banana, guava"

Splitting Strings Working in the opposite direction than Joiner

Allows to split String into collection of elements

String.split() on steroids

A better, more intuitive String.split()

Doesn't silently discard trailing separators

Handles empty pieces predictably

Splitter

String input = ",, ,apple, orange ,,, banana,, ,guava, ,,"; Iterable<String> split = Splitter.on(',') .omitEmptyStrings() .trimResults() .split(input);

>> [ "apple", "orange", "banana", "guava" ]

The default behavior is simplistic

If you want extra features, ask for them!

Splitter

String input = ",, ,apple, orange ,,, banana,, ,guava, ,,"; Iterable<String> split = Splitter.on(',') .omitEmptyStrings() .trimResults() .split(input);

>> [ "apple", "orange", "banana", "guava" ]

The default behavior is simplistic

If you want extra features, ask for them!

By default, assumes nothing about whitespace

These classes use what Googlers (tentatively) call the "Utility Object pattern."

Splitter more…

Splitter.onPattern("\\d+").split("Java3Scala4Haskell0Brainfuck5Kotlin"); >> [ "Java ", "Scala", "Haskell", "Brainfuck", "Kotlin" ] Splitter.on(CharMatcher.inRange('3','5')) .split("Java3Scala4Haskell0Brainfuck5Kotlin"); >> [ "Java", "Scala", "Haskell0Brainfuck", "Kotlin" ]

Split using regular expression

Split using CharMatcher

CharMatcher CharMatcher provides many text processing

methods based on “matching character” notation

Separates "configuration" from "processing"

CharMatcher represents two notions:

What constitutes a matching character?

What to do with those matching characters?

Can also be used to "filter" chars

CharMatcher (cont.) Allows to check if a sequence of characters satisfies

given condition:

WHITESPACE, ASCII, ANY, DIGIT (many pre-defined sets)

is('x')

isNot('_')

oneOf("aeiou")

inRange('a', 'z') .or (inRange('A', 'Z')).negate()

All that you can do... CharMatcher

Provides methods to modify char sequences:

boolean matchesAllOf(CharSequence)

boolean matchesAnyOf(CharSequence)

boolean matchesNoneOf(CharSequence)

int indexIn(CharSequence, int)

int lastIndexIn(CharSequence, int)

int countIn(CharSequence)

String removeFrom(CharSequence)

String retainFrom(CharSequence)

String trimFrom(CharSequence)

String trimLeadingFrom(CharSequence)

String trimTrailingFrom(CharSequence)

String collapseFrom(CharSequence, char)

String trimAndCollapseFrom(CharSequence, char)

String replaceFrom(CharSequence, char)

CharMatcher (example)

CharMatcher matcher = CharMatcher.DIGIT .or(CharMatcher.inRange('a', 'z') .or(CharMatcher.inRange('A', 'Z'))); if (matcher.matchesAllOf("this1will2match3")) { // ... }

CharMatcher (example)

// eliminate all characters that aren't digits or lowercase String lowerAndDigit = CharMatcher.DIGIT .or(CharMatcher.JAVA_LOWER_CASE) .retainFrom(input);

CharMatcher matcher = CharMatcher.DIGIT .or(CharMatcher.inRange('a', 'z') .or(CharMatcher.inRange('A', 'Z'))); if (matcher.matchesAllOf("this1will2match3")) { // ... }

CharMatcher (example)

String pass = “$$$ secret passcode $$$"; String result = CharMatcher.is(“$”).trimFrom(pass); >> “ secret password "

trimLeadingFrom(), trimTrailingFrom() or trimAndCollapseFrom()

Guava Basic Utilities String Utilities

Preconditions Objects Helper

Avoiding Nulls

StopWatch

checkState(boolean)

Throws IllegalStateException if false

Used to check object state

if (state != State.PLAYABLE) { throw new IllegalStateException( "Can't play movie; state is " + state); }

checkState(boolean)

Throws IllegalStateException if false

Used to check object state

… or…

if (state != State.PLAYABLE) { throw new IllegalStateException( "Can't play movie; state is " + state); }

Preconditions.checkState(state == State.PLAYABLE, "Can't play movie; state is %s", state);

(what's the difference? none!)

checkNotNull(T) Throws NullPointerException if null

Returns the value. Can be used inline.

public Car(Engine engine) { this.engine = Preconditions.checkNotNull(engine); }

checkNotNull(T) Throws NullPointerException if null

Returns the value. Can be used inline.

. . . with using static import . . .

public Car(Engine engine) { this.engine = Preconditions.checkNotNull(engine); }

public Car(Engine engine) { this.engine = checkNotNull(engine, “engine cannot be null”); }

checkArgument(boolean) Throws IllegalArgumentException if false

Used to validate method arguments

public void drive(double speed) { Preconditions.checkArgument(speed > 0.0, "Speed (%s) must be positive", speed); }

Why Preconditions?

Defensive coding

Useful for validation

Each method has three variants: No extra arguments

An extra object for error message

An extra String & Objects. String.format like but only allows %s

Recommended to be used as static imports

Guava Basic Utilities String Utilities

Preconditions

Objects Helper Avoiding Nulls

StopWatch

Object common methods

Objects.equal(Object, Object)

Objects.hashCode(Object...)

Objects.toStringHelper(Object)

Objects.firstNonNull(T, T)

equal(…) & hashCode(…)

public class Person { private final String name, nickname; private final Movie favoriteMovie;

@Override public boolean equals(Object object) { if (object instanceof Person) { Person that = (Person) object; return Objects.equal(this.name, that.name) && Objects.equal(this.nickname, that.nickname) && Objects.equal(this.favoriteMovie, that.favoriteMovie); } return false; }

@Override public int hashCode() { return Objects.hashCode(name, nickname, favoriteMovie);

}

toStringHelper(…) & firstNonNull(T, T)

@Override public String toString() { // speakers is @Nullable! return Objects.toStringHelper(this) .add("conference", name) .add(“location", location) .add("speakers", speakers) .omitNullValues().toString(); } >> "Person{name=JeeConf, location=Kiev}“ // w/o omitNullValues() >> "Person{name=JeeConf, location=Kiev, speakers=null}“

public String preferredLocation() { return Objects.firstNonNull(location, name); }

Guava Basic Utilities String Utilities

Preconditions

Objects Helper

Avoiding Nulls StopWatch

Null is ambiguous

if (x != null && x.someM() != null && ..) { // some code… }

Problem with Null

No entry? Or entry exists but the nickname is unlisted?

Person person = personService.findByNickname(“Andy"); if (person == null) { // what does this mean? }

Problem with Null

No entry? Or entry exists but the nickname is unlisted? The value in the map is null, or the value is not in the map. Null can mean failure, can mean success, can mean almost anything.

Person person = personService.findByNickname(“Andy"); if (person == null) { // what does this mean? }

Map.get(key)

Optional<T> vs. null null is "hidden", Optional is ”explicit” An immutable wrapper that is either:

present: contains a non-null reference absent: contains nothing Note that it never "contains null"

Possible uses: return type

“a T that must be present" "a T that might be absent"

distinguish between "unknown" (for example, not present in a map) "known to have no value" (present in the map, with value

Optional.absent())

wrap nullable references for storage in a collection that does not support null

Optional<T>

// Make optional of given type Optional<String> possible = Optional.of(“Ido”); Optional<String> value = Optional.fromNullable(str); if (value.isPresent()) { // ... } // returns true if nonNull possible.isPresent(); // returns this possible value or default possible.or(“Nick”); // returns Ido possible.get();

Making an Optional Optional.of(T) - make optional of given non-null

value or fail fast on null

Optional.absent() - return an absent optional of some type

Optional.fromNullable(T) - turn value in Optional and treat null as absent

For null-unfriendly collections Many collections, including the JDK's Queue and ConcurrentMap implementations, don't allow null elements.

Queue<Optional<Foo>> is a simple and natural solution!

Others… Strings Methods are primarily for interfacing with

unpleasant APIs that equate null strings and empty strings:

Strings.emptyToNull(String)

Strings.isNullOrEmpty(String)

Strings.nullToEmpty(String)

Guava Basic Utilities String Utilities

Preconditions

Objects Helper

Avoiding Nulls

StopWatch

StopWatch Class for measuring elapsed time

Prefer StopWatch over System.nanoTime()

Don't use System.currentTimeMillis()!

Provides methods that automatically calculate time between start() and stop() execution

StopWatch

public void measureElapsedTime(Collection<String> input) { Stopwatch stopwatch = new Stopwatch().start(); doSomeOperation(input); long nanos = stopwatch.elapsed(TimeUnit.NANOSECONDS); }

StopWatch Pros StopWatch uses nanoTime() but exposes only relative

timings, a meaningless absolute value

Alternate time sources can be substituted using Ticker (read() returns nanoseconds)

Can be easily mocked with custom passing time provider

Returns counted time using different units

toString() gives human readable format

Functional Concepts brought to Java

Many things in Guava are inspired by functional concepts from other programming languages

In Java can only be approximated through awkward and verbose use of anonymous classes

Expected to change in Java 8

Guava is currently aimed at users of Java 5 and above

Core concepts Key functional concepts Guava uses:

Core concepts Key functional concepts Guava uses:

Function<F, T> == F => T

=> to transform a collection

public interface Function<F, T> { @Nullable T apply(@Nullable F input); }

Core concepts Key functional concepts Guava uses:

Function<F, T> == F => T

=> to transform a collection

Predicate<T> == T => Boolean

=> filter out a collection

public interface Function<F, T> { @Nullable T apply(@Nullable F input); }

public interface Predicate<T> { boolean apply(@Nullable T input); }

Function<F,T> usage

Iterables.transform()

FluentIterable.transform()

Iterators.transform()

Collections2.transform()

Lists.transform()

Maps, Multimaps … etc.

Main usage with transform()

Tools to manipulate collections using Function:

FluentIterable API Chaining methods (return FluentIterable<T>)

filter(Predicate)

transform(Function)

skip(int), limit(int)

cycle()

Query methods (return boolean) allMatch(Predicate), anyMatch(Predicate)

contains(Object)

isEmpty()

Extraction methods (return Optional<T>) first(), last(), firstMatch(Predicate), get(int)

Conversion methods (return a copied Collection<T>) toList(), toSet(), toSortedSet(), toArray()

Function <F,T>

Java 7 doesn't have lambda expressions, but Guava helps us out (a bit) with Function

public void demo(Collection<String> input) { Function<String, String> toUpperCase = new Function<String, String>() {

@Override public String apply(String string) { return string.toUpperCase(); } };

Collection<String> transformed = Collections2.transform(input, toUpperCase); }

Predicate<T> usage

Predicate checks if condition is met for passed object

Tools to manipulate collections using Predicate:

FluentIterables.filter()

Iterables.filter()

Iterators.filter()

Collections2.filter()

Sets.filter()

Maps, MultiMaps, … etc.

Predicate <T> Quite similar to Function but does NOT extend it

Predicate<User> onlyAwesome = new Predicate<User>() { @Override public boolean apply(User in) { return Optional.fromNullable(in) .or(User.NOT_AWESOME).isAwesome(); } };

Predicate <T> Let's use it on a collection:

Iterable<User> users = getMixedUsers(); // find all awesome users Iterable<User> onlyAwesomeUsers = Iterables.filter(users, onlyAwesome); // find one (first) awesome user User awesomeUser = Iterables.find(users, onlyAwesome); // or better Optional<User> awesomeOrAbsent = Iterables.tryFind(users, onlyAwesome);

Putting it together

private Iterable<Integer> puttingItTogether(Iterable<Integer> numbers) { FluentIterable<Integer> squaresOfEvens = FluentIterable.from(numbers) .filter(new Predicate<Integer>() { @Override public boolean apply(Integer input) { checkNotNull(input, "nulls are not allowed here!"); return input % 2 == 0; }}).transform(new Function<Integer, Integer>() { @Override public Integer apply(Integer input) { checkNotNull(input, "nulls are not allowed here!"); return input * input; } }); return squaresOfEvens; }

… Or

List<Integer> squaresOfEvens = Lists.newArrayList(); for (Integer number : givenNumbers) { if (number % 2 == 0) { squaresOfEvens.add(number * number); } } >> [ 4, 16, 36, 64, 100 ]

… Moral

List<Integer> squaresOfEvens = Lists.newArrayList(); for (Integer number : givenNumbers) { if (number % 2 == 0) { squaresOfEvens.add(number * number); } } >> [ 4, 16, 36, 64, 100 ]

Just be careful!!

Functional style can be great but it's not automatically the better way to go.

Java Caching

Java Caching with Guava Guava has a powerful on-heap key→value cache

Thread-safe implementation

More or less internally similar to ConcurrentMap + automatic eviction

No explicit support for distributed caching

Types of Caches Provides two types of caches:

LoadingCache - knows how to load entries when a cache miss occurs LoadingCache.get(key) returns the value associated with

key, loading it first if necessary

Cache - does not automatically load entries

We're going to focus on the loading case here; it's usually what you want

Caches (example)

CacheLoader<String, Graph> loader = new CacheLoader<String, Graph>() {

@Override public Graph load(String key) { return createExpensiveGraph(key); }

}; LoadingCache<String, Graph> cache = CacheBuilder.newBuilder().build(loader);

CacheBuilder The CacheBuilder has next properties:

Cache size

Time to expire entries after last access

Time based expiration of entries after being updated

Use of weak or soft references for keys/values

Setting RemovalListener that can receive events once an entry is removed fro the cache.

Concurrency level for update operations (defaults to 4)

Enable recording caching stats

CacheBuilder (example)

LoadingCache<String, String> cache = CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterAccess(10, TimeUnit.SECONDS) .recordStats() .build(new CacheLoader<String, String>() { @Override public String load(String key) throws Exception { return key.toUpperCase(); } });

Concurrency Cache instances are internally implemented very

similar to ConcurrentHashMap (thus thread-safe)

CacheLoader.load will be invoked a single time for each key, regardless of the number of requesting threads

The result will be returned to all requesting threads and inserted into the cache using the equivalent of putIfAbsent

Cache Eviction By size

.maximumSize(long)

By custom weight:

.weigher(Weigher)

.maximumWeight(long)

By time:

.expireAfterAccess(long, TimeUnit)

.expireAfterWrite(long, TimeUnit)

Cache Eviction (cont.) Reference-based eviction:

.weakKeys()

.weakValues()

.softValues()

Explicit:

.invalidate(key)

.invalidateAll(keys)

.invalidateAll()

Checked Exceptions What if loading causes a checked exception?

CacheLoader<String, String> checkedLoader = new CacheLoader<String, String>() { @Override public String load(String key) throws IOException { return loadFromDisk(key); } };

Checked Exceptions (cont.)

LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(checkedLoader); try { cache.get(key); } catch (ExecutionException e) { // ensure stack trace is for this thread throw new IOException(e.getCause()); }

Cache Stats hitCount – number of times Cache returned the

cached value

missCount – number of times Cache returned uncached value

loadSuccessCount – number of times Cache loaded new value successfully

Cache Stats (example)

LoadingCache<String, String> cache = CacheBuilder.newBuilder() .recordStats().build(checkedLoader); // cumulative stats since cache creation CacheStats stats = cache.stats(); CacheStats{ hitCount=4, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=676064, evictionCount=0 }

…Collections

MultiSet<E>

Implements Collection<E>

List: [a, c, b, b, c, a, a, b]

Set: [a, c, b]

Multiset: [a, a, a, c, c, b, b, b]

MultiSet<E>

Implements Collection<E>

List: [a, c, b, b, c, a, a, b]

Set: [a, c, b]

Multiset: [a, a, a, c, c, b, b, b]

So a Multiset<E> implementation only needs to store one occurrence of each element, plus a count!

[a x 3, c x 2, b x 3]

MultiSet<E>

Add multiple instances of a given element

Counts how many occurrences exist

Similar to a Map<E, Integer>, but...

only positive counts

size() returns total # of items, not # keys

count() for a non-existent key is 0

iterator() goes over each element

Usage: i.e. Track frequencies of elements, e.g. "word counting"

MultiSet<E> Implementations HashMultiset

TreeMultiset

LinkedHashMultiset

ConcurrentHashMultiset

ImmutableMultiset

Tired of this? Map<String, List<String>>

Tired of this? Map<String, List<String>> {a=1, a=2, b=3, c=4, c=5, c=6}

Try …

MultiMap<K, V> Like Map (key-value pairs), but may have duplicates

The values related to a single key can be viewed as a collection (set or list)

Similar to a Map<K, Collection<V>>, but...

get() never returns null (returns an empty collection)

containsKey() is true only if 1 or more values exist

entries() returns all entries for all keys

size() returns total number of entries, not keys

asMap() to view it as a Map<K, Collection<V>>

MultiMap<K, V>

Multimap<String, String> mm = ArrayListMultimap.create(); Collection<String> smiths = mm.get("Smith"); >> empty collection (never null) mm.put("Smith", "John"); mm.put("Smith", "Alice"); mm.put("Smith", "Diane"); smiths = mm.get("Smith"); >> [ "John", "Alice", "Diane" ]

MultiMap Implementations ArrayListMultimap

HashMultimap

LinkedListMultima

LinkedHashMultimap

TreeMultimap

ImmutableListMultimap

ImmutableSetMultimap

BiMap<K1, K2>

Bi-directional Map

Both keys and values are unique

Can view the inverse map with inverse()

Use instead of maintaining two separate maps: Map<K1, K2>

Map<K2, K1>

Static utilities In classes with name ending with an s Lists

Maps

Multimaps

Multisets

Sets

SortedMaps

Tables

Iterators

Iterables

Collections2

Static factories methods

Rather than typing

you type

Map<String, Class<? extends Handler>> m = new HashMap<String, Class<? extends Handler>>();

Map<String, Class<? extends Handler>> m2 = Maps.newHashMap();

Guava Alternatives

Should you use Guava or Apache Commons?

We may be biased, so consult this question on Stack Overflow:

http://tinyurl.com/guava-vs-apache

The large number of upvotes for the top answers shows a pretty strong community consensus

Need help with a problem? Post to Stack Overflow! Use the "guava" tag

Report a defect, request an enhancement?

http://code.google.com/p/guava-libraries/issues/list

Start an email discussion?

Send to guava-discuss@googlegroups.com

General discussion takes place on

http://groups.google.com/group/guava-discuss

The Guava team is generally highly active in all of these areas

Time to Play

What to Remember Functional flavor of collection handling

CharMatcher / Splitter / Joiner

Avoid null where possible

Ease of Preconditions

Caching

Multimap / Multiset / Bimap

Q&A

http://www.flickr.com/photos/wwworks/4759535950/sizes/o/in/photostream/