Scala does not implement the concept of checked exceptions

Unlike Java, when implementing code with Scala, you do not need to catch (nor declare in method signatures) checked exceptions to be thrown.

In Scala, you do not have to deal with checked exceptions even when a Scala class implements a Java interface method with a declared checked exception.

The example code below will use the interface java.lang.Appendable which has an 'append' method which declares that the checked exception IOException might be thrown. The purpose of reusing this interface might be that you want to use a common base type for StringBuilder and StringBuffer to do some appendings through polymorphism. However, there are also other implementations of the Appendable interface, e.g. 'FileWriter' which might be the reason for why the IOException has propagated into the method signatures in Appendable.

The example below defines a Java interface with some SQL utility method, which might be used for constructing a where clause part (an "IN statement") by using either a StringBuffer or StringBuilder, but declared through the common base type Appendable. For the purpose of this example, the interface defines two very similar methods, with the only difference that one of the method signatures will define that the checked exception IOException might be thrown, while the other method signature does not declare any checked exception.

To avoid the checked exception to be declared in the metod signature, the Java implementation needs to catch the IOException when invoking 'appendable.append'. Here is the interface:

/**
 * The main purpose of using this interface would be to use StringBuffer or StringBuilder instances as 'Appendable'
 * parameters, even though for example a 'FileWriter' might also be used as Appendable since it implements that interface.  
 */
public interface SqlAppender {
    // ...
    
    /**
     * @param appendable e.g. with the parameters below, the appendable may become appended with the
     * string "country IN ('sweden','usa')"
     * (to be used as a part of a SQL where clause, of course prefixed with "SELECT ... FROM ... WHERE ")
     * @param columnName e.g. "country"
     * @param values e.g. a list of the values "sweden" and "usa"
     */
    void addSqlWhereClausePartWith_IN_Statement_WITH_DeclaredCheckedException(
        Appendable appendable, // e.g. StringBuffer or StringBuilder (or possibly e.g. FileWriter)
        String columnName,
        List values
    ) throws IOException;


    /**
     * @see #addSqlWhereClausePartWith_IN_Statement_WITH_DeclaredUncheckedException(Appendable, String, java.util.List)
     * which has the exact same purpose, except from the method signatures with or without declaring a checked exception 
     */
    void addSqlWhereClausePartWith_IN_Statement_withOUT_DeclaredCheckedException(
        Appendable appendable, // e.g. StringBuffer or StringBuilder (or possibly e.g. FileWriter)  
        String columnName,
        List values
    );

    // ...
}

Below is a potential Java implementation of the interface. Note that whenever the method 'Appendable.append' is invoked, it can throw the checked exception IOException, which must then be either declared in the method signature or has to be caught and handled in some other way, for example by throwing an unchecked exception as in the second method below.

public class SqlAppenderImplementedWithJava implements SqlAppender {
    public void addSqlWhereClausePartWith_IN_Statement_WITH_DeclaredCheckedException(
        final Appendable appendable,
        final String columnName,
        final List values
    )
        throws IOException // as in the interface declaration
    {
        // the method "append" being used below is declared to throw IOException,
        // which either must be caught or propagated into method signature (which it is above)
        appendable.append(" ");
        appendable.append(columnName);
        appendable.append(" IN (");
        for (int i=0; i 0) {
                appendable.append(",");
            }
            appendable.append("'");
            appendable.append(values.get(i));
            appendable.append("'");

        }
        appendable.append(") ");
    }

    public void addSqlWhereClausePartWith_IN_Statement_withOUT_DeclaredCheckedException(
        final Appendable appendable,
        final String columnName,
        final List values
    )
     // throws IOException // note that this method must not declare the IOException since the interface does not ! 
    {
        try {
            // reuse the implementation of the other method above, which can throw an exception
            addSqlWhereClausePartWith_IN_Statement_WITH_DeclaredCheckedException(appendable, columnName, values);
        } catch (IOException e) { // Java must either catch or throw an checked exception
            // rethrow as unchecked exception, to avoid declaring the checked exception in the signature,
            // which was not allowed for this method signature defined in the implemented interface SqlAppender 
            throw new RuntimeException(e);
        }
    }
}

A Scala implementation of the interface might look as below:

// Please note that the semantic of the methods in the Java interface 'SqlAppender'
// implemented by this Scala class below was chosen from a Java perspective in this example,
// i.e. regardless the suffix of the method names below, the Scala code below does not declare
// any exception to be thrown.
// The Java method 'Appendable.append' (which is frequently used in this class)
// can throw the checked exception 'IOException', which is also declared in one of the method
// signatures in the Java interface 'SqlAppender' implemented by this Scala class.
// Nevertheless, the Scala does not have to deal with the exception in any way, but if
// it would occur, then it will be thrown anyway in runtime, but at compile time there
// is no kind of action being enforced, regarding the exceptions
class SqlAppenderImplementedWithScala extends SqlAppender {

