Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cleaner code with Guava v2

Cleaner code with Guava v2

Cleaner code with Guava - v2 presented at IO Extended in Madrid

Alexandru Simonescu

May 18, 2016
Tweet

More Decks by Alexandru Simonescu

Other Decks in Programming

Transcript

  1. Our mission - Write cleaner code - Be more productive

    - Open mind to new workflows - Discover new types
  2. What is Guava? - Google’s core Java library - Utilities

    classes and methods - Used in Google and Netflix
  3. Guava components hashing event bus math reflection networking annotations basic

    utilities collections concurrency comparison strings primitives ranges functional I/O cache
  4. Guava components hashing event bus math reflection networking annotations basic

    utilities collections concurrency ordering strings primitives ranges functional I/O cache
  5. Null use cases - None - Represent some sort of

    absence - Value not found - Something went wrong :(
  6. Optional - may contain non null value (reference is present)

    - may contain nothing (reference is absent)
  7. Optional<String> name = Optional.of("Alex");
 Optional<Integer> age = Optional.absent();
 Optional<String> car

    = Optional.fromNullable(null);
 
 
 System.out.println(String.format("Name exists: %s", name.isPresent())); System.out.println(String.format("Age or default: %s", age.or(0))); 
 System.out.println(String.format("Car: %s", car.toString()));
  8. Optional goodness - Increases readability - Makes you analyze absent

    case - Never forget if a value can be null - Forces you to unwrap Optional<T>
  9. List<String> fruits = Arrays.asList(
 null, "banana", null, "blackberry", null, "guava");


    String joined = Joiner.on(", ").skipNulls().join(fruits);
 
 // banana, blackberry, guava String contacts = "alex:123;bob:321;ana:333;daniel:890;victor: 456";
 
 Splitter.MapSplitter mapSplitter = Splitter.on(";").withKeyValueSeparator(":");
 Map<String, String> descompose = mapSplitter.split(contacts);
 for (Map.Entry<String, String> f : descompose.entrySet()) {
 println(String.format("%s -> %s", f.getKey(), f.getValue()));
 } // alex -> 123 // bob -> 321 // ana -> 333
  10. Person person = persons.get(ONE);
 StringBuffer sb = new StringBuffer();
 sb.append(person.getSuffix());


    sb.append(" --- ");
 sb.append(person.getName());
 sb.append(" --- ");
 sb.append(person.getPhoneNumber());
 String phone = CharMatcher.DIGIT.retainFrom(sb);

  11. @Override
 public boolean equals(Object o) {
 if (this == o)

    return true;
 if (o == null || getClass() != o.getClass()) return false;
 
 Person person = (Person) o;
 
 if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null) return false;
 if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null) return false;
 if (name != null ? !name.equals(person.name) : person.name != null) return false;
 if (suffix != null ? !suffix.equals(person.suffix) : person.suffix != null) return false;
 if (phoneNumber != null ? !phoneNumber.equals(person.phoneNumber) : person.phoneNumber != null) return false;
 if (email != null ? !email.equals(person.email) : person.email != null) return false;
 if (website != null ? !website.equals(person.website) : person.website != null) return false;
 if (address != null ? !address.equals(person.address) : person.address != null) return false;
 return car != null ? car.equals(person.car) : person.car == null;
 
 }
 
 @Override
 public int hashCode() {
 int result = firstName != null ? firstName.hashCode() : 0;
 result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
 result = 31 * result + (name != null ? name.hashCode() : 0);
 result = 31 * result + (suffix != null ? suffix.hashCode() : 0);
 result = 31 * result + (phoneNumber != null ? phoneNumber.hashCode() : 0);
 result = 31 * result + (email != null ? email.hashCode() : 0);
 result = 31 * result + (website != null ? website.hashCode() : 0);
 result = 31 * result + (address != null ? address.hashCode() : 0);
 result = 31 * result + (car != null ? car.hashCode() : 0);
 return result;
 }
  12. @Override
 public boolean equals(Object o) {
 if (this == o)

    return true;
 if (o == null || getClass() != o.getClass()) return false;
 
 Person person = (Person) o;
 
 if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null) return false;
 if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null) return false;
 if (name != null ? !name.equals(person.name) : person.name != null) return false;
 if (suffix != null ? !suffix.equals(person.suffix) : person.suffix != null) return false;
 if (phoneNumber != null ? !phoneNumber.equals(person.phoneNumber) : person.phoneNumber != null) return false;
 if (email != null ? !email.equals(person.email) : person.email != null) return false;
 if (website != null ? !website.equals(person.website) : person.website != null) return false;
 if (address != null ? !address.equals(person.address) : person.address != null) return false;
 return car != null ? car.equals(person.car) : person.car == null;
 
 }
 
 @Override
 public int hashCode() {
 int result = firstName != null ? firstName.hashCode() : 0;
 result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
 result = 31 * result + (name != null ? name.hashCode() : 0);
 result = 31 * result + (suffix != null ? suffix.hashCode() : 0);
 result = 31 * result + (phoneNumber != null ? phoneNumber.hashCode() : 0);
 result = 31 * result + (email != null ? email.hashCode() : 0);
 result = 31 * result + (website != null ? website.hashCode() : 0);
 result = 31 * result + (address != null ? address.hashCode() : 0);
 result = 31 * result + (car != null ? car.hashCode() : 0);
 return result;
 }
  13. @Override
 public String toString() {
 return MoreObjects.toStringHelper(this)
 .add("firstName", firstName)
 .add("lastName",

    lastName)
 .add("name", name)
 .add("suffix", suffix)
 .add("phoneNumber", phoneNumber)
 .add("email", email)
 .add("website", website)
 .add("address", address.toString())
 .toString();
 }
  14. @Override
 public boolean equals(Object object) {
 
 if (object instanceof

    Person) {
 Person that = (Person) object;
 return Objects.equal(this.name, that.name)
 && Objects.equal(this.firstName, that.firstName)
 && Objects.equal(this.email, that.email);
 }
 return false;
 }
 
 @Override
 public int hashCode() {
 return Objects.hashCode(name, firstName, email);
 }
  15. @Override
 public int compareTo(Person o) {
 return ComparisonChain.start()
 .compare(this.name, o.name)


    .compare(this.firstName, o.firstName)
 .compare(this.email, o.email)
 .result();
 }
  16. public static void main(String[] args) {
 
 Stopwatch stopwatch =

    Stopwatch.createStarted();
 
 List<Person> pers = PersonRepository.getByLimit(50);
 
 Iterable<PersonDto> dtos = from(pers).transform(personToDto);
 
 from(personDtos).forEach(printer);
 
 long nanos = stopwatch.elapsed(TimeUnit.NANOSECONDS);
 }
  17. Preconditions goodness - Validate arguments - Check for null -

    Check elements in Lists, String or Array
  18. import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 
 public class PreconditionsSample

    { 
 public static void main(String[] args) { 
 checkState(args.length > 0, "Too less parameters”); 
 checkNotNull(args, "You have to pass some parameters");
 }
 }

  19. Ordering goodness - Ordering is a special Comparator instance -

    Guava’s fluent Comparator class - Chaining methods
  20. Ordering<Person> bySuffix = new Ordering<Person>() {
 @Override
 public int compare(Person

    p1, Person p2) {
 return p1.getSuffix().compareTo(p2.getSuffix());
 }
 };
 Ordering<Person> byNameLength = new Ordering<Person>() {
 @Override
 public int compare(Person p1, Person p2) {
 return Ints.compare(p1.getName().length(), p2.getName().length());
 }
 };
 
 List<Person> persons = PersonRepository.instance().getByLimit(50);
 Collections.sort(persons, byNameLength.compound(bySuffix));
  21. Why immutable? - Thread safe: no race conditions - More

    memory efficient that mutable alternative - Safe for use by untrusted libraries - Guava collection types doesn’t allow nulls
  22. New collection types - Multiset - Multimap - BiMap -

    Table - ClassToInstanceMap - RangeSet
  23. Multiset - Generalization of the notion of Set - Members

    allowed to appear more than once - Order is irrelevant
  24. Multimap - Associates keys to arbitrary values - Alternative to

    Map<K, List<V>> or Map<K, Set<V>> A -> 1 A -> 2 A -> 4 B -> 3 C -> 5 A -> [1, 2, 4] B -> 3 C -> 5
  25. 
 Person p1 = new Person("Alex", "Simonescu");
 Person p2 =

    new Person("Josh", "Block");
 
 BiMap<String, Person> emailPerson = HashBiMap.create();
 emailPerson.put("[email protected]", p1);
 emailPerson.put("[email protected]", p2);
 
 String p1Email = emailPerson.inverse().get(p1);

  26. Table - Really a Table collection. Surprised? - Implementations as:

    • HashBasedTable, • TreeBasedTable, • ImmutableBasedTable • ArrayTable
  27. String[] names = {"Bob", "Alice", "Andy", "Carol", "Ben"};
 
 //

    Table of names
 Table<Character, Integer, String> table = HashBasedTable.create();
 
 // First letter is a row key, length is a column key
 for (String name : names) {
 table.put(name.charAt(0), name.length(), name);
 }
 
 // Value corresponding to the given row and column keys
 table.get('A', 5); // -> Alice
 table.get('B', 3); // -> Ben
 
 // Set of column keys that have one or more values in the table
 table.columnKeySet(); // -> [4, 5, 3]
 
 // View of all mappings that have the given row key
 table.row('A'); // -> {4=Andy, 5=Alice}
  28. ClassToInstance - Map types to values of that type -

    No need of getInstance(Class<T>) and T putInstance(Class<T>, T)
  29. Static Constructors 
 List<Person> list = Lists.newArrayList();
 
 
 Map<Integer,

    Person> map = Maps.newLinkedHashMap();
 
 
 Set<Person> set = Sets.newHashSet(repo.getByLimit(50));
 
 
 List<String> elements = Lists.newArrayList("alpha", "beta", "gamma");
  30. Iterables Collections style 
 addAll(Collection addTo, Iterable toAdd)
 
 


    removeAll(Iterable removeFrom, Collection toRemove)
 
 
 contains(Iterable, Object) get(Iterable, int) 
 
 retainAll(Iterable removeFrom, Collection toRetain)
  31. Functional goodness - Kind of functional style on Java -

    Still not Scala - Handy filtering and transforming collections
  32. Functional concepts - Function <F, T> —> A => B

    - Predicate <T> —> T => Boolean
  33. List<State> states = Lists.newArrayList(
 new State("MT", false),
 new State("ID", false),


    new State("WY", false),
 new State("SD", false),
 new State("NE", false),
 new State("WI", false),
 new State("IN", false),
 new State("TX", false),
 new State("CA", true),
 new State("AZ", true),
 new State("NM", true),
 new State("AR", true),
 new State("IL", true),
 new State("IA", true)); Predicate<State> withI = state -> state.getAbbreviation().startsWith("I");
 Predicate<State> isExpanded = state -> state.isExpanded();
 
 Collection<State> filtered = filter(states, or(withI, not(isExpanded)));
  34. Function<Person, Person> emailToUpper = person -> {
 person.setEmail(person.getEmail().toUpperCase());
 return person;


    };
 
 Function<Person, String> toUsername = person -> {
 String[] chunks = person.getEmail().split("@");
 return chunks[0];
 };
 
 List<Person> people = getPeople();
 Collection<String> usernames = transform(people, compose(toUsername, emailToUpper));
  35. Predicate<Person> isNotNull = Predicates.notNull();
 Predicate<Object> isPerson = Predicates.instanceOf(Person.class);
 Predicate<Person> startsWithA

    = person -> person.getName().toLowerCase().startsWith("a");
 Predicate<Person> hasCar = person -> person.getCar().isPresent();
 
 Function<Person, String> toCarName = person -> person.getCar().get().getName();
 
 ImmutableList<Person> people = ImmutableList.copyOf(getPeople());
 
 List<String> cars = FluentIterable
 .from(people)
 .filter(isNotNull)
 .filter(isPerson)
 .filter(startsWithA)
 .filter(hasCar)
 .transform(toCarName)
 .toList();
  36. Concurrency goodness - Future <T> on steroids - Allows registering

    callback when finished - Provides more executors
  37. ExecutorService executorService = Executors.newFixedThreadPool(1);
 ListeningExecutorService executor = MoreExecutors.listeningDecorator(executorService);
 
 ListenableFuture<String>

    future = executor.submit(() -> {
 Thread.sleep(1000);
 return "Task completed";
 });
 
 future.addListener(() -> runOnCompletion(), executor);

  38. ExecutorService executorService = Executors.newCachedThreadPool();
 ListeningExecutorService executor = MoreExecutors.listeningDecorator(executorService);
 
 ListenableFuture<String>

    future = executor.submit(() -> {
 Thread.sleep(1000);
 return "Task completed";
 });
 
 Futures.addCallback(future, new FutureCallback<String>() {
 @Override
 public void onSuccess(String s) {
 System.out.println("Task succeed: " + s);
 }
 
 @Override
 public void onFailure(Throwable throwable) {
 System.out.println("Task failed");
 }
 }, executor);
  39. Guava Darkside - Heavy library —> use Proguard or similar

    - Lot’s of utilities —> extract only needed