【问题标题】:Java, what if I want to return different types from function?Java,如果我想从函数返回不同的类型怎么办?
【发布时间】:2017-01-04 01:25:18
【问题描述】:
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();
    }
}

上面的代码打算做某事,如果OK则返回true如果不是(发生错误),则应该返回此错误消息string。用 PHP 可以,但用 Java 不行

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

【问题讨论】:

  • 当 smth 出错时只返回一个布尔值或抛出异常。将异常处理留给调用者。
  • 这在 Java 中是不可能的。让异常通过吧。
  • 既然你不想让异常出去,你可以这样做。如果一切顺利,则将“true”作为字符串返回。如果发生错误,则返回错误消息。在方法签名中,将返回类型设为 String。这基本上是一个糟糕的设计。
  • 这肯定是重复的。看看How to write Java function that returns values of multiple data types?。我当然希望还有其他人。
  • 既然要求ScriptException 不会逃逸到您的方法之外:回滚后是否会抛出其他异常以指示失败?一种标准方案是将原始异常包装在调用者可以处理的新异常中。

标签: java


【解决方案1】:

如果您使用的是 Java 8,则可以返回 Optional<String>。然后,如果代码成功,则返回一个空 Optional,如果失败,则返回一个包含失败消息的可选。

【讨论】:

    【解决方案2】:
    1. 在您的情况下,异常应该使用,而不是隐藏。这不是结果,而是错误。了解如何在事务中进行异常处理!

    2. 函数式编程爱好者会提倡类似 Monad 的结构,您可以在 Java 8 的 Optional<T> API 中找到。

      即您可以返回 Optional<String> 并在成功时将其取消设置(如果您没有 return falsereturn true)。

    3. 为了清楚起见,最好使用自定义类来构建类似的东西:

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

      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 以外的任何返回值都是错误代码。成功时,将结果写入方法调用时提供的指针。

    5. 你可以使用Object

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

      这是 1980 年代的编程风格。 这就是 PHP 所做的,所以它在 J​​ava 中实际上是可能的!这只是 bad 因为它不是 lpnger 类型安全的。这是最糟糕的选择。

    我建议您在此偏好中尝试 1.、3.、2.、4.、5.。或者更好的是,只考虑选项 1 和 3。

    至于选项 1。您真的应该学习如何使用 try-with-resources。您的交易资源。

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

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

    如果发生异常,Java 会调用a.close() 甚至。然后它将向上抛出异常。 Sour 事务类应该有这样的代码来处理回滚:

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

    这是最优雅的最短安全使用方法,因为Java确保调用close()抛出异常,然后妥善处理。您在上面显示的代码片段是一种反模式,并且很容易出错。

    【讨论】:

      【解决方案3】:

      您不能返回多种类型,但您可以重新设计,因此您不必这样做。一些可能性:

      1. 不返回错误消息。改为抛出或重新抛出异常,并让调用者处理它。
      2. 创建一些可以封装成功和错误状态以及所有相关信息的类,并返回其实例。

      我推荐选项 1。您已经在处理异常,您可以看到它用于错误处理。没有理由将其停在那里,处理任何本地清理,然后将其继续发送给调用者。


      现在我回到键盘前,一些仓促构建的示例,仅用于说明概念,而不是详尽无遗或必须逐字使用:

      清理然后重新抛出:

      public boolean test () throws javax.script.ScriptException {
          try {
              transaction.begin();
              ...
              transaction.commit();
              return true;
          } catch (javax.script.ScriptException ex) {
              transaction.rollback();
              throw ex;
          }
      }
      

      如果需要,清理然后重新抛出不同的异常类型:

      public boolean test () throws MyGreatException {
          try {
              transaction.begin();
              ...
              transaction.commit();
              return true;
          } catch (javax.script.ScriptException ex) {
              transaction.rollback();
              throw new MyGreatException(ex);
          }
      }
      

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

      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;
          }
      
      }
      

      然后:

      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;
      }
      

      等等

      【讨论】:

      • 我没提,但它是一种事务处理方法,所以没有期望可以走出去
      • @john 然后你必须选择选项 2 或它的一些变体。您对返回的值究竟做什么?这可能有助于缩小您的选择范围。我在用手机,否则我会输入一个不错的选项 2 示例,抱歉。
      • 调用者检查它的值,它不是真的,然后我们警告异常消息。
      • @JohnSmith 异常可以而且应该在方法之外抛出。您可以在方法中捕获它,执行rollback(),然后重新抛出异常以指示操作失败(并且已回滚)。任何其他方法都是糟糕的设计。
      • ^ @JohnSmith Kayaman 说了什么,并指出有时有问题的设计在小的一次性程序中是可以原谅的,或者当您面临其他人的不良限制时,您必须解决,但是如果您处于中等严肃的开发环境中并且可以完全控制您所做的设计选择,则不会。 :)
      【解决方案4】:

      异常不能出去,必须在这里处理。

      我必须说这个限制只会让界面更难使用。假设你想返回一些东西给调用者检查这个方法是否发生了异常,而调用者无论如何都可以忽略返回值。所以我想你想给调用者一些灵活性:如果可能的话,他/她不需要为最终结果而烦恼。但是使用异常方法,调用者仍然可以这样做,使用空(不推荐)catch 子句。

      例外是这里最好的方法。除非“外部”是不支持异常的环境。那么你别无选择,只能在 Scala 中提出类似 Try 的东西。

      【讨论】:

        【解决方案5】:

        UGLY 解决方法,但如果你真的想这样做,你总是可以定义一个包含状态和错误消息的 Helper 类,但我更喜欢@JsonC 的方法。

        // 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();
        

        【讨论】:

        • 为胜利而丑陋(但 the first part of this answer 让你因为丑陋而被击败,哈)! +1
        • 是的,它是在我在 Stackoverflow 上努力缩进时发布的。
        【解决方案6】:

        如果您坚持,可以返回Object。在这种情况下,true 将被自动装箱为Boolean.TRUE。当然不建议这样做,它会给调用者一些额外的麻烦,以确定返回的对象是String 还是Boolean。更糟糕的是,调用者无法保证返回类型仅限于上述两种,但还应考虑到它可能是另一个类。

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

        【讨论】:

          【解决方案7】:

          不要返回任何东西。回滚后只需re-throw the original exception

          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
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2012-12-05
            • 2021-05-01
            • 2022-01-05
            • 1970-01-01
            • 1970-01-01
            • 2019-04-02
            • 1970-01-01
            • 2011-04-23
            • 2019-12-19
            相关资源
            最近更新 更多