欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java中避免空指针异常的方法

程序员文章站 2024-03-01 16:26:22
没人会喜欢空指针异常!有什么方法可以避免它们吗?或许吧。。 本文将讨论到以下几种技术 1.optional类型(java 8中新引入的) 2.objects类(jav...

没人会喜欢空指针异常!有什么方法可以避免它们吗?或许吧。。

本文将讨论到以下几种技术

1.optional类型(java 8中新引入的)
2.objects类(java 7中原有的)

java 8中的optional类

它是什么?

1.java 8中新引入的类型
2.它是作为某个指定类型的对象的包装器或者用于那些不存在对象(null)的场景

简单来说,它是处理空值的一个更好的替代品(警告:乍一看可能并没有那么明显)

基本用法

它是一种类型(一个类)——那么,怎么才能创建一个这个类型的实例?

使用下它的三个静态方法就可以了:

复制代码 代码如下:

public static optional<string> stringoptional(string input) {
    return optional.of(input);
}

简单明了——创建一个包含这个值的optional包装器。记住——如果这个值是null的话,它会抛出npe!

复制代码 代码如下:

public static optional<string> stringnullableoptional(string input) {
if (!new random().nextboolean()) {
input = null;
}
return optional.ofnullable(input);
}

我个人认为是要更好一点。这样就不会有npe的风险了——如果输入为null的话,会返回一个空的optional。

复制代码 代码如下:

public static optional<string> emptyoptional() {
return optional.empty();
}

如果你真的就是希望返回一个”空"值的话。“空”值并不意味着null。

好吧,那如何去消费/使用optional呢?

复制代码 代码如下:

public static void consumingoptional() {
optional<string> wrapped = optional.of("astring");
if (wrapped.ispresent()) {
system.out.println("got string - " + wrapped.get());
}
else {
system.out.println("gotcha !");
}
}

简单的方法就是检查optional包装器是否真的有值(使用ispresent方法)——你会怀疑这和使用if(myobj != null)相比有什么好处。别担心,这个我会解释清楚的。

复制代码 代码如下:

public static void consumingnullableoptional() {
string input = null;
if (new random().nextboolean()) {
input = "icanbenull";
}
optional<string> wrapped = optional.ofnullable(input);
system.out.println(wrapped.orelse("default"));
}

你可以使用orelse方法,这样万一封装的确实是一个null值的话可以用它来返回一个默认值——它的好处显而易见。在提取出真实值的时候可以避免调用ifpresent方法这样明显多余的方式了。

复制代码 代码如下:

public static void consumingemptyoptional() {
string input = null;
if (new random().nextboolean()) {
input = "icanbenull";
}
optional<string> wrapped = optional.ofnullable(input);
system.out.println(wrapped.orelseget(
() -> {
return "defaultbysupplier";
}
 
));
}

这个我就有点搞不清楚了。为什么有两个同样目的的不同方法?orelse和orelseget明明可以重载的(同名但不同参数)。

不论如何,这两个方法明显的区别就在于它们的参数——你可以选择使用lambda表达式而不是supplier的实例来完成这个(一个函数式接口)

为什么使用optional要比常见的null检查强?

1.使用optional最大的好处就是可以更明白地表述你的意图——返回null值的话会让消费者感到疑惑(当真的出现npe的时候)这是不是故意返回的,因此还得查看javadoc来进一步定位。而使用optional就相当明了了。

2.有了optional你就可以彻底避免npe了——如上所提,使用optional.ofnullable,orelse以及orelseget可以让我们远离npe。

另一个救星!

看下这个代码片段

复制代码 代码如下:

package com.abhirockzz.wordpress.npesaviors;
 
import java.util.map;
import java.util.objects;
 
public class usingobjects {
 
string getval(map<string, string> amap, string key) {
return amap.containskey(key) ? amap.get(key) : null;
}
 
public static void main(string[] args) {
usingobjects obj = new usingobjects();
obj.getval(null, "dummy");
}
}

哪个可能会为空?

1.map对象
2.进行搜索使用的key
3.方法调用的这个实例

如果抛出npe的话,我们怎么能确定到底是哪个是null的?

复制代码 代码如下:

package com.abhirockzz.wordpress.npesaviors;
 
import java.util.map;
import java.util.objects;
 
public class usingobjects {
string getvalsafe(map<string, string> amap, string key) {
map<string, string> safemap = objects.requirenonnull(amap,
"map is null");
string safekey = objects.requirenonnull(key, "key is null");
 
return safemap.containskey(safekey) ? safemap.get(safekey) : null;
}
 
public static void main(string[] args) {
usingobjects obj = new usingobjects();
obj.getvalsafe(null, "dummy");
}
}

requirenonnull方法

1.如果对象不为null的话就返回它本身
2.如果值为null的话,返回的npe会带有指定的消息

为什么比if(myobj!=null)要好?

你所看到的栈跟踪信息会很清楚地看见objects.requirenonnull的方法调用。这个再配合你自己的错误日志,可以让你更快地定位问题。。。至少在我看来是更快。

你还可以自己自义校验器,比如说实现一个简单的校验器来确保没有空值。

复制代码 代码如下:

import java.util.collections;
import java.util.list;
import java.util.objects;
import java.util.function.predicate;
 
public class randomgist {
 
    public static <t> t requirenonempty(t object, predicate<t> predicate, string msgtocaller){
        objects.requirenonnull(object);
        objects.requirenonnull(predicate);
        if (predicate.test(object)){
            throw new illegalargumentexception(msgtocaller);
        }
        return object;
    }
 
    public static void main(string[] args) {
       
    //usage 1: an empty string (intentional)
 
    string s = "";
    system.out.println(requirenonempty(objects.requirenonnull(s), (s1) -> s1.isempty() , "my string is empty!"));
 
    //usage 2: an empty list (intentional)
    list list =  collections.emptylist();
    system.out.println(requirenonempty(objects.requirenonnull(list), (l) -> l.isempty(), "list is empty!").size());
 
    //usage 3: an empty user (intentional)
    user user = new user("");
    system.out.println(requirenonempty(objects.requirenonnull(user), (u) -> u.getname().isempty(), "user is empty!"));
}
 
    private static class user {
        private string name;
 
        public user(string name){
            this.name = name;
        }
 
        public string getname(){
            return name;
        }
    }
}

不要让npe在错误的地方成为痛苦。我们有许多工具能更好地处理npe,甚至彻底地根除它们!