Summary
Allowing static references to class members could greatly increase Java's type-safe expressive capability.
Advertisement
I recently ran across First-class
methods: Java-style closures by Stephen Colebourne and Stefan
Schulz. The second section in particular, "Member Literals", caught
my eye as suggesting something which I have been wishing Java had for
awhile. Since Java 1.0, we've been able to
name classes in our source code, e.g. String.class. Of
course, we can also say
Class.forName("java.lang.String"), but this is both less
elegant and more error prone (and hence also requires coding for
ClassNotFoundExceptions). Once we have a class, however,
we can go no further without abandoning static typing; to refer to a
class's members requires reflection.
It doesn't have to be this way. Already, whenever a class directly
accesses a member of another class, that member reference is stored in the
referring class's constant
pool. Allowing member literals in Java code would greatly
add to Java's ability to be expressive in a type-safe manner,
especially with annotations. Colebourne and Schulz propose using
Javadoc-style syntax to accomplish just this.
For example,
consider the GuardedBy
annotation from Brian Goetz's Java Concurrency In Practice.
This annotation is meant to be placed on a field to indicate what lock
must be acquired before accessing the field. The annotation takes a
single string value with documented semantics.
@GuardedBy("mutex") implies that a lock should be
acquired on the mutex field,
@GuardedBy(Foo.bar) implies that a lock should be
acquired on the static field bar in the class
Foo, and so forth. Thus in the following code:
public class Worker { private Object runningMutex = new Object();
the @GuardedBy annotation expresses the intent that all
accesses to running should take place while holding a
synchronization lock on runningMutex. Unfortunately, if
there were a typo, say the value passed to @GuardedBy was
"runnningMutex", the compiler would not catch this. If
the @GuardedBy
annotation was just meant to be used by a tool like FindBugs, the
problem will be caught soon enough. However, if one was hoping to use
some runtime byte code manipulation to enforce the access policy, a
runtime error would occur.
Using Colebourne and Schulz's "Member Literals" proposal, the above
code could be rewritten as:
public class Worker { private Object runningMutex = new Object();
@Override public boolean equals(Object obj) { return equalsHelper.equals(this, obj);
}
While member literals do not dramatically increase the expressiveness
of Java over the current methods of just using strings, they do
dramatically increase the type safety of those expressions. Method
and field constants would be readily understood by refactoring tools
and the like. Similarly, IDEs would be able to provide good code
completion support for them.
> Really? According to javap, String.class > translates to:
IIRC, older versions of Java did insert the call to Class.forName(String).
This is a pretty good idea, I think. The more I think about it, the more I feel this does address a particular clumsiness of Java. It would eliminate a lot of boilerplate without the ugliness and verbosity of (Java) reflection.
This is something I've wanted for a long time too, though I'm not sure I like your syntax. I'd prefer to keep . separators for readability, if a special deliminator is needed I think I'd prepend the special character:
According to Google, there is no EqualsHelper.create(). Nor is there a non-static myEqualsHelper.equals() method (in the Hibernate EqualsHelper, the only one I could find), only a static method. So I have no idea just what this proposal proposes.
Also, a question on annotations, Does GuardedBy mean that access should be guarded, or can it somehow enforce that access actually is guarded??? If the former, adding a language feature just to avoid typos in what is effectively a comment, seems a bit extreme. And there are far more good instances of bad comments to tackle. :-)
> According to Google, there is no EqualsHelper.create(). > Nor is there a non-static myEqualsHelper.equals() method > d (in the Hibernate EqualsHelper, the only one I could > find), only a static method. So I have no idea just what > this proposal proposes.
I think some example code for EqualsHelper would be a good idea but I think I have a grasp on what this feature does.
The idea is basically a way to do typesafe reflection in Java. It would almost be like have a method pointer or pointer to a field. The equals helper would take the input fields and create an instance that would take two instances of Person and look up the fields in those Person instance to compare them.
You could build this right now using the reflection APIs. The difference would be that the compiler could check that the named fields are actually members of the Person class and of the proper types for the expressions they are used in.
Thanks James. Don't forget that EqualsHelper must also do hashCode!!!
Catching it in the compiler is nice, but if any of your unit test code, or manual testing, ever calls equals that will catch the typo. I'm far from a TDD fanatic, but this "problem" strikes me as unlikely to happen given any amount of reasonable testing.
> Thanks James. Don't forget that EqualsHelper must also do > hashCode!!! > > Catching it in the compiler is nice, but if any of your > unit test code, or manual testing, ever calls equals that > will catch the typo. I'm far from a TDD fanatic, but this > "problem" strikes me as unlikely to happen given any > amount of reasonable testing.
Sure but you get all the dependency information and the refactoring etc. etc.
Personally, I like this idea because there are things that I could do with this that I can't bring myself to do with reflection. It's just too hard to read and for that reason, in combination with the lack of typechecking makes it unmaintainable.
> Also, a question on annotations, Does GuardedBy mean that > access should be guarded, or can it somehow enforce > that access actually is guarded??? If the former, > adding a language feature just to avoid typos in what is > effectively a comment, seems a bit extreme. And there are > far more good instances of bad comments to tackle. :-)
It means the former. However, tools such as FindBugs can work with the annotation to do static code analysis to see if the annotations claim appears to be true. Of course, if there is a typo, FindBugs will likely catch it, but a type-safe mechanism will also play nicely with refactoring, code completion and the like.
This is an excellent idea. I have another use-case. We have a home-built O/R framework. One of the APIs allows specifying columns for the WHERE clause of the query. Currently, it takes a String name. But, a member literal would be much better and much safer.
> Umm, As per Java Language Specification v2, Annotations > *cannot* do any of the following: > > 1) Cause an otherwise correct and valid code to not > compile.
Doesn't the @Override annotation violate that rule?