Returning multiple values from a method in Java
Written and © by P. Most ()
Problem
Sometimes it is necessary to return multiple values from a method. When you search the Internet for possible solutions, then you find a couple of suggestions:- Return an
Object[]
orList<Object>
. - Modify an
Object[]
orList<Object>
parameter. - Return a
Tuple<>
with the values. - Return a proper Object with the values as attributes.
- etc.
3. is at least typesafe but, if for example, you want to return multiple strings, then you have to know which tuple attribute contains what value.
4. is probably the cleanest solution but it means that you might end up with a lot of small "Result" classes.
Approach
Other curly brace languages like C++ or C# have built-in call-by-reference mechanism for this situation. C++ has reference parameter (&,
&&) or pointer parameter (
&,
*). C# has the
outand
refparameter modifiers.
Java on the other hand, has only call-by-value and so it is not possible to modify a given parameter directly. But if you call a method with an modifiable object, then you can modify the given object and store additional information. if we generalize this approach then we need a class which allows us to:
- Set a value in the called method.
- Get the value in the caller method.
outparameters are only assigned in the called method and are not used to deliver a value to the called method.
So let's try with a very simple generic
Out<T>:
package com.pera_software.blog.returning_multiple_values;
public class Out< T > {
private T _value = null;
public Out() {
}
public void set( T value ) {
_value = value;
}
public T get() {
return _value;
}
}
Examples
The following examples show howOut<T>can be used.
A simple example is splitting a string at a specified separator:
package com.pera_software.blog.returning_multiple_values;
import static org.junit.Assert.*;
public class Example1 {
public static void main( String[] arguments ) {
String keyValue = "language=java";
Out< String > key = new Out<>();
Out< String > value = new Out<>();
assertTrue( splitString( keyValue, '=', key, value ));
assertEquals( "language", key.get() );
assertEquals( "java", value.get() );
}
public static boolean splitString( String s, char separator, Out< String > before, Out< String > after ) {
int index = s.indexOf( separator );
if ( index >= 0 ) {
before.set( s.substring( 0, index ));
after.set( s.substring( index + 1 ));
}
return index >= 0;
}
}
Another example simulates a technique which is used quite often in C++. You call a
function which returns an error indicator and in case of an error fills an
error_code. In the Java case we fill an exception output parameter:
package com.pera_software.blog.returning_multiple_values;
import java.io.*;
public class Example2 {
public static void main( String[] arguments ) throws IOException {
Out< IOException > streamException = new Out<>();
try ( InputStream stream = openStream( "somefile.txt", streamException )) {
if ( stream == null )
System.out.printf( "Couldn't open stream because %s%n", streamException.get().getMessage() );
}
}
public static InputStream openStream( String fileName, Out< IOException > streamException ) {
try {
return new FileInputStream( fileName );
}
catch ( IOException exception ) {
streamException.set( exception );
return null;
}
}
}
As a last example I'll show how the C# TryParsefunction could be implemented in Java:
package com.pera_software.blog.returning_multiple_values;
import static org.junit.Assert.*;
public class Example3 {
public static void main( String[] arguments ) {
String number = "12345";
Out< Integer > integer = new Out<>();
assertTrue( tryParse( number, integer ));
assertEquals( 12345, integer.get().intValue() );
}
public static boolean tryParse( String number, Out< Integer > integer ) {
try {
integer.set( Integer.parseInt( number ));
return true;
}
catch ( NumberFormatException exception ) {
return false;
}
}
}
Implementation
This is theOut<T>version which you can find in my JavaAidKit Library. This version however is more strict and doesn't allow
nullvalues:
Out<T>::set()
will throw aNullPointerException
when trying to setnull
.Out<T>::get()
will throw aNoSuchElementException
when a value hasn't been set.
// Copyright 2016 Peter Most, PERA Software Solutions GmbH
//
// This file is part of the JavaAidKit library.
//
// JavaAidKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// JavaAidKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with JavaAidKit. If not, see <http://www.gnu.org/licenses/>.
package com.pera_software.aidkit.lang;
import java.util.*;
/**
* A class which can be used to return a value from a method via a parameter. The difference to
* {@link com.pera_software.aidkit.lang.Ref} is that it doesn't need to be initialized with a value.
*
* {@code}
* <blockquote><pre>
* public static boolean splitString( String s, char separator, Out< String > before, Out< String > after ) {
* int index = s.indexOf( separator );
* if ( index >= 0 ) {
* before.set( s.substring( 0, index ));
* after.set( s.substring( index + 1 ));
* }
* return index >= 0;
* }
* </pre></blockquote>
*
* @author P. Most
*/
public class Out< T > {
private T _value = null;
/**
* No initializing constructor because {@code out} parameters are only supposed to be set in the
* called method.
*/
public Out() {
}
/**
* Sets the value if not null, otherwise throws a {@code NullPointerException}.
* @param value the non-null value to set
* @throws NullPointerException if value is null
*/
public void set( T value ) {
if ( value != null )
_value = value;
else
throw new NullPointerException();
}
/**
* If a value is present, returns the value, otherwise throws {@code NoSuchElementException}.
* @throws NoSuchElementException if there is no value present
* @return the non-null value held
*/
public T get() {
if ( _value != null )
return _value;
else
throw new NoSuchElementException();
}
@Override
public String toString() {
String className = ( _value != null ) ? _value.getClass().getSimpleName() : Strings.EMPTY;
return String.format( "Out<%s>: '%s'", className, _value != null ? _value : "null" );
}
}
Related
The JavaAidKit Library. also contains aRef<T>type, which extends
Out<T>and contains an additional constructor which allows to set an initial value. Because
Ref<T>extends
Out<T>you can use it whenever an
Out<T>is expected.
Tips
- As a callee make sure that the
Out<T>
parameter gets a value and/or give the caller some indication that theOut<T>
parameter wasn't set. (The C# compiler will complain if you don't assign to anout
parameter, but Java and C++ don't!) - As a caller consider to use
Ref<T>
and initialize it with a default value. This will preventNoSuchElementException
if the callee didn't set a value.
Notes
After writingOut<T>and
Ref<T>I've found this article Simulating C# ref parameter in Java.