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.
I believe @tynn suggests you do the following:
override val string: String
@Throws(FileNotFoundException::class) get() = BufferedReader(FileReader("file.txt")).readText()
This should give you the proper Java version with throws
in the signature. I guess the reasoning is that if you just do this:
@get:Throws(IOException::class)
val foo: String = "foo"
the compiler is smart enough to see that there's nothing in the getter that would throw an IOException
, since you've never overridden it, so it won't produce the throws
section. When the getter is overridden, the compiler has no way to know if the code you've supplied can throw, so it obeys the annotation and will always output the throws
part.
UPDATE
The following seems to generate correct bytecode:
abstract class KotlinAbstractClass {
abstract var string: String
@Throws(IOException::class) get
@Throws(IOException::class) set
}
After taking a closer look at it, I see no reason for @get:Throws(IOException::class)
not to work in this case. You might file an issue on the YouTrack for Kotlin and see what the team members have to say about it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With