Java,如果我想从函数中返回不同的类型怎么办?

Java, what if I want to return different types from function?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public WHATTOWRITEHERE test()
{
    try
    {
        transaction.begin();
        code which may trigger exception
        transaction.commit();
        return true;
    }
    catch (javax.script.ScriptException ex)
    {
        transaction.rollback();
        return ex.getMessage();
    }
}

上面的代码打算做一些事情,如果它正常,那么返回true;如果没有(发生错误),那么应该返回这个错误消息string。它可以用PHP实现,但不能用Java

编辑:期望不能出去,必须在这里处理。


不能返回多个类型,但可以重新设计,这样就不必返回。一些可能性:

  • 不要返回错误消息。相反,引发或重新引发异常,并让调用者处理它。
  • 创建一些可以封装成功和错误状态以及所有相关信息的类,并返回该类的实例。
  • 我推荐选项1。您已经在处理一个异常,您可以看到它对IT错误处理的用途。没有理由让它停在那里,处理任何本地清理,然后继续向调用者报告。

    一些仓促构建的例子,现在我又回到了键盘前,仅仅是为了说明概念,而不是为了详尽或必要地逐字使用:

    清理,然后再冲洗:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public boolean test () throws javax.script.ScriptException {
        try {
            transaction.begin();
            ...
            transaction.commit();
            return true;
        } catch (javax.script.ScriptException ex) {
            transaction.rollback();
            throw ex;
        }
    }

    清除,然后根据需要重新引发其他异常类型:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public boolean test () throws MyGreatException {
        try {
            transaction.begin();
            ...
            transaction.commit();
            return true;
        } catch (javax.script.ScriptException ex) {
            transaction.rollback();
            throw new MyGreatException(ex);
        }
    }

    返回提供状态信息的对象(这只是一般思想的一个简单示例):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class TransactionResult {

        private final boolean failed;
        private final String reason;

        /** Construct a result that represents a successful transaction. */
        public TransactionResult () {
            failed = false;
            reason = null;
        }

        /** Construct a result that represents a failed transaction with a reason. */
        public TransactionResult (String failedReason) {
            failed = true;
            reason = failedReason;
        }

        public boolean isFailed () {
            return failed;
        }

        public String getReason () {
            return reason;
        }

    }

    然后:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public TransactionResult test () {
        TransactionResult result;
        try {
            transaction.begin();
            ...
            transaction.commit();
            result = new TransactionResult();
        } catch (javax.script.ScriptException ex) {
            transaction.rollback();
            result = new TransactionResult(ex.getMessage());
        }
        return result;
    }

    等。


    不要归还任何东西。在您回滚后重新抛出原始异常。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public void test()
    {
        try
        {
            transaction.begin();
            code which may trigger exception
            transaction.commit();
        }
        catch (javax.script.ScriptException ex)
        {
            transaction.rollback();
            throw ex;     // re-throw the original exception
        }
    }


    丑陋的解决方法,但是如果您真的想这样做,您可以始终定义一个包装状态和错误消息的助手类,但是我更喜欢@jsonc的方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    // Helper class
    class Pair<First,Second>{
        private First first;
        private Second second;
        Pair(First first,Second second){
            this.first = first;
            this.second = second;
        }
        public First getFirst(){ return this.first; }
        public First getSecond(){ return this.second; }
    }

    // Function returning two types
    public Pair<boolean,String> returnSomething(){
        try {
           return new Pair<boolean,String>(true,null);
        }catch(Exception e){
           return new Pair<boolean,String>(false,e.getMessage());
        }
    }

    // Calling this method would look like this
    Pair<String,boolean> result = returnSomething();

    // Retrieve status
    boolean status = result.getFirst();

    // Retrieve error message (This is null if an exception was caught!)

    String errorMessage = result.getSecond();


    如果您坚持,您可以退回Object。在这种情况下,true将自动发送到Boolean.TRUE。当然不建议这样做,这样会给调用者带来额外的麻烦,让他们弄清楚返回的对象是String还是Boolean。更糟的是,调用者不保证返回类型仅限于上述两种类型,但还应考虑到它可能是另一个类。

    更好的选择取决于情况,所以我可能无法告诉你什么是最好的。有几个想法浮现在脑海中,但请不要不加批判地使用:(1)返回String,并返回null,而不是真正的成功。(2)设计自己的返回类;例如,它可以同时包含布尔值和消息字符串。


  • 在您的情况下,应该使用异常,而不是隐藏异常。这不是结果,而是错误。了解如何在事务中处理异常!

  • 函数编程FANBOOS将提倡一个单元格式结构,正如您可以在Java 8的EDCOX1×0 API中发现的那样。

    也就是说,您可以返回Optional并在成功时(如果您没有return falsereturn true的话)不进行设置。

  • 为了清晰起见,最好用自定义类来构建类似这样的内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    interface Result {}
    class BooleanResult implements Result {
      boolean result;
      public boolean getResult() { return result; }
    }
    class ErrorResult implements Result {
      Exception cause;
      public Exception getCause() { return cause; }
    }
  • 您可以使用null值模拟Optional(如果只有一个布尔结果)。成功后,返回null。非空值表示错误。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    String perform() {
      try{
        ...
        return null; // No error
      } except(Exception e) { // bad code style
        return e.getMessage(); // Pray this is never null
      }
    }

    String err = perform();
    if (err != null) { throw up; }

    类似的API在旧的C库中相当常见。除0以外的任何返回值都是错误代码。成功后,结果将写入方法调用时提供的指针。

  • 你可以使用Object

    1
    2
    3
    4
    public Object perform() {...}

    Object o = perform();
    if (o instanceof Boolean) { ...

    这是80年代的编程风格。这就是PHP所做的,所以在爪哇是可行的!这很糟糕,因为它不是一个安全的lpnger类型。这是最糟糕的选择。

  • 我建议你试试1,3,2,4,5。在这个偏好中。或者更好,只考虑选项1和3。

    至于选项1。你真的应该学习如何使用Try with Resources。您的交易是一种资源。

    如果操作正确,代码将如下所示:

    1
    2
    3
    4
    try(Transaction a = connection.newTransaction()) {
      doSomethingThatMayFail(a);
      a.commit();
    } // No except here, let try handle this properly

    即使发生异常,Java也会调用EDCOX1 8Ω。然后它将向上抛出异常。sour事务类应该有这样的代码来处理回滚:

    1
    2
    3
    public void close() {
      if (!committed) rollback();
    }

    这是最优雅、最短、最安全的方法,因为Java确保调用EDOCX1 9Ω。抛出异常,然后正确地处理它。上面显示的代码截图是反模式的,并且很容易出错。


    Exceptions can't go outside, it has to be handled here.

    我必须说,这种限制只能使接口更难使用。假设您希望返回一些内容,以便调用方检查此方法中是否发生异常,而调用方可以忽略返回的值,无论发生什么。所以我想你应该给来电者一些灵活性:如果可能的话,他/她不需要为最终的结果操心。但是使用异常方法,调用者仍然可以这样做,使用空的(不推荐)catch子句。

    例外是最好的方法。除非"外部"是不支持异常的环境。然后你别无选择,只能在scala中想出类似于Try的东西。


    如果您使用Java 8,则可以返回EDCOX1 OR 1。然后,如果代码成功,则返回一个空的可选代码;如果出现故障,则返回一个包含故障消息的可选代码。