我使用x!=null以避免NullPointerException。还有其他选择吗?
if (x != null) {
// ...
}
我使用x!=null以避免NullPointerException。还有其他选择吗?
if (x != null) {
// ...
}
当前回答
避免不必要的空检查的方法很简单:
您需要知道哪些变量可以为空,哪些变量不能为空,并且您需要对给定变量属于哪一类有信心。
但是,尽管它可以说得很简单,但实现它却很困难。关键在于自信的部分,因为你如何确定变量不能为空?
对此没有快速的解决方法,但这里有一些提示:
干净的代码。能够推理一段代码的行为最重要的一点是,它是以易于理解的方式编写的。根据变量所代表的内容命名变量,根据它们所做的内容命名方法,应用单一责任原则(SOLID中的S:http://en.wikipedia.org/wiki/SOLID_(object-oriented_design),这意味着每一段代码都应该有一个单独的责任,并且不做任何其他事情)。一旦你的代码是干净的,那么你就更容易理解它,也更容易理解多个代码层。对于杂乱的代码,试图理解一个方法的作用可能会让你忘记当初为什么要读这个方法。(提示:阅读罗伯特·C·马丁的《清洁代码》)避免返回空值。如果空值会使程序无法正常运行,请改为抛出异常(确保添加适当的错误处理)。返回空值可能是可接受的情况,例如,尝试从数据库中获取对象。在这些情况下,编写处理空值的代码,并在您的耳朵后面记下,这里有可能返回空值的内容。处理返回的null值,使其尽可能接近返回null的方法的调用方(不要盲目地将其传递回调用链)永远不要将显式空值作为参数传递(至少不要跨类传递)。如果您曾经处于传递空参数是唯一选项的位置,那么创建一个不包含此参数的新方法是一种方法。验证您的输入!确定应用程序的“入口点”。它们可以从Web服务、REST服务、远程EJB类、控制器等所有内容。对于这些入口点中的每个方法,问问自己:“如果此参数为空,此方法是否正确执行?”如果答案为否,请添加Validate.notNull(someParam,“当someParam为空时无法运行!”);。如果缺少必需的参数,这将引发IllegalArgumentException。在入口点进行这种类型的验证的好处是,您可以很容易地在从入口点执行的代码中假设该变量永远不会为空!此外,如果这失败了,在入口点,调试会比代码深处出现NullPointerException要容易得多,因为这样的失败只能意味着一件事:客户端没有向您发送所有必需的信息。在大多数情况下,您希望验证所有输入参数,如果您发现自己处于需要允许大量空值的位置,这可能是接口设计不良的标志,需要进行重构/添加以满足客户机的需要。使用集合时,返回一个空集合而不是空集合!使用数据库时,请使用非空约束。这样,您就知道从数据库中读取的值不能为空,并且不必检查它。构造代码并坚持执行。这样做可以让您对代码的行为做出假设,例如,如果应用程序的所有输入都经过验证,那么您可以假设这些值永远不会为空。如果您还没有这样做,请编写代码的自动测试。通过编写测试,您将对代码进行推理,并且您也将更加确信它确实做到了它应该做的事情。此外,自动化测试通过让您立即知道这段代码没有做以前的事情来防止重构过程中的错误。
当然,您仍然需要进行空检查,但它可以降到最低限度(即,知道您可能会得到空值,而不是到处都是空值)。当谈到空检查时,我实际上更喜欢使用三元运算符(但要小心使用,当您开始嵌套它们时,它们会变得非常混乱)
public String nullSafeToString(final Object o) {
return o != null ? o.toString() : "null";
}
其他回答
在Java8中,如果局部变量/字段/方法参数/方法返回类型从未赋值为null(并且不检查null),则可以使用类型T;如果可以为null,则可以键入Optional<T>。然后使用方法map处理T->,使用方法flatMap处理T->可选<R>:
class SomeService {
@Inject
private CompanyDao companyDao;
// return Optional<String>
public Optional<String> selectCeoCityByCompanyId0(int companyId) {
return companyDao.selectById(companyId)
.map(Company::getCeo)
.flatMap(Person::getHomeAddress)
.flatMap(Address::getCity);
}
// return String + default value
public String selectCeoCityByCompanyId1(int companyId) {
return companyDao.selectById(companyId)
.map(Company::getCeo)
.flatMap(Person::getHomeAddress)
.flatMap(Address::getCity)
.orElse("UNKNOWN");
}
// return String + exception
public String selectCeoCityByCompanyId2(int companyId) throws NoSuchElementException {
return companyDao.selectById(companyId)
.map(Company::getCeo)
.flatMap(Person::getHomeAddress)
.flatMap(Address::getCity)
.orElseThrow(NoSuchElementException::new);
}
}
interface CompanyDao {
// real situation: no company for such id -> use Optional<Company>
Optional<Company> selectById(int id);
}
class Company {
// company always has ceo -> use Person
Person ceo;
public Person getCeo() {return ceo;}
}
class Person {
// person always has name -> use String
String firstName;
// person can be without address -> use Optional<Address>
Optional<Address> homeAddress = Optional.empty();
public String getFirstName() {return firstName;}
public Optional<Address> getHomeAddress() {return homeAddress;}
}
class Address {
// address always contains country -> use String
String country;
// city field is optional -> use Optional<String>
Optional<String> city = Optional.empty();
String getCountry() {return country;}
Optional<String> getCity() {return city;}
}
您还可以使用CheckerFramework(带有JDK7及更高版本)静态检查空值。这可能会解决很多问题,但需要运行一个目前仅适用于OpenJDK AFAIK的额外工具。https://checkerframework.org/
这是大多数开发人员最常见的错误。
我们有很多方法来处理这个问题。
方法1:
org.apache.commons.lang.Validate //using apache framework
notNull(对象对象,字符串消息)
方法2:
if(someObject!=null){ // simply checking against null
}
方法3:
@isNull @Nullable // using annotation based validation
方法4:
// by writing static method and calling it across whereever we needed to check the validation
static <T> T isNull(someObject e){
if(e == null){
throw new NullPointerException();
}
return e;
}
函数方法可能有助于包装重复的空检查并执行匿名代码,如下面的示例。
BiConsumer<Object, Consumer<Object>> consumeIfPresent = (s,f) ->{
if(s!=null) {
f.accept(s);
}
};
consumeIfPresent.accept(null, (s)-> System.out.println(s) );
consumeIfPresent.accept("test", (s)-> System.out.println(s));
BiFunction<Object, Function<Object,Object>,Object> executeIfPresent = (a,b) ->{
if(a!=null) {
return b.apply(a);
}
return null;
};
executeIfPresent.apply(null, (s)-> {System.out.println(s);return s;} );
executeIfPresent.apply("test", (s)-> {System.out.println(s);return s;} );
如果您认为对象不应为空(或是错误),请使用断言。如果您的方法不接受null参数,请在javadoc中说它并使用断言。
您必须检查对象!=仅当您想处理对象可能为空的情况时才为空。。。
有人建议在Java7中添加新注释,以帮助处理null/notnull参数:http://tech.puredanger.com/java7/#jsr308