Fwd: Extending 15.28 to include toString() on an enum constant
Brian Goetz
brian.goetz at oracle.com
Mon Jul 9 12:55:39 UTC 2018
The following was received on the -comments list.
Here's my thoughts on this.
This is a prime example of the sort of language feature that could be
described as "opening a fresh can of worms." In addition to increasing
the complexity of the language by adding a narrow and esoteric
irregularity (special treatment for toString()), it would surely be the
case that tomorrow, there would be a long line of similar irregularities
queueing up to demand equal treatment.
But, the problems you cite are real concerns, so let's talk about those,
rather than the specific solution. The first observes how non-logging
of constants is less expensive than non-logging of non-constants. As an
example, you cite this change:
- logger.info("Datacenter is: " + DataCenterInfo.Name.Amazon);
+ logger.info("Datacenter is: {}", DataCenterInfo.Name.Amazon);
There are other common ways to handle this in logging frameworks. One is
to pass a lambda instead of arguments; another is to unroll the constant
yourself:
+ logger.info("Datacenter is: AMAZON");
Logging is a bit of a special case, since in few other APIs do you
regularly pass expressions that most of the time you wish would not
actually be evaluated. And I would suggest that this sort of
almost-but-not-quite-foldable string concatenation is a relatively rare
case. So I'm not really seeing this as a big motivation for an
irregular change.
That said, there are some other operations that are far more deserving
of better compile-time treatment. In the context of raw string
literals, we expect the `align()` call to be common; given that
`align()` is pure, evaluating it at compile time rather than run time
will be a big win. You should expect to see this sort of thing coming
out of JEP 303. (Though I doubt it would go as far as `toString()` on
enums, because at compile time, we cannot prove that _at run time_
`toString()` would not have been overridden. Constant-folding of static
methods is far less problematic than for virtual methods.)
You raised a second case: constants in annotations. This is very
different in character from the first case. The first case is a pure
quest-for-performance; hoping that the compile/runtime can optimize
something better. The second is looking for language changes to make
annotations more expressive; extending the notion of "constant
expression" so more expressions can be used as annotation parameters.
We've actually thought about this quite a bit in the context of JEP 303.
My conclusion is: this would be a bad idea. It's great if the compiler
can opportunistically spot opportunities to eliminate computation, but
tying the semantics of a high-level language construct to this is pretty
dangerous. It means that in order to correctly reason about what can go
in an annotation, every developer must be able to correctly reason about
all the optimizations a compiler might do -- which is clearly a recipe
for frustration and confusion.
Additionally, I thing the resulting language is not one we'll want to
program in. When you pull this string to the end, you will end up with
complex mini-programs as parameters to annotations. But annotations are
for metadata, not carriers for mini-programs.
(We also confronted this from another angle; it has recently become
practical to allow putting (non-bound) method references in annotations,
which is a reasonable fit for the role of annotations. But the obvious
next question is: what about lambdas? And while the machinery is the
same, this is almost certainly over the line -- do we really want
annotations stuffed full with little mini-method bodies (or, not so mini)?)
-------- Forwarded Message --------
Subject: Extending 15.28 to include toString() on an enum constant
Date: Sun, 8 Jul 2018 20:43:00 -0400
From: Daniel Trebbien <dtrebbien at gmail.com>
To: amber-spec-comments at openjdk.java.net
One small idea I have for enhancing the Java language is to allow
toString() on enum constants to be used in constant expressions, as long as
toString()
<https://docs.oracle.com/javase/10/docs/api/java/lang/Enum.html#toString()>
is not overidden, or if it is, the implementation returns a constant
expression.
There are two use cases I have in mind for this:
1. When using the SLF4J logging library, if you want to embed the name of
an enum constant in the log message, you should use parameterized logging
to avoid incurring a wasted runtime string concatenation if logging at the
specified level is disabled. See SLF4J's FAQ for "What is the fastest way
of (not) logging?": https://www.slf4j.org/faq.html#logging_performance
As an example of this in the wild, I submitted a pull request in which I
converted a log statement involving an enum constant to parameterized
logging:
https://github.com/Netflix/eureka/commit/ad81a1140f351ec19165cb94d5aa668cfc08a932
There are other cases where an SLF4J logger call involving an enum constant
correctly used parameterized logging, but I am not able to find an example
right now.
Although using parameterized logging solves the performance issue, it is
desirable to keep down the number of parameters to an SLF4J logger call
because the SLF4J Logger interface
<https://www.slf4j.org/apidocs/org/slf4j/Logger.html> has more efficient
variants of the trace(), debug(), info(), warn(), and error() calls that
take zero, exactly one, and exactly two parameters. If a logger call
passes at most two parameters, then the construction of an Object array to
package the parameters into varargs is avoided. Needing to "waste" a
parameter slot on an enum constant is undesirable.
2. When using Spring Security, it is possible to define an enum that lists
the roles and other categories of permissions that are used by a Spring
application. For example:
package com.myapp.security;
import org.springframework.security.core.GrantedAuthority;
public enum AppAuthority implements GrantedAuthority {
ROLE_USER,
ROLE_ADMINISTRATOR;
@Override
public String getAuthority() {
return toString();
}
}
When using the org.springframework.security.access.annotation.Secured
<https://docs.spring.io/spring-security/site/docs/5.0.6.RELEASE/api/org/springframework/security/access/annotation/Secured.html>
annotation to secure controllers or actions, it would be nice to be able to
use an enum constant in the annotation's value, as in @Secured({
ROLE_ADMINISTRATOR.toString() }); however, this is currently not valid Java
code because ROLE_ADMINISTRATOR.toString() is not a constant expression.
Instead, the name of the role must be duplicated: @Secured({ "
ROLE_ADMINISTRATER" }). This is undesirable because it is error-prone and
makes refactoring more difficult. It is also more difficult to find usages
of a particular authority. If toString() on an enum constant were allowed
within a constant expression, then finding usages of a particular authority
would be a "Find Usages" operation in an IDE.
More information about the amber-spec-observers
mailing list