Should Secondary Constructor Throw

Perhaps this should have been tagged as “Support”, but I think I’ve done my due diligence and this seems to be a Language Design issue.

I think secondary constructors should be able to throw, when try is called as part of the invocation of the primary constructor. As I understand it the following are true:

  1. Secondary constructors must call primary constructors.
  2. Primary constructors may evaluate expressions.
  3. try {} catch {} blocks are expressions.

Therefor I’d expect the following code to function:

class Foo(private val inputStream: inputStream) {
     constructor(path: String) : this(
          try {
               FileInputStream(path)
          } catch (e: FileNotFoundException) {
               throw BarException("Custom message", e)
          }
     )
}

Assuming the constructor on BarException is correct and no file is found at path (thereby causing the constructor for FileInputStream to throw).

What am I missing?

Secondary constructrors can throws expceptions.

import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.InputStream

class BarException(message: String, cause: FileNotFoundException) : RuntimeException(message, cause)

class Foo(private val inputStream: InputStream) {
   constructor(path: String) : this(
           try {
               FileInputStream(path)
           } catch (e: FileNotFoundException) {
               throw BarException("Custom message", e)
           }
   )
}

fun main(args: Array<String>) {
   Foo("/no/such/file")
}

Results:

Exception in thread "main" BarException: Custom message
	at Foo.<init>(sample.kt:12)
	at SampleKt.main(sample.kt:18)
Caused by: java.io.FileNotFoundException: /no/such/file (No such file or directory)
	at java.base/java.io.FileInputStream.open0(Native Method)
	at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
	at Foo.<init>(sample.kt:10)
	... 1 more

If I correctly understood your isssue everything works as you expected.

I appreciate the timely response. You’ve got it exactly as I presented it and I appreciate you coding it up. One difference is the custom exception I was attempting to throw was an Exception, not a RuntimeException. Updated my implementation to throw a runtime exception with no change. Another difference is that I’m calling the constructor from a test, not main; but I don’t imagine that should affect it.

What may affect it is that the constructor is part of an actual class. But if that’s the case I don’t understand how.

This code works for me, even if I change BarException to extend Exception instead.
What kind of error message do you get?

java.io.FileNotFoundException: err.or (No such file or directory)

	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at java.io.FileInputStream.<init>(FileInputStream.java:93)
	at com.my.fancy.pkg.platform.gloop.Foo.<init>(Foo.kt:19)
	at com.my.fancy.pkg.platform.glorp.BazKt.getCustomProperty(Baz.kt:109)
	at com.my.fancy.pkg.platform.glorp.BazTests.confirm custom property logic(BazTests.kt:117)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
	at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:256)
	at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:89)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

Works in this runnable example:

import java.io.*

class Foo(private val inputStream: InputStream) {
    constructor(path: String) : this(
        try {
            println("Throwing FileNotFoundException")
            throw FileNotFoundException() // Throwing manually just to test things.
            FileInputStream(path) // Doesn't work in Kotlin Playground for security reasons.
        } catch (e: FileNotFoundException) {
            println("Catching exception: $e")
            throw BarException("Custom message", e)
        }
    )
}

class BarException(val s: String, e: Throwable) : Exception(s)

fun main() {
    Foo("test")
}
1 Like