    def addSqlWhereClausePartWith_IN_Statement_WITH_DeclaredCheckedException(
      appendable: Appendable, 
      columnName: String,
      values: java.util.List[String]
    ): Unit = {
      appendable.append(" ") // no need to catch or declare/throw the checked exception IOException
      appendable.append(columnName)
      appendable.append(" IN (")
      var i: Int = 0
      // This 'while loop implementation' below would unlikely
      // be appreciated by a Scala programmer
      // (since 'while loops' are generally discouraged in Scala)
      // but it should still be easy to understand by
      // any Java programmer, and also the implementation
      // of the iteration is not at all relevant for
      // this example, but the essential thing is how
      // the unchecked exception are never enforced to be declared
      // nor catched
      while (i < values.size) {
        {
          if (i > 0) {
            appendable.append(",")
          }
          appendable.append("'")
          appendable.append(values.get(i))
          appendable.append("'")
        }
        i += 1;
      }
      appendable.append(") ")
    }

    // Note that this method might throw an IOException, which is a checked exception
    // which with a Java implementation has to be declared in the Java interface method signature,
    // or alternatively would have to be caught and rethrown as unchecked
    def addSqlWhereClausePartWith_IN_Statement_withOUT_DeclaredCheckedException(
        appendable: Appendable,
        columnName: String,
        values: java.util.List[String]
    ): Unit = {
      // no difference here in the Scala implementation, regarding try/catch/throws
      // (but please compare with the required difference in the Java implementation class SqlAppenderImplementedWithJava)
      addSqlWhereClausePartWith_IN_Statement_WITH_DeclaredCheckedException(appendable, columnName, values)
    }

 }

If you want to experiment with the above implementations, below is some client code you can use:

import static junit.framework.TestCase.*;
import org.junit.Test;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
General comment regarding the duplication in some of the methods in this test class:
In theory some of the methods (almost duplicated for StringBuilder and StringBuffer)
and might be easily refactored into a method taking an
Appendable parameter (with concrete instances assumed to be new StringBuilder/StringBuffer objects)
since toString would then be possible to use for the assertions.
However, such a metod would violate the Liskov substitution principle, since other
implementations of Appendable than StringBuilder/StringBuffer (e.g. FileWriter)
would not return the string in the toString method.
There are indeed other possible refactorings to reduce duplication,
e.g. in the two test methods for the FileWriter, but not without
some boilerplating verbose code, and the duplications in this JUnit test class is not a really
essential issue here, but the essential issue is to illustrate the different behaviours
regarding checked exceptions in Java compared to Scala
*/
public class SqlAppenderTest {

    @Test
    public void testSqlAppenders() {
        testAppender(new SqlAppenderImplementedWithJava());
        testAppender(new SqlAppenderImplementedWithScala());
    }

    private void testAppender(final SqlAppender sqlAppender) {
        testSqlAppenderWithStringBuffer_WithOUT_DeclaredException(sqlAppender);
        testSqlAppenderWithStringBuffer_WITH_DeclaredException(sqlAppender);

        testSqlAppenderWithStringBuilder_WithOUT_DeclaredException(sqlAppender);
        testSqlAppenderWithStringBuilder_WITH_DeclaredException(sqlAppender);

        testSqlAppenderWithFileWriter_WithOUT_DeclaredException(sqlAppender);
        testSqlAppenderWithFileWriter_WITH_DeclaredException(sqlAppender);        
    }

    private void testSqlAppenderWithStringBuffer_WithOUT_DeclaredException(final SqlAppender sqlAppender) {
        final StringBuffer stringBuffer = new StringBuffer();
        sqlAppender.addSqlWhereClausePartWith_IN_Statement_withOUT_DeclaredCheckedException(stringBuffer, COUNTRY, LIST_OF_COUNTRY_VALUES);
        assertWithExpectedSqlPart(stringBuffer.toString());
    }

    private void testSqlAppenderWithStringBuilder_WithOUT_DeclaredException(final SqlAppender sqlAppender) {
        final StringBuilder stringBuilder = new StringBuilder();
        sqlAppender.addSqlWhereClausePartWith_IN_Statement_withOUT_DeclaredCheckedException(stringBuilder, COUNTRY, LIST_OF_COUNTRY_VALUES);
        assertWithExpectedSqlPart(stringBuilder.toString());
    }    

    private void testSqlAppenderWithStringBuilder_WITH_DeclaredException(final SqlAppender sqlAppender) {
        final StringBuilder stringBuilder = new StringBuilder();
        try {
            sqlAppender.addSqlWhereClausePartWith_IN_Statement_WITH_DeclaredCheckedException(stringBuilder, COUNTRY, LIST_OF_COUNTRY_VALUES);
            assertWithExpectedSqlPart(stringBuilder.toString());

        } catch (IOException e) {
            fail("When StringBuilder was used, an IOException should not have been thrown");
        }
    }

    private void testSqlAppenderWithStringBuffer_WITH_DeclaredException(final SqlAppender sqlAppender) {
        final StringBuffer stringBuffer = new StringBuffer();
        try {
            sqlAppender.addSqlWhereClausePartWith_IN_Statement_WITH_DeclaredCheckedException(stringBuffer, COUNTRY, LIST_OF_COUNTRY_VALUES);
            assertWithExpectedSqlPart(stringBuffer.toString());

        } catch (IOException e) {
            fail("When StringBuffer was used, an IOException should not have been thrown");
        }
    }    

