Use truthy falsy booleans in Java

Some scripting languages such as PHP, JavaScript, and Groovy allow the use of object reference in a conditional context. They have a standard boolean coercion for certain objects or references, “truthy” and “falsy” values. Could this be useful in Java?

For example, we can run this inline script in the Groovy language:

groovy -e "if(![]) println 'Hello empty array!'"
Hello empty array!

or,

groovy -e "println( [] ? '' : 'Hello empty array!')"
Hello empty array!

What this did is take the empty array, [], and coerced it to a boolean value, then we tested that value for false.

The equivalent in Java could be:

String[] ary = new String[0];
if(ary.length == 0){
   System.out.println("Hello empty array");
}

In Java we have to directly access a property or invoke a method to create a boolean value. In some scripting languages, an array is false if it is null or empty. This simplifies the use of scripting and reduces the syntactic clutter. Of course, this also can introduce very subtle issues for the unwary.

Boolean coercion in Java

What if we could have an asBoolean type of conversion in Java, like:

If( !valid(new Object[0])){
   System.out.println("Hello empty array");
}

Some possible method names are: true(), false(), truth(), valid(), asBoolean(), and so forth. I used “valid”, since it is not really about truth, but about whether a value is ready for use.

One truthy and falsy list for Java could be:

  • Object: false if null
  • Collection: false if empty or null
  • Maps: false if empty or null
  • Boolean: false if false
  • Iterators: false if there are no further elements
  • Strings: non-empty are true
  • Numbers: non-zero are true
  • Float: non-null are true

Implementation
Below is a simplistic implementation of a Truth class that provides truthy support in Java. A full implementation would have to take into account a lot of things, such as wrapped objects, multidimensional arrays, and so forth.
Example use:

import static com.octodecillion.util.Truth.valid;

List value = new ArrayList();
if(valid(value)){
     // do stuff with value
} 

Unfortunately in the implementation below, the test for null and the object’s condition, such as whether a list is empty, mixes two very different object states. Thus, in certain situations, further tests are required, or the idiomatic Java coding style retained.

Listing 1, Simple Java implementation of truthyness

package com.octodecillion.util;

import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertThat;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import mockit.Expectations;

import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/**
 * Coerce Objects to boolean value in script-like manner.
 * <p>
 * <ul>
 * <li>Object: false if null</li>
 * <li>Collection: false if empty</li>
 * <li>Map: false if empty</li>
 * <li>Iterators: false if there are no further elements</li>
 * <li>Enumeration: false if there are no further elements</li>
 * <li>Strings: empty are false</li>
 * <li>Float: null is false</li>
 * <li>Numbers: non-zero are true</li>
 * </ul>
 * 
 * @author <a href="http://octodecillion.com/">Josef Betancourt</a>
 * 
 */
public class Truth {

	/** non-public since this is a static utility class */
	private Truth() {
		// singleton utility
	}

	public static boolean valid(String ref) {
		return isNull(ref) ? false : ref.length() != 0;
	}	

	public static boolean valid(Collection<?> ref) {
		return isNull(ref) ? false : !ref.isEmpty();
	}

	public static <E> boolean valid(Set<E> ref) {
		return isNull(ref) ? false : !ref.isEmpty();
	}

	public static <E> boolean valid(Enumeration<E> ref) {
		return isNull(ref) ? false : !ref.hasMoreElements();
	}

	public static <E> boolean valid(Iterator<E> ref) {
		return isNull(ref) ? false : !ref.hasNext();
	}

	public static boolean valid(Map<?, ?> ref) {
		return isNull(ref) ? false : !ref.isEmpty();
	}

	public static boolean valid(Boolean ref) {
		return isNull(ref) ? false : ref.booleanValue();
	}
	
	public static boolean valid(Integer ref) {
		return isNull(ref) ? false : ref != 0;
	}

	public static boolean valid(byte ref) {
		return ref != 0;
	}
	
	public static boolean valid(Float ref){
		return ref != 0;
	}

	public static boolean valid(Byte ref) {
		return isNull(ref) ? false : ref != 0;
	}

	public static boolean valid(long ref) {
		return ref != 0;
	}

	public static boolean valid(Long ref) {
		return isNull(ref) ? false : ref != 0;
	}

	public static boolean valid(BigDecimal ref) {
		return isNull(ref) ? false : ref.compareTo(BigDecimal.ZERO) != 0;
	}

	public static boolean valid(char ref) {
		return ref != 0;
	}

	public static boolean valid(double ref) {
		return ref != 0;
	}

	public static boolean valid(short ref) {
		return ref != 0;
	}

	private static boolean isNull(Object obj) {
		return obj == null;
	}

	/**
	 * Test if Object is true from a scripting standpoint.
	 * <p>
	 * @param ref an object reference
	 * @return true if object in not null or is not "empty"
	 * @throws Throwable
	 */
	public static boolean valid(Object ref){
		if (ref == null) {
		  return false;
		}

		Class<? extends Object> actualClass = ref.getClass();
		String className = actualClass.getName();

		if (className.startsWith("[")) {
			return validArray(ref);
		}
		
		return true;
	}
	
