由于这是一项艰巨而简单的任务,所以我很乐意交付。
WTF是番石榴吗?
这是一组非常简单的基本类,您最终还是要自己编写。 仅由Google来思考Apache的共同点。 只是为了让您的生活更轻松一点。
Wiktor Gworek在Javarsowia 2010上做了一个早期(v04) 演讲 ,另外一个是波兰语演讲 。
在撰写本文时,最新版本是v07,已经过Mavenized,可以在公共Maven repo上找到 。
这是一些有趣的事情的快速回顾。 不过,不要期望任何花哨的东西,Guava非常基础。
@VisibleForTesting
一个简单的注释,告诉您为什么放宽了特定的属性访问限制。
在测试中使用的一个常见技巧是放宽对特定属性的默认访问限制,以便您可以在单元测试中使用它,该单元测试位于相同的程序包中(尽管在不同的目录中)。 无论是好是坏,请记住给开发人员一个提示。
考虑:
public class User {
private Long id;
private String firstName;
private String lastName;
String login;
为什么登录程序包有作用域?
public class User {
private Long id;
private String firstName;
private String lastName;
@VisibleForTesting String login;
啊,那是为什么。
前提条件
Guava有一些防御性编程的先决条件(按合同设计),但不如Apache Commons / Spring框架具备的条件好。 有趣的一件事是Guava解决方案返回了对象,因此可以进行内联。 考虑:
使用手写的前提条件:
public User(Long id, String firstName, String lastName, String login) {
validateParameters(id, firstName, lastName, login);
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.login = login.toLowerCase();
}
private void validateParameters(Long id, String firstName, String lastName, String login) {
if(id == null ) {
throw new IllegalArgumentException('id cannot be null');
}
if(firstName == null || firstName.length() == 0) {
throw new IllegalArgumentException('firstName cannot be empty');
}
if(lastName == null || lastName.length() == 0) {
throw new IllegalArgumentException('lastName cannot be empty');
}
if(login == null || login.length() == 0) {
throw new IllegalArgumentException('login cannot be empty');
}
}
使用番石榴先决条件:
public void fullyImplementedGuavaConstructorWouldBe(Long id, String firstName, String lastName, String login) {
this.id = checkNotNull(id);
this.firstName = checkNotNull(firstName);
this.lastName = checkNotNull(lastName);
this.login = checkNotNull(login);
checkArgument(firstName.length() > 0);
checkArgument(lastName.length() > 0);
checkArgument(login.length() > 0);
}
(感谢Yom注意到checkNotNull必须在checkArgument之前进行,尽管这有点不直观)
使用spring或apache commons前提(两个库的用法看起来完全一样):
public void springConstructorWouldBe(Long id, String firstName, String lastName, String login) {
notNull(id); hasText(firstName); hasText(lastName); hasText(login);
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.login = login;
}
CharMatcher
对于讨厌regexp或只想要简单美观的对象样式模式匹配解决方案的人。
例子:
和/或易用性
String input = 'This invoice has an id of 192/10/10';
CharMatcher charMatcher = CharMatcher.DIGIT.or(CharMatcher.is('/'));
String output = charMatcher.retainFrom(input);
输出是:192/10/10
否定:
String input = 'DO NOT scream at me!';
CharMatcher charMatcher = CharMatcher.JAVA_LOWER_CASE.or(CharMatcher.WHITESPACE).negate();
String output = charMatcher.retainFrom(input);
输出是:DONOT!
范围:
String input = 'DO NOT scream at me!';
CharMatcher charMatcher = CharMatcher.inRange('m', 's').or(CharMatcher.is('a').or(CharMatcher.WHITESPACE));
String output = charMatcher.retainFrom(input);
输出是:sram am
细木工/分离器
顾名思义,它是正确完成字符串连接/拆分的方法,尽管我发现调用有点儿反转了……哦,那是java。
String[] fantasyGenres = {'Space Opera', 'Horror', 'Magic realism', 'Religion'};
String joined = Joiner.on(', ').join(fantasyGenres);
输出:太空歌剧,恐怖片,魔幻现实主义,宗教
您可以跳过空值:
String[] fantasyGenres = {'Space Opera', null, 'Horror', 'Magic realism', null, 'Religion'};
String joined = Joiner.on(', ').skipNulls().join(fantasyGenres);
输出:太空歌剧,恐怖片,魔幻现实主义,宗教
您可以填写空值:
String[] fantasyGenres = {'Space Opera', null, 'Horror', 'Magic realism', null, 'Religion'};
String joined = Joiner.on(', ').useForNull('NULL!!!').join(fantasyGenres);
输出:太空歌剧,NULL !!!,恐怖,魔术现实主义,NULL !!!,宗教
您可以加入地图
Map<Integer, String> map = newHashMap();
map.put(1, 'Space Opera');
map.put(2, 'Horror');
map.put(3, 'Magic realism');
String joined = Joiner.on(', ').withKeyValueSeparator(' -> ').join(map);
输出:1? 太空歌剧2 恐怖3? 魔幻现实主义
Split返回Iterable而不是JDK数组:
String input = 'Some very stupid data with ids of invoces like 121432, 3436534 and 8989898 inside';
Iterable<String> splitted = Splitter.on(' ').split(input);
尽管您不能为每个“列”指定不同的长度,但Split会进行固定长度的拆分,这使得在解析某些导出效果不佳的Excel时,它的使用受到了限制。
String input =
'A 1 1 1 1\n' +
'B 1 2 2 2\n' +
'C 1 2 3 3\n' +
'D 1 2 5 3\n' +
'E 3 2 5 4\n' +
'F 3 3 7 5\n' +
'G 3 3 7 5\n' +
'H 3 3 9 7';
Iterable<String> splitted = Splitter.fixedLength(3).trimResults().split(input);
您可以在拆分时使用CharMatcher
String input = 'Some very stupid data with ids of invoces like 123231/fv/10/2010, 123231/fv/10/2010 and 123231/fv/10/2010';
Iterable<String> splitted = Splitter.on(CharMatcher.DIGIT.negate())
.trimResults()
.omitEmptyStrings()
.split(input);
谓词/函数
谓词本身并不多,它只是一个带有返回true的方法的接口,但是如果将谓词与函数和Collections2(一个简化了集合处理的番石榴类)结合使用,则可以在工具箱中找到一个不错的工具。
但是,让我们从基本谓词使用开始。 假设我们要查找是否有用数字登录的用户。 接种将是(返回布尔值):
Predicates.in(users).apply(shouldNotHaveDigitsInLoginPredicate);
谓词看起来像那样
public class ShouldNotHaveDigitsInLoginPredicate implements Predicate<User> {
@Override
public boolean apply(User user) {
checkNotNull(user);
return CharMatcher.DIGIT.retainFrom(user.login).length() == 0;
}
}
现在让我们添加一个函数,该函数会将用户转换为他的全名:
public class FullNameFunction implements Function<User, String> {
@Override
public String apply(User user) {
checkNotNull(user);
return user.getFirstName() + ' ' + user.getLastName();
}
}
您可以使用静态方法转换调用它:
List<User> users = newArrayList(new User(1L, 'sylwek', 'stall', 'rambo'),
new User(2L, 'arnold', 'schwartz', 'commando'));
List<String> fullNames = transform(users, new FullNameFunction());
现在,让谓词与函数结合使用,以打印登录名不包含数字的用户名:
List<User> users = newArrayList(new User(1L, 'sylwek', 'stall', 'rambo'),
new User(2L, 'arnold', 'schwartz', 'commando'),
new User(3L, 'hans', 'kloss', 'jw23'));
Collection<User> usersWithoutDigitsInLogin = filter(users, new ShouldNotHaveDigitsInLoginPredicate());
String names = Joiner.on('\n').join( transform(usersWithoutDigitsInLogin, new FullNameFunction()) );
我们没有得到的是: 折叠(减少)和元组 。 哦,好吧,如果您想使用Java中的函数 ,您可能还是会转向Java Functional Library ,对吧?
案例格式
是否曾经想过用一个衬里将这些丑陋的PHP Pear名称转换为漂亮的java / cpp样式? 没有? 好吧,无论如何,您可以:
String pearPhpName = 'Really_Fucked_Up_PHP_PearConvention_That_Looks_UGLY_because_of_no_NAMESPACES';
String javaAndCPPName = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL , pearPhpName);
输出:ReallyFuckedUpPhpPearconventionThatLooksUglyBecauseOfNoNamespaces
但是,由于Oracle接管了Sun,您实际上可能希望将其转换为sql风格,对吗?
String sqlName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, javaAndCPPName);
输出:real_fucked_up_php_pearconvention_that_looks_ugly_because_of_no_namespaces
馆藏
番石榴具有Google集合库1.0的超集,这确实是将这种依赖关系包含在pom中的一个很好的理由。 我什至不会尝试描述所有功能,而只是指出一些不错的东西:
- 您几乎拥有所有东西的不可变版本
- 您可以在常见类型(如列表,集合,地图,对象数组)上获得一些不错的静态和静态类型化方法,这些方法包括:
- 基于返回类型创建的简单方法:例如newArrayList
- 转换(应用返回不可变版本的函数的方式)
- 分区(分页)
- 逆转
现在还有更多有趣的收藏。
多图
Mutlimap基本上是一个映射,单个键可以具有许多值。 是否曾经在您的代码中创建Map <T1,Set <T2 >>? 您不再需要了。
Multimap<Integer, String> multimap = HashMultimap.create();
multimap.put(1, 'a');
multimap.put(2, 'b');
multimap.put(3, 'c');
multimap.put(1, 'a2');
当然也有不可变的实现:ImmutableListMultimap,ImmutableSetMultomap等。
您可以在线(最多5个元素)或使用构建器构造不可变项:
Multimap<Integer, String> multimap = ImmutableSetMultimap.of(1, 'a', 2, 'b', 3, 'c', 1, 'a2');
Multimap<Integer, String> multimap = new ImmutableSetMultimap.Builder<Integer, String>()
.put(1, 'a')
.put(2, 'b')
.put(3, 'c')
.put(1, 'a2')
.build();
双图
BiMap是仅具有唯一值的地图。 考虑一下:
@Test(expected = IllegalArgumentException.class)
public void biMapShouldOnlyHaveUniqueValues() {
BiMap<Integer, String> biMap = HashBiMap.create();
biMap.put(1, 'a');
biMap.put(2, 'b');
biMap.put(3, 'a'); //argh! an exception
}
这使您可以反转地图,因此值成为关键,反之亦然:
BiMap<Integer, String> biMap = HashBiMap.create();
biMap.put(1, 'a');
biMap.put(2, 'b');
biMap.put(3, 'c');
BiMap<String, Integer> invertedMap = biMap.inverse();
不知道我实际上想用它做什么。
约束条件
这使您可以在集合上添加约束检查,以便仅添加通过约束的值。
想象一下,我们想要一个在他们的登录名中带有首字母“ r”的用户集合。
Constraint<User> loginMustStartWithR = new Constraint<User>() {
@Override
public User checkElement(User user) {
checkNotNull(user);
if(!user.login.startsWith('r')) {
throw new IllegalArgumentException('GTFO, you are not Rrrrrrrrr');
}
return user;
}
};
现在进行测试:
@Test(expected = IllegalArgumentException.class)
public void shouldConstraintCollection() {
//given
Collection<User> users = newArrayList(new User(1L, 'john', 'rambo', 'rambo'));
Collection<User> usersThatStartWithR = constrainedCollection(users, loginMustStartWithR);
//when
usersThatStartWithR.add(new User(2L, 'arnold', 'schwarz', 'commando'));
}
您还可以立即获得notNull约束:
//notice it's not an IllegalArgumentException :(
@Test(expected = NullPointerException.class)
public void notNullConstraintShouldWork() {
//given
Collection<Integer> users = newArrayList(1);
Collection<Integer> notNullCollection = constrainedCollection(users, notNull());
//when
notNullCollection.add(null);
}
需要记住的事情:约束条件不是检查集合中已经存在的数据。
桌子
正如预期的那样,表是具有列,行和值的集合。 我猜没有更多的Map <T1,Map <T2,T3 >>。 用法很简单,您可以转置:
Table<Integer, String, String> table = HashBasedTable.create();
table.put(1, 'a', '1a');
table.put(1, 'b', '1b');
table.put(2, 'a', '2a');
table.put(2, 'b', '2b');
Table transponedTable = Tables.transpose(table);
就是这样,伙计们。 我没有介绍util.concurrent,原语,io和net软件包,但您可能已经知道会发生什么。
祝您编程愉快,别忘了分享!
参考: Solid Craft博客上来自JCG合作伙伴 Jakub Nabrdalik的Google Guava v07示例 。
翻译自: https://www.javacodegeeks.com/2012/10/google-guava-v07-examples.html