Guava学习笔记:Optional优雅的使用null

 在咱们学习和使用Guava的Optional以前,咱们须要来了解一下Java中null。由于,只有咱们深刻的了解了null的相关知识,咱们才能更加深刻体会领悟到Guava的Optional设计和使用上的优雅和简单。java

  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);
}

} 复制代码google

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

复制代码设计

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);code

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() { if (null 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.fromNullable(T):将一个T的实例转换为Optional对象,T的实例能够不为空,也能够为空[Optional.fromNullable(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.fromNullable(null);
    Optional<Integer> NoNullableOpt=Optional.fromNullable(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("fromNullableOpt 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,逆操做是fromNullable()   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.fromNullable(null);
}    private Optional<Long> methodNoNull() {        return Optional.fromNullable(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对象中去得到真正的返回值。

相关文章
相关标签/搜索