While taking a look at this question, I noticed that applying @Throws
to a get
or set
use-site has no effect.
Additionally, the only valid targets for @Throws
are AnnotationTarget.FUNCTION
, AnnotationTarget.PROPERTY_GETTER
, AnnotationTarget.PROPERTY_SETTER
, and AnnotationTarget.CONSTRUCTOR
.
Other annotations, such as the JPA annotations and Deprecated
work fine and are properly applied to the method!
This is strange behavior.
To demonstrate, I created a simple abstract class in Java, with one constructor, one method and one get method.
public abstract class JavaAbstractClass {
@Deprecated
@NotNull public abstract String getString() throws IOException;
public abstract void setString(@NotNull String string) throws IOException;
public abstract void throwsFunction() throws IOException;
public JavaAbstractClass() throws IOException {
}
}
As you can see, every method/constructor is marked as throwing IOException
.
However, when I try to write an equivalent class in Kotlin, and mark the appropriate methods with throws
for interop, the generated getString
and setString
methods have no throws
clause.
abstract class KotlinAbstractClass @Throws(IOException::class) constructor() {
@get:Deprecated("Deprecated")
@get:Throws(IOException::class)
@set:Throws(IOException::class)
abstract var string: String
@Throws(IOException::class)
abstract fun throwsFunction()
}
The decompiled code:
@Metadata(Some metadata here)
public abstract class KotlinAbstractClass {
/** @deprecated */
@Deprecated(
message = "Deprecated"
) // @Deprecated made it through!
@NotNull
public abstract String getString(); // Nothing here!
public abstract void setString(@NotNull String var1); // Nothing here!
public abstract void throwsFunction() throws IOException;
public KotlinAbstractClass() throws IOException {
}
}
To me, it seems to be because these internal annotations must be handled specially by the compiler, instead of being applied directly to the method.
Additionally, applying it to the getter of a non-abstract property:
val string: String
@Throws(IOException::class) get() = "Foo"
does generate a method with the signature public final String getString() throws IOException
!
Perhaps this case wasn't handled properly?
Is this a bug?
Note: This doesn't have anything to do with whether the method actually throws this exception.
If I do:
@get:Throws(IOException::class)
val string: String
get() = BufferedReader(FileReader("file.txt")).readText()
The compiled code is still
@NotNull
public final String getString() {
return TextStreamsKt.readText((Reader)(new BufferedReader((Reader)(new FileReader("file.txt")))));
}
despite the fact that the FileReader
constructor throws a FileNotFoundException
.
Additionally, this shouldn't matter for abstract methods anyway, as they can't have an implementation and still can have a throws
clause.
If I do as @tynn suggests and add a concrete implementation:
class ConcreteClass : KotlinAbstractClass() {
override val string: String
get() = BufferedReader(FileReader("file.txt")).readText()
...
}
I still get the same result.