聪明的方法,以避免尝试捕获每一行

时间:2012-09-07 13:28:02

标签: java exception-handling try-catch

我目前正在处理XML文件,我正在寻找一种更好的方法来避免尝试/捕获块。

这是事情。假设我有一个XML文件。

<A>
    <BB>37</BB>
    <CC>
        <DDD>1</DDD>
    </CC>
</A> 

事实上,我把它变成了一个对象,这意味着我可以做到 myXml.getA()等等。

在我的代码中,我搜索了这个对象中的给定元素,这意味着我有很多行,比如

int ddd = myXml.getA().getCC().getDDD();

问题是某些元素可能不存在,例如,另一个XML元素可能只是这样:

<A'>
    <BB'>37</BB'>
</A'> 

因此,如果我尝试获取ddd, getCC()会引发 NullPointerException

最后,我最终将其编码为:

int ddd;
try{
    ddd = myXml.getA().getCC().getDDD();
}
catch (NullPointerException e){
ddd = 0;
}

这可行,但代码变得非常难看。 我正在寻找类似

的解决方案
int ddd = setInt(myXml.getA().getCC().getDDD(), 0);
如果方法引发异常,

0 是默认值。

有一个很好的方法吗?

到目前为止,我找不到不会引发错误的解决方案。

谢谢你的帮助!

编辑:只是为了得到与XML相关的答案。 我向每个人展示了xml部分以了解问题。 在我的代码中,我无法访问XML,只能访问代表它的对象。

为了简短起见,我真正喜欢的是某种用于测试吸气剂的isNull方法。

7 个答案:

答案 0 :(得分:2)

您可以查看Null objec pattern

例如:

public class A {
    private C c;
    public C getC() {
        if (c == null) {
            c = new C(0); // the "null object"
        }
        return c;
    }
}

public class C {
    private int d;
    public C(int d) {
        this.d = d;
    }

    public int getD() {
        return d;
    }
}

但是个人,我对这段代码感觉不好:

int ddd = myXml.getA().getCC().getDDD();

强烈违反了law of Demeter。类调用者对A,C和D知之甚多。这段代码显然很难适应和维护。

答案 1 :(得分:2)

这有点像使用jaxb一样烦恼。在我的公司,我们做了足够的jaxb工作,值得编写一个xjc插件,它生成每个getter的“安全”版本,保证为任何非平凡的值返回非null值(在一个非常重要的值的情况下是不可变的实例)子对象并不存在)。

以下是我们生成的模型实体的示例:

public class ExampleUser implements Serializable {
    private final static long serialVersionUID = 20090127L;
    @XmlAttribute
    protected String name;
    @XmlAttribute
    protected String email;
    public final static ExampleUser EMPTY_INSTANCE = new ExampleUser() {
        private static final long serialVersionUID = 0L;
        @Override
        public void setName(java.lang.String value) { throw new UnsupportedOperationException(); }
        @Override
        public void setEmail(java.lang.String value) { throw new UnsupportedOperationException(); }
    };

    public String getName() {
        return name;
    }

    public void setName(String value) {
        this.name = value;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String value) {
        this.email = value;
    }
}

public class ExampleAccount implements Serializable {
    private final static long serialVersionUID = 20090127L;
    protected ExampleUser user;
    @XmlElement(name = "alias")
    protected List<String> aliases;
    @XmlAttribute
    protected String id;
    @XmlAttribute
    protected String name;
    public final static ExampleAccount EMPTY_INSTANCE = new ExampleAccount() {
        private static final long serialVersionUID = 0L;
        @Override
        public void setUser(com.boomi.platform.api.ExampleUser value) { throw new UnsupportedOperationException(); }
        @Override
        public List<String> getAliases() { return java.util.Collections.emptyList(); }
        @Override
        public void setId(java.lang.String value) { throw new UnsupportedOperationException(); }
        @Override
        public void setName(java.lang.String value) { throw new UnsupportedOperationException(); }
    };

    public ExampleUser getUser() {
        return user;
    }

    public void setUser(ExampleUser value) {
        this.user = value;
    }

    public List<String> getAliases() {
        if (aliases == null) {
            aliases = new ArrayList<String>();
        }
        return this.aliases;
    }

    public String getId() {
        return id;
    }

    public void setId(String value) {
        this.id = value;
    }

    public String getName() {
        return name;
    }

    public void setName(String value) {
        this.name = value;
    }

    public ExampleUser safeGetUser() {
        return (getUser() != null) ? getUser() : ExampleUser.EMPTY_INSTANCE;
    }
}

所以你可以编写这段代码而不用担心NPE:

userEmail = account.safeGetUser().getEmail();

答案 2 :(得分:1)

解决此类问题的两种常用方法是其他答案已经涵盖的空对象模式,并键入安全空值,例如Scala的选项。

http://www.scala-lang.org/api/current/scala/Option.html

有一些Java版本的Option敲响了。

http://functionaljava.googlecode.com/svn/artifacts/2.20/javadoc/fj/data/Option.html

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/Optional.html

与flatmap结合使用时,类型安全空值特别有用。

答案 3 :(得分:1)

使用Apache common-beanutils创建您的set方法。它将使用反射,然后你只有一个地方来捕捉错误。

它看起来像这样(没有编码,所以借口语法错误)。

int getInt(Object root, String beanPattern, int defaultValue)
{
   try
   {
      return PropertyUtils.getNestedProperty(root, beanPattern);
   }
   catch (Exception e)
   {
      return 0;
   }
}

这将被称为。

int ddd = getInt(myXml, "A.CC.DDD", 0);

答案 4 :(得分:0)

你不能只编写一个通用的函数来为每个值调用,并返回值或0。

这样的东西
myGetSomething(FOO){
  try {getFOO} catch ...
}

然后您的代码本身看起来不错,但该函数基本上每次调用都有一个try-catch。

答案 5 :(得分:0)

使用Xpath query代替get方法。如果它找不到元素路径,它将为您提供一个空列表。

List ddds = myXml.query("/AA/BB/CC/DDD");
if (!ddds.empty()) {}

正确的语法取决于您使用的XML库。

答案 6 :(得分:0)

GroovyXtend中写下部分代码;两者都支持?.语法,该语法返回表达式左侧的null计算结果为null。他们也摆脱了无用的get所以你可以写:

myXml.a?.cc?.ddd

与Groovy相比,Xtend的语法更糟,但它编译为普通的Java,所以你只需要在代码中添加一个带有一些帮助类的JAR来使用结果。