	public static boolean validArray(Object ref) {
		// TODO
		throw new UnsupportedOperationException("array coercion not supported");
	}	
	
	/**
	 * 
	 * Tests of {@link Truth}.
	 * 
	 * Uses nested JUnit testing approach advocated by Ben J. Christensen in <a
	 * href=
	 * "http://benjchristensen.com/2011/10/23/junit-tests-as-inner-classes/"
	 * >"JUnit Tests as Inner Classes"</a>
	 * 
	 * @author jbetancourt
	 * 
	 */
	@RunWith(JUnit4.class)
	public static class TruthTest {
		@Test
		public final void truth_empty_string() throws Throwable {
			String actual = "";
			assertThat(valid(actual), is(false));
		}
		
		@Test
		public final void truth_nonempty_string() throws Throwable {
			String actual = "x";
			assertThat(valid(actual), is(true));
		}
		
		@Test
		public final void truth_null_object() throws Throwable {
			Object obj = null;
			assertThat(valid(obj),is(false));
		}

		@Test
		public final void truth_not_null_object() throws Throwable {
			Object obj = new Object();
			assertThat(valid(obj),is(true));
		}

		@Test
		public final void truth_Byte() {
			Byte b = new Byte((byte) 0);
			assertThat(valid(b),is(false));
		}
		
		@Test
		public final void truth_Integer() {
			Integer num = new Integer(0);
			assertThat(valid(num),is(false));
		}
		
		@Test
		public final void truth_nonzero_Integer() {
			Integer num = new Integer(47);
			assertThat(valid(num),is(true));
		}
		
		@Test
		public final void zero_float(){
			Float fl = +0.0f;
			boolean actual = valid(fl);
			assertThat(valid(actual), is(false));
		}

		@Test
		public final void truth_Boolean() {
			Boolean bool = Boolean.TRUE;
			boolean actual = valid(bool);
			assertThat(actual, is(true));
		}

		@Test
		public final void null_Boolean() {
			Boolean bool = null;
			assertThat(valid(bool), not(true));
		}

		@Test
		public final void truth_Boolean_false() {
			Boolean bool = Boolean.FALSE;
			boolean actual = valid(bool);
			assertThat(actual, not(true));
		}
		
		@Test
		public final void truth_list() throws Throwable {			
			final List<String> lst = new ArrayList<String>();
			boolean actual = valid(lst);
			assertThat(actual, is(false));			
		}

		@SuppressWarnings("static-access")
		@Test
		public final void truth_empty_list() throws Throwable {			
			final List<String> lst = new ArrayList<String>();
			
			new Expectations() {
				Truth truth;
				{
					truth.valid((Collection<?>)lst); minTimes = 1;
				}
			};			
			
			boolean actual = Truth.valid(lst);
			assertThat(actual, is(false));
		}

		@Test
		public final void truth_empty_map() throws Throwable {
			@SuppressWarnings("rawtypes")
			Map map = new HashMap();
			boolean actual = valid(map);
			assertThat(actual, not(true));
		}	
		
		@Test
		public final void primitive_byte(){
			int x = 3;
			boolean actual = valid(x);
			assertThat(actual, is(true));		
		}
		
		@Test
		@Ignore
		public final void two_dimensional_array() throws Throwable{
			int[][] ref = new int[2][4];
			boolean actual = valid(ref);
			assertThat(actual, is(true));		
		}
		
		@Test
		@Ignore
		public final void truth_array_empty() throws Throwable {
			int[] ary = new int[3];
			boolean actual = valid(ary);
			assertThat(actual, is(true));
		}
		
	}
}

Scripting language support for truthy and falsey values

Here is the Groovy language coercion support

  • Object: false if null
  • Collection: false if empty
  • Maps: false if empty
  • Iterators: false if there are no further elements
  • Strings: non-empty are true
  • Numbers: non-zero are true

In JavaScript we have:

  • false // obviously
  • 0 // The only falsy number
  • “” // the empty string
  • null
  • undefined
  • NaN
  • Numbers: non-zero are true

In Python:

  • False: false
  • None: false
  • 0 : false
  • “” : false
  • empty containers: false

In Ruby

  • false: false
  • nil: false
  • true:true
  • -1:true
  • 0:true
  • 0.0:true
  • “”:true
  • “not empty”:true
  • []:true
  • {x:1}:true
  • Object.new:true

In Clojure:

  • nil: false
  • false: false

Perl uses

  • 0 # converts to “0″ so it’s false;
  • “” # is empty string so it’s false;
  • “0.0″ # not “0″ or “” so it’s true
  • 0.0 # computes to 0 and is then converted to “0″ so false
  • undef # evaluates to “” so it’s false — note, you may get a warni+ng “use of uninitialized value” if you are using -w
  • 2-3+1 # computes to 0 which is converted to “0″ so it is false

Links

Similar Posts:

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>