为了尽量避免空指针在编码中给我们带来的问题,JAVA8提供了一个包装类Optional来解决空指针异常的问题,该类中包含了许多的方法,一起看看
Optional.of(T t)
该方法用于创建一个Optional实例,T为我们想要创建的类型,比如我们想创建一个Student类型的对象,可以这样写:
Optional<Student> optional=Optional.of(new Student());
在Optional的泛型中放入我们想要新建的对象,用于接收,这样我们就新建了由Optional包装起来的Student类,当我们想取出该student对象时,调用.get()方法:
@Test
public void test1(){
Optional<Student> optional=Optional.of(new Student());
Student student=optional.get();
System.out.println(student);
}
这里我们创建的是一个没有任何属性的student对象,运行结果如下:
Optional.empty()
该方法用于创建一个空的Optional实例
@Test
public void test2(){
Optional<String > optional = Optional.empty();
System.out.println(optional.get());
}
而运行后,不再会报空指针异常,而是如下信息:
信息告诉我们get方法没有获取到值,也就是optional包装的String没有值,这样我们就可以很快的定位到报空的位置,而不用像以前一样一级一级进行断点调试
Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例,我们可以将它理解为.of()与.empty()方法的综合
isPresent():判断是否包含值
该方法的逻辑应该在平时编码中会经常用到,比如我们要判断一个对象是否有值,有值才会对其进行操作
Optional<Student> optional = Optional.ofNullable(new Student());
if (optional.isPresent()){
System.out.println(optional.get());
}
orElse(T t):如果调用对象包含值,返回该值,否则返回t
在上一步的基础上,我们继续这个方法,如果该optional对象有值时,我们就返回,否则返回我们指定的一个值:
Optional<Student> optional = Optional.ofNullable(new Student());
Student student=optional.orElse(new Student(001,"张三",18,183.2));
System.out.println(student);
运行结果:
由于我们声明了一个无参的student,该对象有默认值,直接输出打印
接下来我们传入一个空对象
Optional<Student> optional = Optional.ofNullable(null);
Student student=optional.orElse(new Student(001,"张三",18,183.2));
System.out.println(student);
运行结果:
通过使用替代值的方式,我们就能很好的避免空指针的发生
orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回S获得的值
该方法与orElse方法类似,但它传入的是一个函数式接口,也就是意味着,在orElse方法的基础上,我们还可以通过lambda表达式对替代的值做一些处理。
比方有一个student集合,数据如下:
List<Student> stus= Arrays.asList(
new Student("张三",16,168.25, Student.Status.FREE),
new Student("李四",48,178.25,Student.Status.VOCATION),
new Student("王五",36,148.45,Student.Status.BUSY),
new Student("赵六",56,180.68,Student.Status.FREE),
new Student("田七",18,180.68,Student.Status.VOCATION)
);
现在我们要对集合中的数据进行一次判断,如果没有身高大于181的学生,则返回我们指定的数据,否则返回空对象,利用orElse方法我们可以这样写:
Optional<Student> optional = Optional.ofNullable(null);
Student student=optional.orElseGet(() ->{
boolean b = stus.stream().anyMatch((e) ->e.getHeight()>181);
System.out.println(b);
if (b){
return new Student();
}else {
return new Student(001,"张三",18,183.2);
}
});
System.out.println(student);
map(Function f)、 flatMap(Function mapper):如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
这个方法之前在streamAPI中我们提到过,这里就不多说
接下来我们说一个比较常见的空指针情况,我们有一个班级类MyClass,以及一个学生类MyStudent,班级中有学生,但不一定有某个名叫A的学生,但名叫A的学生是一定存在的。我们要做的是得到该班级中包含的学生姓名,两个实体类如下:
在传统方法中,为了避免空指针,我们会这样写:
public String getStudentName(MyClass myClass){
if (myClass!=null){
MyStudent student=myClass.getMyStudent();
if (student!=null){
return student.getName();
}
}
return "学生";
}
然后写下一个测试方法调用该方法得到我们想要的值:
@Test
public void test5(){
MyClass myclass=new MyClass();
String name=getStudentName(myclass);
System.out.println(name);
}
先对MyClass对象进行非空判断,再对student对象进行非空判断,利用Optional类,我们可以这样写:
放入一个类型为MyStudent类型的空optional类,而获取名字的方法则可以写成这样:
public String getStudentName2(Optional<MyClass> myClass){
return myClass.orElse(new MyClass()).getOptional().orElse(new MyStudent("学生")).getName();
}
为了防止MyClass为空,我们将传入的MyClass使用Optional包装起来,在返回时使用orElse方法进行避免,而后再调用其中的get方法得到其中的Optional属性,再对该属性进行一次orElse避免空指针
接下来再说一说接口默认方法:在JAVA8中,允许接口中含有实现的方法(默认方法),使用default关键字修饰即可:
public interface MyFun {
default String getName(){
return "aaa";
}
}
在这种情况下,我们也许会碰到一个冲突的问题,比如我们现在有一个类,也含有同样的方法:
public class TestClass {
public String getName(){
return "bbb";
}
}
有一个类既实现了MyFun接口,又继承了TestClass类:
那么当我们调用其中的getName方法时,会调用哪个方法呢?
查看运行结果:
最终打印的是bbb,也就是调用了父类中的方法,这就说明:当接口中定义了默认方法,而另外一个父类中又定义了一个同名的方法时,会选择父类中的方法。
另外,还会有一个接口冲突的情况出现,假设我们有两个含有同样默认方法的接口,这是第二个:
而有一个实现类实现了这两个接口
而这时因为我们没有重写方法导致类名报错,此时因为两个接口的方法相同,若如果我们想实现其中一个,比如我们想实现MyFun中的方法,就需要这么写:
@Override
public String getName() {
return MyFun.super.getName();
}
运行结果:
同样地,若我们想实现MyDefault中的方法,则把类名更换即可