    private void testSqlAppenderWithFileWriter_WithOUT_DeclaredException(final SqlAppender sqlAppender) {
        final File tempFile = createTempFile();
        try {
            FileWriter fileWriter = null;
            try {
                fileWriter = new FileWriter(tempFile);
                fileWriter.close(); // to provoke the IOException when appending will be done
            } catch (IOException e) {
                e.printStackTrace();
            }
            assertNotNull(fileWriter);

            try {
                sqlAppender.addSqlWhereClausePartWith_IN_Statement_withOUT_DeclaredCheckedException(fileWriter, COUNTRY, LIST_OF_COUNTRY_VALUES);
                fail("We should have got some exception since the file was closed");
                //} catch (IOException e) {
                // the above IOException can not be catched here, i.e. not allowed by the compiler since the Java interface
                // does not declare the checked exception, even though the Scala implementation is able to throw it anyway
            } catch (RuntimeException e) { // not needed (i.e. not enforced y the compiler) to catch an exception here but since we expect an exception we do it  
                System.out.println("WithOUT DeclaredException , FileWriter Exception with implementing class: " + sqlAppender.getClass().getSimpleName() +  " , Exception class:" + e.getClass().getName());
                // OK, some exception was expected since the file was closed
            } catch (Exception e) { // will catch the IOException which is thrown by the Scala implementation
                System.out.println("WithOUT DeclaredException , FileWriter Exception with implementing class: " + sqlAppender.getClass().getSimpleName() +  " , Exception class:" + e.getClass().getName());
            }
        } finally {
            tempFile.delete();
        }
    }

    // the above and below test methods do indeed contain duplication, but that is not essential here (see javadoc comment as class level above)

    private void testSqlAppenderWithFileWriter_WITH_DeclaredException(final SqlAppender sqlAppender) {
        final File tempFile = createTempFile();
        try {
            FileWriter fileWriter = null;
            try {
                fileWriter = new FileWriter(tempFile);
                fileWriter.close(); // to provoke the IOException when appending will be done
            } catch (IOException e) {
                e.printStackTrace();
            }
            assertNotNull(fileWriter);

            try {
                sqlAppender.addSqlWhereClausePartWith_IN_Statement_WITH_DeclaredCheckedException(fileWriter, COUNTRY, LIST_OF_COUNTRY_VALUES);
                fail("We should have got some exception since the file was closed");
            } catch (IOException e) { // this catch is enforced by the compiler (alternatively to declare in test method signature)
                System.out.println("WITH    DeclaredException , FileWriter Exception with implementing class: " + sqlAppender.getClass().getSimpleName() +  " , Exception class:" + e.getClass().getName());
                // OK, some exception was expected since the file was closed
            }
        } finally {
            tempFile.delete();
        }
    }

    private File createTempFile() {
        try {
            File tempFile = File.createTempFile("testing", "testing");
            return tempFile;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static final String COUNTRY = "country";
    private static final String SWEDEN = "sweden";
    private static final String USA = "usa";
    private static final List LIST_OF_COUNTRY_VALUES = Arrays.asList(SWEDEN, USA);
    private final static String expectedSqlPartForTheTests = "country IN ('sweden','usa')";
    private void assertWithExpectedSqlPart(final String actualSqlPart) {
        assertEquals(expectedSqlPartForTheTests, actualSqlPart.trim());
    }
}

When the above class is executed, the following will be printed to the console:

WithOUT DeclaredException , FileWriter Exception with implementing class: SqlAppenderImplementedWithJava , Exception class:java.lang.RuntimeException
WITH    DeclaredException , FileWriter Exception with implementing class: SqlAppenderImplementedWithJava , Exception class:java.io.IOException
WithOUT DeclaredException , FileWriter Exception with implementing class: SqlAppenderImplementedWithScala , Exception class:java.io.IOException
WITH    DeclaredException , FileWriter Exception with implementing class: SqlAppenderImplementedWithScala , Exception class:java.io.IOException

The above things were printed to the console when the IOException occurred, and the conclusion you can see here by looking at that output (and by looking in the code) is that the Scala class did indeed throw an IOException in the method "SqlAppenderImplementedWithScala.addSqlWhereClausePartWith_IN_Statement_WITH_DeclaredCheckedException" without declaring it to be thrown (but it was declared in the Java interface method signature).

The other interesting thing you can see here is that the method "SqlAppenderImplementedWithScala.addSqlWhereClausePartWith_IN_Statement_withOUT_DeclaredCheckedException" could also throw an IOException through the Java interface, even though that checked exception was not declared to be allowed to be thrown in the Java interface. The Java implementation instead had to throw some unchecked exception (the RuntimeException above) to be able to compile the Java class.

/ Tomas Johansson, Stockholm, Sweden, 2010-01-21