Let the code speak rather than comment (Maybe return value)

table of contents

1.First of all 2. Let individual types speak 3. Let a general-purpose type speak 4. Finally

Introduction

I'm sure you've used the following functions with comment documentation.

java:java.lang.String


 * ...
 *Return value:
 *The index of the position where the specified character first appears in the character sequence represented by this object. If there are no letters-1。
 */
public int indexOf(int ch);

The return value of this function is trying to represent a success value of "position index" and a failure value of "-1" at the same time. Here, the failure value = -1 is probably not a particularly valid value, and anything that does not overlap with the success value> = 0 would have been fine. Therefore, you must ** refer to the documentation ** to check if this kind of IF function succeeded or failed **. In other words, this function is ** not told by the code **. Letting the code speak means writing code that is easy to understand and use without looking at comments or documentation. The following is an example of letting the code speak about the same function.

Let individual types speak

This means that the return value will be an object of your own defined class.

StringUtil


class StringUtil {
	public static ResultOfIndexOf indexOf(String haystack, char needle) {
		for (int i = 0; i < haystack.length(); ++i)
			if (haystack.charAt(i) == needle)
				return new ResultOfIndexOf(i);
		return new ResultOfIndexOf();
	}
}

class ResultOfIndexOf {
	private final boolean isFound;
	private final int foundIndex;
	ResultOfIndexOf() {
		this(false, 0);
	}
	ResultOfIndexOf(int foundIndex) {
		this(true, foundIndex);
	}
	private ResultOfIndexOf(boolean isFound, int foundIndex) {
		this.isFound = isFound;
		this.foundIndex = foundIndex;
	}
	public boolean isFound() {
		return isFound;
	}
	public int foundIndex() {
		if (!isFound)
			throw new RuntimeException("index not found");
		return foundIndex;
	}
}

The code on the side that uses this is as follows. (ideone)

Main


class Ideone
{
	public static void main (String[] args) throws java.lang.Exception
	{
		ResultOfIndexOf result = StringUtil.indexOf("hello, world", 'l');
		if (result.isFound())
			System.out.println(String.format("index=%d", result.foundIndex()));
	}
}

If the intermediate variable is in the way, pass a callback and have it called only if successful. (ideone)

Main


import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.function.*;

class Ideone
{
	public static void main (String[] args) throws java.lang.Exception
	{
		StringUtil.indexOf("hello, world", 'l')
			.DoIfSuccess(foundIndex -> {
				System.out.println(String.format("index=%d", foundIndex));
			});
	}
}

class StringUtil {
	public static ResultOfIndexOf indexOf(String haystack, char needle) {
		for (int i = 0; i < haystack.length(); ++i)
			if (haystack.charAt(i) == needle)
				return new ResultOfIndexOf(i);
		return new ResultOfIndexOf();
	}
}

class ResultOfIndexOf {
	private final boolean isFound;
	private final int foundIndex;
	ResultOfIndexOf() {
		this(false, 0);
	}
	ResultOfIndexOf(int foundIndex) {
		this(true, foundIndex);
	}
	private ResultOfIndexOf(boolean isFound, int foundIndex) {
		this.isFound = isFound;
		this.foundIndex = foundIndex;
	}
	public void DoIfSuccess(Consumer<Integer> postAction) {
		if (isFound && postAction != null)
			postAction.accept(foundIndex);
	}
}

Let a general-purpose type speak

In the above example, the type increases for each function, so create a generics class and name it Maybe and use it. (ideone)

Maybe


import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.function.*;

class Ideone
{
	public static void main (String[] args) throws java.lang.Exception
	{
		StringUtil.indexOf("hello, world", 'l')
			.DoIfSuccess(foundIndex -> {
				System.out.println(String.format("index=%d", foundIndex));
			});
	}
}

class StringUtil {
	public static Maybe<Integer> indexOf(String haystack, char needle) {
		for (int i = 0; i < haystack.length(); ++i)
			if (haystack.charAt(i) == needle)
				return Maybe.<Integer>just(i);
		return Maybe.<Integer>nothing();
	}
}

class Maybe<ResultType> {
	public static <R> Maybe<R> nothing() {
		return new Maybe(false, null);
	}
	
	public static <R> Maybe<R> just(R resultValue) {
		return new Maybe(true, resultValue);
	}
	
	private final boolean isSucceeded;
	private final ResultType resultValue;
	
	private Maybe(boolean isSucceeded, ResultType resultValue) {
		this.isSucceeded = isSucceeded;
		this.resultValue = resultValue;
	}
	
	public void DoIfSuccess(Consumer<ResultType> postAction) {
		if (isSucceeded && postAction != null)
			postAction.accept(resultValue);
	}
}

at the end

Personally, I prefer the Maybe return value. Of course, if it's in the standard library, I think it's better to use it (Optional for java8). If you don't have one, let the code speak even if you make it yourself.

Recommended Posts

Let the code speak rather than comment (Maybe return value)
9 Corresponds to the return value
Ruby methods return the last evaluated value