您知道,缠结的for循环和索引可以跟踪是否/其他情况和切换用例,空/验证检查,转换/复制/删除/排序集合,异常处理……列表随着行号和维护负担的增加而继续存在。
我想到了托尼·霍尔的一句名言。
构建软件设计的方法有两种:一种方法是使它变得如此简单,以至于显然没有缺陷,另一种方法是使它变得如此复杂以至于没有明显的缺陷。
换句话说:细节中有魔鬼。
Apache Commons具有补充JDK API的一些最出色的库,但本文与Commons无关。 它是关于Google Guava的 ,在很多方面都与Commons类似。 它为常用的日常任务提供了一个库,例如集合处理,字符串处理,并发,IO,基元,异常等。
Guava中有很多很棒的东西,我没有时间去浏览完整的库,但是这里至少有一些它可以做什么的例子。
对象
使用对象可以轻松地实现哈希码/等式,而不会使您的类过于混乱(对我而言,Eclipse自动生成往往有些冗长)。
实现toString的类在进行调试和日志记录时非常令人愉悦,但是确实是一个痛苦的实现。 Objects.toStringHelper使得此操作非常容易,并且还有助于维护打印对象的一致格式。
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;}@Overridepublic int hashCode() {return Objects.hashCode(getId(), getName());}@Overridepublic String toString() {return Objects.toStringHelper(this).add("id", getId()).add("name", getName()).toString();}@Overridepublic 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());}
}
打印此类会输出类似这样的内容。
Item{id=1, name=Book}
可抛物
包装原始异常对象并不总是合适的,因为如果不相关的类加载器之间发生通信,或者在网络上对其进行序列化,则可能导致客户端代码中的ClassNotFoundException。 Throwables可以解除这种依赖关系,仍然允许远程客户端通过将其转换为字符串来查看堆栈跟踪。
try {// throws implementation specific exception
} catch (InternalException e) {throw new ApiException("reason", Throwables.getStackTraceAsString(e));
}
可迭代
连接两个单独的集合并对结果执行操作可能会导致很多混乱。 遍地抢救。 花一点时间,思考一下如果没有Iterables.concat,代码的外观如何。
for (Item item : Iterables.concat(books, electronics)) {// do something useful
}
多图
Multimap就像一个Map,但是允许为每个键存储多个值。 以下示例是类型安全的异构容器的变体,该容器使用multimap来实现商品的产品目录。
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在Map的键和值之间实现了一对一的双向关系。 这是一个使用语言代码获取语言的示例,反之亦然。
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");
前提条件
大多数类在构造函数和方法中给定的值都有限制。 无效值应通过在执行前进行显式有效性检查来尽快升级。 快速故障要比以后由于意外的异常而失败,或更糟糕的是,静默地计算错误的结果要好得多。
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");
}
约束条件
约束与先决条件类似,它们可以限制将哪些值添加到集合中。 由于约束与业务代码分离,因此这使集合的使用更加容易并且代码更加简洁。
public class Voyage {private Country targetcountry;private int capacity;private List<Cargo> items = Constraints.constrainedList(new ArrayList<Cargo>(), new Constraint<Cargo>() {@Overridepublic 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语句的内容现在可以简化为单行代码。 那有多甜?
Predicate<Item> heavyItemPolicy = new Predicate<Item>() {@Overridepublic boolean apply(Item item) {if(item.getWeight() > 1000){return true;}return false;}
};
Collection<Item> heavyItems = Collections2.filter(order, heavyItemPolicy);
您也可以类似的方式使用Maps.filterKeys或Iterables.filter。 但是请记住,从修改中删除是双向的。 例如从原点移除会影响结果,反之亦然。
另一方面,函数是一种将一个对象转换为另一个对象的方法。 例如,按项目顺序转换并发。
Function currencyConverter = new Function<Double, Item>() {@Overridepublic Double apply(Item item) {return item.getPrice() * ANOTHER_CURRENCY;}
}
Collection<Double> prices = Collections2.transform(order, currencyConverter);
您也可以类似的方式使用Maps.transformValues或Iterables.transform。
查询API
一段时间以来,我一直在考虑如何创建简单但功能强大的Fake Objects 。 但是我不希望假冒产品本身成为维护负担,因此它们必须易于实施。 我的直觉告诉我,我需要一个通用的状态管理框架来使其正常工作。 因此,我使用谓词创建了一个小的流利查询接口,该接口与内存中的存储进行交互。
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。
参考: Deep Hacks博客上 的详细信息来自我们的JCG合作伙伴 KristofferSjögren 。
相关文章 :
- Java Lambda语法替代
- Java中的低GC:使用原语而不是包装器
- Java泛型快速教程
- Java最佳实践教程系列
翻译自: https://www.javacodegeeks.com/2011/09/google-guava-libraries-essentials.html