} } }

    Guava进修笔记:Optional优雅的应用null

    添加时间:2013-6-16 点击量:

      在我们进修和应用Guava的Optional之前,我们须要来懂得一下Java中null。因为,只有我们深切的懂得了null的相干常识,我们才干加倍深切领会融合到Guava的Optional设计和应用上的优雅和简单。




       null代表不断定的对象:


      Java中,null是一个关键字,用来标识一个不断定的对象。是以可以将null赋给引用类型变量,但不成以将null赋给根蒂根基类型变量。


      Java中,变量的应用都遵守一个原则:先定义,并且初始化后,才可以应用。例如如下代码中,我们不克不及定义int age后,不给age指定值,就去打印age的值。这条对对于引用类型变量也是实用的(String name也同样实用),在编译的时辰就会提示为初始化。



    public class NullTest {
    
    public static void testNull(){
    int age;
    System.out.println(
    user age:+age);

    long money;
    money
    =10L;
    System.out.println(
    user money+money);

    String name;
    System.out.println(
    user name:+name);
    }
    }


      在Java中,Java默认给变量赋值:在定义变量的时辰,若是定义后没有给变量赋值,则Java在运行时会主动给变量赋值。赋值原则是整数类型int、byte、short、long的主动赋值为0,带小数点的float、double主动赋值为0.0,boolean的主动赋值为false,其他各供引用类型变量主动赋值为null。上方代变为如下可运行代码:



    public class NullTest {
    
    public static void testNull(){
    int age = 0;
    System.out.println(
    user Age:+age);

    long money;
    money
    =10L;
    System.out.println(
    user money+money);

    String name
    = null;
    System.out.println(
    user name:+name);
    }
    }


      null本身不是对象,也不是Objcet的实例:


      null只是一个关键字,用来标识一个不断定的对象,他既不是对象,也不是Objcet对象的实例。下面我们经由过程代码断定一下null是不是Object对象实例:



    public class NullTest {
    
    public static void main(String[] args) {
    testNullObject();
    }

    public static void testNullObject() {
    ifnull instanceof java.lang.Object) {
    System.out.println(
    null属于java.lang.Object类型);
    }
    else {
    System.out.println(
    null不属于java.lang.Object类型);
    }
    }
    }


      运行上方代码,输出:null不属于java.lang.Object类型,可见,null对象不是Object对象的实例。




       null对象的应用:


      1.常见应用处景:


      有时辰,我们定义一个引用类型变量,在刚开端的时辰,无法给出一个断定的值,然则不指定值,法度可能会在try语句块中初始化值。这时辰,我们下面应用变量的时辰就会报错。这时辰,可以先给变量指定一个null值,题目就解决了。例如:  



     Connection conn = null;
    
    try {
      conn
    = DriverManager.getConnection(url, user, password);
    }
    catch (SQLException e) {
       e.printStackTrace();
    }
    String catalog
    = conn.getCatalog();


      若是刚开端的时辰不指定conn = null,则最后一句就会报错。


      2.容器类型与null:
         List:容许反复元素,可以参加随便率性多个null。
         Set:不容许反复元素,最多可以参加一个null。
         Map:Map的key最多可以参加一个null,value字段没有限制。
         数组:根蒂根基类型数组,定义后,若是不给定初始值,则java运行时会主动给定值。引用类型数组,不给定初始值,则所有的元素值为null。
     
         3.null的其他感化
         1>、断定一个引用类型数据是否null。 用==来断定。
         2>、开释内存,让一个非null的引用类型变量指向null。如许这个对象就不再被任何对象应用了。守候JVM垃圾收受接管机制去收受接管。


      4.null的应用建议:


      1>. 在Set或者Map中应用null作为键值指向的value,切切别这么用。很显然,在Set和Map的查询操纵中,将null作为特别的例子可以使查询成果更浅近易懂。
      2>. 在Map中包含value是null值的键值对,你应当把这种键值对移出map,应用一个自力的Set来包含所有null或者非null的键。很轻易混合的是,一个Map是不是包含value是 null的key,还是说这个Map中没有如许的键值对。好的办法就是把这类key值分立开来,并且好好想想到底一个value是null的键值对对于你的法度来说到底意味着什么。
      3>. 在列表中应用null,并且这个列表的数据是稀少的,或许你好应当应用一个Map<Integer,E>字典来庖代这个列表。因为字典更高效,并且也许加倍正确的合适你潜意识里对法度的需求。
      4>. 想象一下若是有一种天然的“空对象”可以应用,比方说对于列举类型,添加一个列举常数实例,这个实例用来默示你想用null值所默示的景象。比如:Java.math.RoundingMode有一个常数实例UNNECESSARY来默示“不须要四舍五入”,任何精度策画的办法若传以RoundingMode.UNNECESSARY为参数来策画,必定抛出一个异常来默示不须要舍取精度。
      


      5.题目和困惑:


      起首,对于null的随便应用会一系列难以预感的题目。经由过程对多量代码的研究和解析,我们发明可能95%以上的凑集类默认并不接管null值,若是有null值将被放入凑集中,代立即中断并报错而不是默认存储null值,对于开辟来说,如许可以或许加倍轻易的定位法度失足的处所。
      别的,null值是一种令人不满的模糊含义。有的时辰会产生二义性,这时辰我们就很难搞清楚具体的意思,若是法度返回一个null值,其代表的含义到底是什么,例如:Map.get(key)若返回value值为null,其代表的含义可能是该键指向的value值是null,亦或者该键在map中并不存在。null值可以默示失败,可以默示成功,几乎可以默示任何景象。用其它一些值(而不是null值)可以让你的代码表述的含义更清楚。
      反过来说,应用null值在有些景象下是一种正确的选择,因为从内存消费和效力方面推敲,应用null加倍便宜,并且在对象数组中呈现null也是不成避免的。然则在法度代码中,比方说在函数库中,null值的应用会变成导致误会的首恶,也会导致一些莫名的,模糊的,很难批改的题目。就像上述map的例子,字典返回null可以代表的是该键指向的值存在且为空,或者也可以代表字典中没有这个键。关键在于,null值不克不及指明到底null代表了什么含义。




      Guava的Optional:


      大多半景象下法度员应用null是为了默示某种不存在的意思,也许应当有一个value,然则这个value是空或者这个value找不到。比方说,在用不存在的key值从map中取  value,Map.get返回null默示没有该map中不包含这个key。 


      若T类型数据可认为null,Optional<T>是用来以非空值调换T数据类型的一种办法。一个Optional对象可以包含一个非空的T引用(这种景象下我们称之为“存在的”)或者不包含任何器材(这种景象下我们称之为“空白的”)。但Optional从来不会包含对null值的引用。



    import com.google.common.base.Optional;
    

    public class OptionalTest {

    public void testOptional() throws Exception {
    Optional
    <Integer> possible=Optional.of(6);
    if(possible.isPresent()){
    System.out.println(
    possible isPresent:+possible.isPresent());
    System.out.println(
    possible value:+possible.get());
    }
    }
    }



      因为这些原因,Guava库设计了Optional来解决null的题目。很多Guava的对象被设计成若是有null值存期近刻报错而不是只要高低文接管处理惩罚null值就默认应用null值持续运行。并且,Guava供给了Optional等一些对象让你在不得不应用null值的时辰,可以加倍简便的应用null并帮助你避免直接应用null。

      Optional<T>的最常用价值在于,例如,假设一个办法返回某一个数据类型,调用这个办法的代码来按照这个办法的返回值来做下一步的动作,若该办法可以返回一个null值默示成功,或者默示失败,在这里看来都是意义模糊的,所以应用Optional<T>作为返回值,则后续代码可以经由过程isPresent()来断定是否返回了期望的值(底本期望返回null或者返回不为null,其意义不清楚),并且可以应用get()来获得实际的返回值。




      Optional办法申明和应用实例:


      1.常用静态办法:


      Optional.of(T):获得一个Optional对象,其内部包含了一个非null的T数据类型实例,若T=null,则立即报错。
      Optional.absent():获得一个Optional对象,其内部包含了空值
      Optional.Nullable(T):将一个T的实例转换为Optional对象,T的实例可以不为空,也可认为空[Optional.Nullable(null),和Optional.absent()等价。


      应用实例如下:



    import com.google.common.base.Optional;
    


    public class OptionalTest {

    @Test
    public void testOptional() throws Exception {
    Optional
    <Integer> possible=Optional.of(6);
    Optional
    <Integer> absentOpt=Optional.absent();
    Optional
    <Integer> NullableOpt=Optional.Nullable(null);
    Optional
    <Integer> NoNullableOpt=Optional.Nullable(10);
    if(possible.isPresent()){
    System.out.println(
    possible isPresent:+possible.isPresent());
    System.out.println(
    possible value:+possible.get());
    }
    if(absentOpt.isPresent()){
    System.out.println(
    absentOpt isPresent:+absentOpt.isPresent()); ;
    }
    if(NullableOpt.isPresent()){
    System.out.println(
    NullableOpt isPresent:+NullableOpt.isPresent()); ;
    }
    if(NoNullableOpt.isPresent()){
    System.out.println(
    NoNullableOpt isPresent:+NoNullableOpt.isPresent()); ;
    }
    }
    }


      2.实例办法:


      1>. boolean isPresent():若是Optional包含的T实例不为null,则返回true;若T实例为null,返回false
      2>. T get():返回Optional包含的T实例,该T实例必须不为空;不然,对包含null的Optional实例调用get()会抛出一个IllegalStateException异常
      3>. T or(T):若Optional实例中包含了传入的T的雷同实例,返回Optional包含的该T实例,不然返回输入的T实例作为默认值
      4>. T orNull():返回Optional实例中包含的非空T实例,若是Optional中包含的是空值,返回null,逆操纵是Nullable()
      5>. Set<T> asSet():返回一个不成批改的Set,该Set中包含Optional实例中包含的所有非空存在的T实例,且在该Set中,每个T实例都是单态,若是Optional中没有非空存在的T实例,返回的将是一个空的不成批改的Set。
      
    应用实例如下:



    import java.util.Set;
    
    import com.google.common.base.Optional;

    public class OptionalTest {

    public void testMethodReturn() {
    Optional
    <Long> value = method();
    if(value.isPresent()==true){
    System.out.println(
    获得返回值: + value.get());
    }
    else{

    System.out.println(
    获得返回值: + value.or(-12L));
    }

    System.out.println(
    获得返回值 orNull: + value.orNull());

    Optional
    <Long> valueNoNull = methodNoNull();
    if(valueNoNull.isPresent()==true){
    Set
    <Long> set=valueNoNull.asSet();
    System.out.println(
    获得返回值 set 的 size : + set.size());
    System.out.println(
    获得返回值: + valueNoNull.get());
    }
    else{
    System.out.println(
    获得返回值: + valueNoNull.or(-12L));
    }

    System.out.println(
    获得返回值 orNull: + valueNoNull.orNull());
    }

    private Optional<Long> method() {
    return Optional.Nullable(null);
    }
    private Optional<Long> methodNoNull() {
    return Optional.Nullable(15L);
    }

    }


      输出成果:



    获得返回值: -12
    
    获得返回值 orNull:
    null
    获得返回值 set 的 size :
    1
    获得返回值:
    15
    获得返回值 orNull:
    15


      Optional除了给null值定名所带来的代码可浏览性的进步,大益处莫过于Optional是傻瓜式的。Optional对象的应用强迫你去积极的思虑如许一种景象,若是你想让你的法度返回null值,这null值代表的含义是什么,因为你想要取得返回值,必定从Optional对象内部去获得,所以你必定会这么去思虑。然则只是简单的应用一个Null值会很随便马虎的让人忘怀去思考代码所要表达的含义到底是什么,尽管FindBugs有些帮助,然则我们还是认为它并没有尽可能的解决好帮助法度员去思考null值代表的含义这个题目。
      这种思虑会在你返回某些存在的值或者不存在的值的时辰显得希罕相干。和其他人一样,你绝对很可能会忘怀别人写的办法method(a,b)可能会返回一个null值,就如同当你去写method(a,b)的实现时,你也很可能忘怀输入参数a也可所以null。若是返回的是Optional对象,对于调用者来说,就可以忘怀怎么去怀抱null代表的是什么含义,因为他们始终要从optional对象中去获得真正的返回值。




      

    容易发怒的意思就是: 别人做了蠢事, 然后我们代替他们, 表现出笨蛋的样子。—— 蔡康永
    分享到: