【问题标题】:Output Parameters in JavaJava 中的输出参数
【发布时间】:2009-09-10 07:55:24
【问题描述】:

使用第三方 API,我观察到以下情况。

而不是使用,

public static string getString(){
   return "Hello World";
}

它使用类似的东西

public static void getString(String output){

}

我正在分配“输出”字符串。

我很好奇实现这种功能的原因。使用这样的输出参数有什么好处?

【问题讨论】:

  • 那个例子是错误的。 Java 没有输出参数。
  • @Chathuranga:仔细看看这个例子。它使用的是 StringBuilder 而不是 String!!
  • 这个String对象是java.lang.String吗? (这是不可变的)

标签: java api methods output-parameter


【解决方案1】:

你的例子有些不对劲。

class Foo {

    public static void main(String[] args) {
        String x = "foo";
        getString(x);
        System.out.println(x);
    }

    public static void getString(String output){
        output = "Hello World"
    }
}

在上面的程序中,会输出字符串“foo”,不是“Hello World”。

有些类型是可变的,在这种情况下,您可以修改传递给函数的对象。对于不可变类型(例如String),您必须构建某种可以传递的包装类:

class Holder<T> {
    public Holder(T value) {
        this.value = value;
    }
    public T value;
}

然后你可以绕过持有人:

public static void main(String[] args) {
    String x = "foo";
    Holder<String> h = new Holder(x);
    getString(h);
    System.out.println(h.value);
}

public static void getString(Holder<String> output){
    output.value = "Hello World"
}

【讨论】:

    【解决方案2】:

    有时这种机制可以避免创建新对象。

    示例: 如果存在适当的对象,则将其传递给方法并更改某些字段会更快。

    这比在被调用的方法中创建一个新对象并返回并分配它的引用(产生需要在某个时候收集的垃圾)更有效。

    【讨论】:

    • 对象创建成本低廉,不应成为使用除了减少创建对象数量之外没有其他目的的构造使代码混乱的原因。
    • @Bombe: (1) 即使对象创建很便宜,对象创建 分配也不能比分配便宜 (2) 成本不仅是创建;还有收费的垃圾收集。 (3) 在 Java 环境中,对象的创建非常昂贵(Java Card)。此处应尽可能避免创建对象
    【解决方案3】:

    那个例子是错误的,Java没有输出参数。

    您可以模仿这种行为的一件事是:

    public void doSomething(String[] output) {
        output[0] = "Hello World!";
    }
    

    但是恕我直言,这在多个层面上都很糟糕。 :)

    如果你想让一个方法返回一些东西,让它返回它。如果您需要返回多个对象,请创建一个容器类将这些对象放入并返回。

    【讨论】:

      【解决方案4】:

      我不同意 Jasper:“在我看来,这是一种返回多个结果的非常丑陋和糟糕的方式”。 在 .NET 中有一个利用输出参数的有趣结构:

      bool IDictionary.TryGet(key, out value);
      

      我觉得它非常有用且优雅。这是最方便的方式来询问一个项目是否在集合中并同时返回它。有了它,你可以写:

      object obj;
      if (myList.TryGet(theKey, out obj))
      {
        ... work with the obj;
      }
      

      如果我看到像这样的旧式代码,我会经常责骂我的开发人员:

      if (myList.Contains(theKey))
      {
        obj = myList.Get(theKey);
      }
      

      你看,它把性能减半。在 Java 中,无法在一次调用中区分现有项目的空值与 Map 中不存在的项目。有时这是必要的。

      【讨论】:

      • 我不确定答案或评论是否已被删除,但我在此页面的其他任何地方都没有看到“Jasper”。问题是关于 Java 的,但这个答案讨论了 .NET,但没有提供任何对 OP 在 Java 中使用有用的东西。
      【解决方案5】:

      此功能有一个很大的缺点 - 它不起作用。函数参数是函数的局部参数,分配给它们不会在函数之外产生任何影响。
      另一方面

      void getString(StringBuilder builder) {
          builder.delete(0, builder.length());
          builder.append("hello world");
      }
      

      会起作用,但我认为这样做没有任何好处(除非您需要返回多个值)。

      【讨论】:

      • 为什么要使用 StringBuilder?
      • 你问的是“StringBuilder有什么用”还是“为什么在这种情况下使用StringBuilder”?通常 StringBuilder 用于快速字符串构建和连接(顾名思义)。在这种情况下,这是达到预期效果的唯一方法(通过修改函数参数返回字符串值)。您不能对 String 对象执行此操作,因为它是不可变的。
      【解决方案6】:

      字符串是不可变的,你不能将Java的伪输出参数与不可变对象一起使用。

      另外,output 的范围仅限于 getString 方法。如果你改变 output 变量,调用者将看不到任何东西。

      但是,您可以做的是更改参数的状态。考虑以下示例:

      void handle(Request r) {
          doStuff(r.getContent());
          r.changeState("foobar");
          r.setHandled();
      }
      

      如果您的管理器使用单个请求调用多个句柄,您可以更改请求的状态以允许(由其他处理程序)对修改后的内容进行进一步处理。经理也可以决定停止处理。

      优点:

      • 您不需要返回包含新内容以及是否应该停止处理的特殊对象。该对象只会被使用一次,创建对象会浪费内存和处理能力。
      • 您不必创建另一个 Request 对象并让垃圾收集器摆脱现在已过时的旧引用。
      • 在某些情况下,您无法创建新对象。例如,因为该对象是使用工厂创建的,而您无权访问它,或者因为该对象具有侦听器,而您不知道如何告诉正在侦听旧请求的对象他们应该改为收听新的请求。

      【讨论】:

        【解决方案7】:

        实际上,在 java 中不可能有 out 参数,但是您可以通过编写一个泛型类,其中不可变是泛型value 和 setter 和 getter 或使用数组,其中元素 0(长度为 1)是提供它首先实例化的值,因为在某些情况下您需要返回多个值,而必须编写一个类只是为了返回它们该类仅被使用,只是浪费文本并且不能真正重复使用。

        现在是 C/C++ 和 .Net(单声道或 MS),它敦促我 Java 至少不支持对原语的取消引用;所以,我改用数组。

        这是一个例子。假设您需要创建一个函数(方法)来检查数组中的索引是否有效,但您还想在索引验证后返回剩余的长度。让我们在 c 中将其称为 'bool validate_index(int index, int arr_len, int&rem)'。在 java 中执行此操作的一种方法是“Boolean validate_index(int index, int arr_len, int[] rem1)”。 rem1 仅表示数组包含 1 个元素。

        public static Boolean validate_index(int index, int arr_len, int[] rem1)
        {
            if (index < 0 || arr_len <= 0) return false;
        
            Boolean retVal = (index >= 0 && index < arr_len);
        
            if (retVal && rem1 != null) rem1[0] = (arr_len - (index + 1));
        
            return retVal;
        
        }
        

        现在,如果我们使用它,我们可以获得布尔返回和余数。

         public static void main(String[] args)
         {
            int[] ints = int[]{1, 2, 3, 4, 5, 6};
            int[] aRem = int[]{-1};
            //because we can only scapegoat the de-ref we need to instantiate it first.
            Boolean result = validate_index(3, ints.length, aRem);
        
            System.out.println("Validation = " + result.toString());
            System.out.println("Remainding elements equals " + aRem[0].toString());
        
         }
        

        puts:验证 = True puts: 剩余元素等于 2

        数组元素要么指向栈上的对象,要么指向堆上对象的地址。因此,即使对于数组,也绝对可以将其用作取消引用,方法是将其实例化为 myArrayPointer = new Class[1][] 然后将其传递给数组,因为有时您不知道数组的长度会是多少直到调用通过类似 'Boolean tryToGetArray(SomeObject o, T[][] ppArray)' 的算法,这与 c/c++ 中的 'template bool tryToGetArray (SomeObject* p, T** ppArray)' 或C#'bool tryToGetArray(SomeObject o, ref T[] array)'。 只要 [][] 或 [] 首先在内存中使用至少一个元素进行实例化,它就可以正常工作。

        【讨论】:

          【解决方案8】:

          在我看来,当函数中有多个结果时,这很有用。

          【讨论】:

          • 在我看来,这是返回多个结果的一种非常丑陋和糟糕的方式。相反,创建一个(bean)类,其中包含您需要返回的多个东西,并让该方法返回一个实例。稍差一点:返回一个包含多个事物的 Object[]。
          猜你喜欢
          • 1970-01-01
          • 2012-05-09
          • 1970-01-01
          • 2012-02-27
          • 1970-01-01
          • 1970-01-01
          • 2019-03-23
          • 2012-01-25
          • 2014-08-22
          相关资源
          最近更新 更多