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); } } }
5 Comments
My initial thought was – why not just take the simplest route and assign to a non-final variable, then re-assign that to a final behaviour? Of course that leaks state, and so is an unacceptable solution. Consequently I am inclined to consider the following:
final Customer c = new Object() {
Customer value; {
try {
value = repo.getCustomer(1);
} catch (CustomerNotFoundException e) {
value = repo.createCustomer();
}
}}.value;
Essentially we simply hide the temporary variable \’value\’ by wrapping it up in an object. I\’m not that its a better solution to your, but I offer it as an alternative.
Advantages:
1. Simpler for the person who coded it to understand, since it looks more like a normal try/catch block.
2. If the \’getCustomer\’ method throws a RuntimeException then you will simply get it straight away, where as it gets wrapped in another RuntimeException with your solution.
3. No requirement for reflection hacks, that have potentially negative performance impact, and would require more knowledge on behalf of the reader.
4. Easier to Extend – chaining TryCatch instances would result in some pretty horrific nested inner class declarations, though I suppose one could build a TryCatch2, TryCatch3 etc. and abstract some of the mess a bit. In my solution you simply add another catch block.
Disadvantages:
1. Your approach encourages better code reuse, since you have built a generic try/catch abstraction, rather than requiring a one-off construct.
2. Uncertain where to place the braces. My placement of braces above would break the code style guidelines in many projects, but reflects my intuition that \’value\’ is really part of the method, and being wrapped in an object is simply there to hide the state. Which is correct is really up to you.
3. Anonymous Constructors look weird in Java.
4. The introduction of temporary state is still a crime against humanity, even if it is cleanly abstracted.
public class Test {
public static void main(String[] args) {
final CustomerRepo r = new CustomerRepo();
final Customer c = getCustomer2(r, 1337);
}
public static Customer getCustomer2(CustomerRepo r, int id) {
try {
return r.getCustomer(id);
} catch (CustomerNotFoundException e) {
return r.createCustomer();
}
}
}
you could also create that method in anonymous class like mullet wrote, but you wouldn\’t have to create non-final variables.
Why do you have necessity to keep “final” that pointer? How are you going to use it?
Not knowing your real purpose, here’s my way:
String temp;
try{
temp = “ok”;
}catch(Throwable t){
temp = “ko”;
}
final String def = temp;
//Then, if you wish:
temp = null;
Classic exposition, I have also mentioned it in my blog article. But it is a pity that almost no frienddiscussed it with me. I am very happy to see your article.
final Customer c = new Object() {
Customer value; {
try {
value = repo.getCustomer(1);
} catch (CustomerNotFoundException e) {
value = repo.createCustomer();
}
}}.value;