scala annotation argument needs to be constant but final val does not make it

In this snippet,

@SwaggerDefinition(...authorizationUrl = 
SecurityConstants.authorizationUrl)

and given that

final object SecurityConstants {
  final val authorizationUrl: String =..
}

(only one of the two finals should be ok, but still) I was expecting/hoping that this would compile with 2.12.3 but it is however giving "annotation argument needs to be a constant; found: SecurityConstants.authorizationUrl [error] authorizationUrl = SecurityConstants.authorizationUrl,"

Also calling Java static method System.getenv("a") in annotation brings the same error, that is

@SwaggerDefinition(...authorizationUrl = System.getenv("a"))

Please help, Nicu M


Solution 1:

final val references can be used as Java annotation arguments under two conditions:

  1. The value assigned to the final val must be a literal.
  2. You must not specify the type of your final val explicitly.

So, this will work:

final val AuthorizationUrl = "http://something.com"

but these will not work:

final val AuthorizationUrl = "http://something.com".trim
final val AuthorizationUrl: String = "http://something.com"

The condition no. 2 may seem especially weird but it's required for the compiler to internally retain the information that the final val is holding not just a String but some particular literal string value (the compiler must infer a literal type).

Solution 2:

It is really important what is behind .. in the

final val authorizationUrl: String =..

In Scala and Java annotation arguments must be compile-time constants rather than just "final". So System.getenv("a") (or any other function call) directly or via intermediate val will not do it as this is not a compile-time constant. This is done that way because annotations are designed to be introspectable at the compile time i.e. if you just have a .class file, you must be able to get the value of all the fields of the annotation. So the compiler must know all the values at the compile time.

Update

Actually Swagger itself relies on the compile-time introspection. So what exactly did you expect it to do with your System.getenv call? What specification Swagger should have generated for such annotation? And why do you need it at all? AFAIU if you have several environments, you should put URLs of the production (i.e. the one accessed by outside users) into the definition.