I discovered that Java 8 has a language feature I'd never heard of before today!
Explicit receiver parameters
Consider a simple method in Java 7:
public class Currency { public String getCode() { ... } }
In Java 8, it turns out there is a second way to write the method:
public class Currency { public String getCode(Currency this) { ... } }
The same trick also works for methods with arguments:
public class Currency { public int compareTo(Currency this, Currency other) { ... } }
So, what is going on here?
The relevant part of the Java Language Specification is here.
The receiver parameter is an optional syntactic device for an instance method or an inner class's constructor. For an instance method, the receiver parameter represents the object for which the method is invoked. For an inner class's constructor, the receiver parameter represents the immediately enclosing instance of the newly constructed object. Either way, the receiver parameter exists solely to allow the type of the represented object to be denoted in source code, so that the type may be annotated. The receiver parameter is not a formal parameter; more precisely, it is not a declaration of any kind of variable, it is never bound to any value passed as an argument in a method invocation expression or qualified class instance creation expression, and it has no effect whatsoever at run time.
The new feature is entirely optional, and exists to allow the type to be annotated at the point it is used:
public class Currency { public int compareTo(@AnnotatedUsage Currency this, Currency other) { ... } } @Target(ElementType.TYPE_USE) public @interface AnnotatedUsage {}
The annotation is made available in reflection using Method::getAnnotatedReceiverType().
Looking to the future
I discovered this feature in the context of Project Valhalla, the effort to add value types to Java.
One possible syntax being considered to allow different methods on List<int>
from List<String>
is to use receiver types:
public class List<any T> { public int sum(List<int> this) { ... } }
This syntax would be used to define a sum()
method that only applies when List
is parameterized by int
, and not when parameterized by anything else, such as String
.
For more information, see this email to the Valhalla experts mailing list. But please note that the list is read-only, cannot be signed up to and is for pre-selected experts only. In addition, everything discussed there is very, very early in the process, and liable to change greatly before being released in a real version of Java (such as Java 10).
Summary
Java 8 has a new language feature which you've probably never heard of and will probably never use. But at least you now have some knowledge that you can use to prove you are a Java Guru!
Very strange feature. Thanks for the author
ReplyDeleteVery nice. Value classes have been in scala for a while. Nice to see jvm support. Also in scala you can use implicit parameters to specialize functions in this way: def sum[B >: A](implicit num: Numeric[B]): B
ReplyDeleteNice find. I checked some static analysis tools (Checkstyle, PMD, SonarQube) and they don't support it yet. Eclipse does support it though.
ReplyDeleteAlso, it's not mentioned in the Java 8 release notes:
http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
These annotations... They're really the envy of a whole industry. :)
ReplyDeleteI discovered this feature in the context of Project Valhalla, the effort to add value types to Java. One possible syntax being considered to allow different methods on List from List is to use receiver types:
ReplyDeleteDid you mean List or is there some other magic that makes primitives usable in generics?
FYI, in case it's useful, I have a small project for offline (classes don't require loading) annotation indexing and reflection. Since 2.0, which has been out for some time, It supports the usage of type annotations, including receiver annotations.
ReplyDeletehttps://github.com/wildfly/jandex
I wonder if type variance at methods may also be implemented with this feature.
ReplyDeleteCollection<E> {
Stream stream(Collection<? extends E> this);
...
}