benjiPosted under Java, Uncategorized.

We often discuss Java limitations on IRC and try to come up with (sometimes silly) workarounds. Unfortunately after time passes it’s often easy to forget the outcome, and lose code snippets. So I thought I’d start blogging some of them so I don’t lose them, and other people might suggest other ways of doing things that we’ve overlooked.

This particular problem occurs when you want to assign the result of a method that can throw an exception to a final variable. For example:

final Customer c;
try
{
	c = getCustomer(id);
} catch (CustomerNotFoundException e)
{
	c = createNewCustomer();
}

This will fail to compile with “variable c might already have been assigned”. Of course making c not final would solve the problem, but that’s no fun.

If we were not using Exceptions, Java provides a useful ternary operator “?:” that lets us do things like:

final Customer c = customerExists(id) ? getCustomer(id) : createNewCustomer();

Which is nice and clean, but means that getCustomer is going to have to return null or throw a RuntimeException in the case that there is no matching customer, which is undesirable. Also customerExists() may be expensive.

We could also possibly use something along the lines of

final Option<Customer> c = getCustomer();

Both of these alternatives, however, require changing the API you’re consuming, and avoiding Exceptions. It would be nice if there was an equivalent of “?:” for try/catch so that you could assign the result to a final variable. The best I can manage in Java is below, can anyone do better?

import java.lang.reflect.ParameterizedType;
 
abstract class TryCatch<T, U extends Exception>
{
	public T value()
	{
		try
		{
			return Try();
		} catch (Exception e)
		{
			if (getTypeOfU().isAssignableFrom(e.getClass()))
			{
				return Catch();
			} else
			{
				throw new RuntimeException(e);
			}
		}
	}
 
	@SuppressWarnings("unchecked")
	private Class<U> getTypeOfU()
	{
		return (Class<U>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
	}
 
	public abstract T Try() throws U;
 
	public abstract T Catch();
}
 
//Example
public class Main
{
	private static CustomerRepo repo = new CustomerRepo();
 
	public static void main(String[] args) 
	{
		final Customer c = new TryCatch<Customer, CustomerNotFoundException>()
		{
			public Customer Try() throws CustomerNotFoundException
			{
				System.out.println("in try");
				return repo.getCustomer(1);
			}
 
			public Customer Catch()
			{
				System.out.println("in catch");
				return repo.createCustomer();
			}
		}.value();
	}
 
}
 
class CustomerRepo
{
 
	public Customer getCustomer(int id) throws CustomerNotFoundException
	{
		throw new CustomerNotFoundException();
	}
 
	public Customer createCustomer()
	{
		return new Customer();
	}
}
 
class Customer
{
}
 
class CustomerNotFoundException extends Exception
{
}

In C# we don’t run into the same problem since readonly is much less useful than Java’s final. However, if we wanted to try/catch at the same time we can do a bit better. Here’s an alternative in C#:

using System;
 
public class Example
{
	public static void Main()
	{
		Example t = new Example();
		t.Foo();
	}
 
	public void Foo()
	{
		String result1 = this.Try(() => GetBar(true)).Catch<BarException>(() => "Caught a BarException");
		String result2 = this.Try(() => GetBar(false)).Catch<BarException>(() => "Caught a BarException");
		Console.WriteLine(result1);
		Console.WriteLine(result2);
	}
 
	public String GetBar(bool succeed)
	{
		if (succeed)
			return "Success!";
		else
			throw new BarException();
	}
}
 
public class BarException : Exception {}
 
public class Tryer<TResult>
{
	private readonly Func<TResult> toTry;
	internal Tryer(Func<TResult> toTry)
	{
		this.toTry = toTry;
	}
 
	public TResult Catch<TException>(Func<TResult> whenCaught)
		where TException : Exception
	{
		try
		{
			return toTry();
		} catch (TException)
		{
			return whenCaught();
		}
	}
}
 
namespace System
{
	public static class ProvidesTry
	{
		public static Tryer<TResult> Try<T,TResult>(this T other, Func<TResult> toTry)
		{
			return new Tryer<TResult>(toTry);
		} 
	}
}

Tags:

Leave a Reply

  • (will not be published)