@Throws has no effect when the target is a property

你离开我真会死。 提交于 2021-02-08 23:44:10

问题


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.


回答1:


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.



来源:https://stackoverflow.com/questions/47737623/throws-has-no-effect-when-the-target-is-a-property

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!