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