你的位置:首页 > Java教程

[Java教程]guava Optional

过多的使用null可能会导致大量的bugs,Google code 底层代码中,95%的集合类默认不接受null值。对null值,使用快速失败拒绝null比默认接受更好。

另外,null本身的含义很模糊。例如,对于null返回值,如Map.get(key)返回null,可能因为和key对应的值为null,也可能map中根本没有该key。null也可以用以表示失败或成功,可能代表任何事物。使用其他值而不是null可以使代码含义表达的更清楚。

尽管如此,有些地方还是应该使用null。在内存和速度方面,null是廉价的,并且在对象数组中是不可避免的。

基于以上原因,Guava大部分工具对null都设计为快速失败。另外,Guava提供了一些方便使用null的工具。

实际案例

不要在Set中使用null,也不要将null作为Map的key值,使用特殊值代替null可以让查找的语义更清晰。

如果你想把null作为Map的value,更好的办法不是将该条目放入Map中,而是应该以一个单独的Set维护“值为null”的键集合。因为Map中某个键对应的值为null和Map中没有某个键值很容易混淆。

如果需要在List中使用null,并且这个List是稀疏的,则使用Map<Integer, E>可能更好。

如果确实需要null,但null值不能和Guava的集合一起工作,则只能选择其他方式,如JDK中的Collections.unmodifiableList代替Guava中的ImmutableList。

Optional

Guava设计Optional来解决null问题,Optional<T>表示可能为null的T类型的引用。Optional实例要么包含一个非null的引用,要么什么都不包含(absent),从不会包含null值引用。在需要使用null的时候,可以用Optional代替。

Optional除了赋予null意义外,增加可读性,还在于它是一种傻瓜式的防护。Optional 迫使你积极思考引用确实的情况,因为你必须显式地从Optional 获取引用。

Optional 的可能应用方面:

  • 作为方法返回值,用于取代使用null作为返回值的情况。
  • 用于区分“unknow”(如Map不包含)和“known to have no value”(如Map包含该键,值为Optional.absent())。
  • 用于包装null引用,从而在不支持null的集合中使用。

创建Optional(均为静态方法)

Optional.of(T) 创建指定引用的Optional实例,对null值抛出NullPointException。建议直接传递常量参数。
Optional.absent() 创建引用缺失的Optional实例
Optional.fromNullable(T) 创建指定引用的Optional实例,若引用为null则表示缺失
assertEquals("training", Optional.of("training").get());Optional<String> optionalName = Optional.absent();assertFalse(optionalName.isPresent());// Non-null instanceOptional<String> optionalName = Optional.fromNullable("bob");assertEquals("bob", optionalName.get());// null instanceassertSame(Optional.absent(), Optional.fromNullable(null));

查询方法(均为非静态方法)

boolean isPresent() 如果该Optional 包含非null引用,返回true。
T get() 返回Optional 所包含的引用,若引用缺失,则抛出IllegalStateException。
T or(T) 返回Optional 所包含的引用,若引用缺失,返回默认值。
T orNull() 返回Optional 所包含的引用,若引用缺失,返回null。
Set<T> asSet() 返回Optional所包含的singleton不可变集合,若为空,返回空集合。

public void testIsPresent_no() {    assertFalse(Optional.absent().isPresent());  }  public void testIsPresent_yes() {    assertTrue(Optional.of("training").isPresent());  }  public void testGet_absent() {    Optional<String> optional = Optional.absent();    try {      optional.get();      fail();    } catch (IllegalStateException expected) {    }  }  public void testGet_present() {    assertEquals("training", Optional.of("training").get());  }    public void testOr_T_present() {    assertEquals("a", Optional.of("a").or("default"));  }  public void testOr_T_absent() {    assertEquals("default", Optional.absent().or("default"));  }  public void testOrNull_present() {    assertEquals("a", Optional.of("a").orNull());  }  public void testOrNull_absent() {    assertNull(Optional.absent().orNull());  }  public void testAsSet_present() {    Set<String> expected = Collections.singleton("a");    assertEquals(expected, Optional.of("a").asSet());  }  public void testAsSet_absent() {    assertTrue("Returned set should be empty", Optional.absent().asSet().isEmpty());  }  public void testAsSet_presentIsImmutable() {    Set<String> presentAsSet = Optional.of("a").asSet();    try {      presentAsSet.add("b");      fail();    } catch (UnsupportedOperationException expected) {    }  }  public void testAsSet_absentIsImmutable() {    Set<Object> absentAsSet = Optional.absent().asSet();    try {      absentAsSet.add("foo");      fail();    } catch (UnsupportedOperationException expected) {    }  }