blob: 30d1b9a5cc9b7649f9db0cf173c863227421869f [file] [log] [blame]
:source-highlighter: pygments
:pygments-style: emacs
:icons: font
Groovy 5 builds upon existing features of earlier versions of Groovy.
In addition, it incorporates numerous new features and streamlines various legacy aspects of the Groovy codebase.
[width="80%",align="center"]
|===
a| NOTE: _WARNING:_
Material on this page is still under development!
We are currently working on alpha versions of Groovy 5.0 with a goal of gathering
feedback on the language changes from our community. In addition, early versions
assist other projects and tool vendors within the Groovy ecosystem to begin assessing
the impact of moving to/supporting Groovy 5.0. Caution should be exercised if using
new features as the details may change before final release. +
Some features described here as "incubating" may become stable before 5.0.0
final is released, others are expected to remain incubating for version 5.
We don’t recommend using alpha versions or incubating features for production systems.
We don't regard alpha versions as being feature-complete, so caution should be exercised
before undertaking any large scale ports to Groovy 5. Having said that, we don't
expect porting to Groovy 5 from Groovy 4 should involve much effort.
|===
[[Groovy5.0-new]]
== New features
=== Support for `var` with multi-assignment
The `var` keyword can be used in combination with multi-assignment:
[source,groovy]
----
var (x, y) = [1, 2]
assert x == 1 && y == 2
----
=== Extension method additions and improvements
There are some additional extension methods for `File` objects:
[source,groovy]
----
def myscript = new File('MyScript.groovy')
assert myscript // Groovy truth: true if the file exists
assert myscript.extension == 'groovy'
assert myscript.baseName == 'MyScript'
----
And similar methods for `Path` objects:
[source,groovy]
----
def mypic = path.resolve('MyFigure.png')
assert mypic // Groovy truth: true if the file exists
assert mypic.extension == 'png'
assert mypic.baseName == 'MyFigure'
----
There are additional variants of `collectEntries` for arrays, iterables and iterators
with separate functions for transforming the keys and values. There are variants
with and without collectors.
There are also variants which transform just the key or value.
The `withCollectedKeys` method collects key/value pairs for each item with the
item as the value and the key being the item transformed by the supplied function.
The `withCollectedValues` method collects key/value pairs for each item with the
item as the key and the value being the item transformed by the supplied function.
[source,groovy]
----
def languages = ['Groovy', 'Java', 'Kotlin', 'Scala']
def collector = [clojure:7]
assert languages.collectEntries(collector, String::toLowerCase, String::size) ==
[clojure:7, groovy:6, java:4, kotlin:6, scala:5]
assert languages.withCollectedKeys(s -> s.take(1)) ==
[G:'Groovy', J:'Java', K:'Kotlin', S:'Scala']
assert languages.withCollectedValues(s -> s.size()) ==
[Groovy:6, Java:4, Kotlin:6, Scala:5]
----
There are also equivalent variants for maps. The `collectEntries` method
takes separate functions for transforming the keys and values.
The `collectKeys` and `collectValues` variants take a single function
for transforming just the keys and values respectively.
[source,groovy]
----
def lengths = [Groovy:6, Java:4, Kotlin:6, Scala:5]
assert lengths.collectEntries(String::toLowerCase, { it ** 2 }) ==
[groovy:36, java:16, kotlin:36, scala:25]
assert lengths.collectKeys{ it[0] } == [G:6, J:4, K:6, S:5]
assert lengths.collectValues(Math.&pow.rcurry(2)) ==
[Groovy:36.0, Java:16.0, Kotlin:36.0, Scala:25.0]
assert lengths.collectValues(Math.&pow.curry(2).memoize()) ==
[Groovy:64.0, Java:16.0, Kotlin:64.0, Scala:32.0]
----
[[Groovy5.0-other]]
== Other improvements
[[Groovy5.0-ongoing]]
== Ongoing work
=== Enhanced switch (under investigation)
Groovy has always had a very powerful switch statement.
The statement could be made more powerful, e.g. support destructuring,
and could be supported in contexts where expressions are expected.
As inspiration, Java has made, or is investigating future enhancements
including switch expressions and other related enhancements:
link:https://openjdk.java.net/jeps/354[JEP 354: Switch Expressions (Second Preview)]
link:https://openjdk.java.net/jeps/361[JEP 361: Switch Expressions]
link:https://openjdk.java.net/jeps/405[JEP 405: Record Patterns & Array Patterns (Preview)]
link:https://openjdk.java.net/jeps/406[JEP 406: Pattern Matching for switch (Preview)]
We should investigate these proposals both in terms of enhancing the existing Groovy switch
but also in terms of deciding which syntax from Java we might like to support in the future.
Other languages like Python are also improving their switch statements:
https://www.python.org/dev/peps/pep-0622/[PEP 622 -- Structural Pattern Matching].
We should investigate whether any features of their design make sense for Groovy's dynamic nature.
As an example of destructuring, instead of the following existing code:
[source,groovy]
--------------------------------------
def make3D(pt) {
switch(pt) {
case Point3D:
return pt
case Point2D:
return new Point3D(pt.x, pt.y, 0)
case List:
def (x, y, z) = pt
if (x == 0 && y == 0 && z == 0)
throw new IllegalArgumentException("Origin not allowed")
return new Point3D(x, y, z)
...
}
}
--------------------------------------
You could use something like:
[source,groovy]
--------------------------------------
def make3D(pt) {
switch(pt) {
case Point3D:
return pt
case Point2D(x, y):
return new Point3D(x, y, 0)
case [0, 0, 0]:
throw new IllegalArgumentException("Origin not allowed")
case [x, y, z]:
return new Point3D(x, y, z)
...
}
}
--------------------------------------
An example of guarded patterns being considered for Java:
[source,java]
--------------------------------------
static void testTriangle(Shape s) {
switch (s) {
case null ->
System.out.println("Null!");
case Triangle t && (t.calculateArea() > 100) ->
System.out.println("Large triangle");
case Triangle t ->
System.out.println("Small triangle");
default ->
System.out.println("Non-triangle");
}
}
--------------------------------------
Another destructuring example:
[source,java]
--------------------------------------
int eval(Expr n) {
return switch(n) {
case IntExpr(int i) -> i;
case NegExpr(Expr n) -> -eval(n);
case AddExpr(Expr left, Expr right) -> eval(left) + eval(right);
case MulExpr(Expr left, Expr right) -> eval(left) * eval(right);
default -> throw new IllegalStateException();
};
}
--------------------------------------
We should consider the currently proposed nested record pattern when exploring our
destructuring options, e.g.:
[source,java]
--------------------------------------
static void printColorOfUpperLeftPoint(Rectangle r) {
if (r instanceof Rectangle(ColoredPoint(Point p, Color c), ColoredPoint lr)) {
System.out.println(c);
}
}
--------------------------------------
=== Other Java-inspired enhancements
* Module definitions written in Groovy (i.e. module-info.groovy)
link:https://issues.apache.org/jira/browse/GROOVY-9273[GROOVY-9273]
* Use of "_" (underscore) for unused parameters (see "Treatment of underscores" in https://openjdk.java.net/jeps/302[JEP 302: Lambda Leftovers])
[[Groovy5.0-breaking]]
== Other breaking changes
* Scripts containing a static `main` method and no statements outside that method have changed slightly
for improved JEP 445 compatibility. The script class for such methods no longer extends `Script` and
hence no longer has access to the script context or bindings. For many such scripts, access to the
binding isn't needed and there is now a simpler structure for those scripts. Scripts which need access
to the binding should instead use a no-arg instance `run` method.
(link:https://issues.apache.org/jira/browse/GROOVY-11118[GROOVY-11118])
* The `getProperty` method allows for getting properties that don't exist within a class.
Previously, static properties from an outer class were given priority over overrides
by `getProperty`. This is in conflict with the priority given to outer classes in other places.
(link:https://issues.apache.org/jira/browse/GROOVY-10985[GROOVY-10985])
* The minus operator for sets in Groovy was subject to an existing
https://bugs.openjdk.org/browse/JDK-6394757[JDK bug]
in the JDK's `AbstractSet#removeAll` method. The behavior now confirms
with the behavior of the fix being proposed for that bug.
If for some strange reason you rely on the buggy behavior, you can use
the `removeAll` method directly rather than the `minus` operator (at least until it is fixed in the JDK).
(link:https://issues.apache.org/jira/browse/GROOVY-10964[GROOVY-10964])
* Groovy 4 had a `$getLookup` method used to work around stricter JPMS access requirements.
Groovy no longer needs this hook. This method is not normally visible or of use to
typical Groovy users but if framework writers are making use of that hook,
they should rework their code.
(link:https://issues.apache.org/jira/browse/GROOVY-10931[GROOVY-10931])
* Groovy was incorrectly setting a null default value for annotations
without a default value. If framework writers have made use of,
or coded around the buggy behavior, they may need to rework their code.
It might mean simplification by removing a workaround.
(link:https://issues.apache.org/jira/browse/GROOVY-10862[GROOVY-10862])
* Some Groovy AST transform annotations, like `@ToString` were given
`RUNTIME` retention even though Groovy itself and typical Groovy user
behavior never needs access to that annotation at runtime. This was
done with a view that perhaps some future tools or framework might
be able to use that information in some useful way. We know of no such
frameworks or tools, so we have changed the retention to `SOURCE` to
give cleaner class files.
(link:https://issues.apache.org/jira/browse/GROOVY-10862[GROOVY-10862])
* Groovy's `%` operator is called the "remainder" operator. Informally,
it is also known as the "mod" operator and indeed, for operator overloading
purposes we have historically used the `mod` method. While this name is in
part just a convention, it can cause some confusion, since for example,
the `BigInteger` class has both `remainder` and `mod` methods and
our behavior, like Java's, follows the behavior of the `remainder` method.
In Groovy 5, operator overloading for `%` is now handled by the `remainder` method.
Fallback behavior is supported and workarounds exist for folks already using the `mod` method.
(link:https://issues.apache.org/jira/browse/GROOVY-10800[GROOVY-10800])
* Improvements have been made to better align how method selection
is performed between the dynamic Groovy runtime and with static compilation.
(link:https://issues.apache.org/jira/browse/GROOVY-8788[GROOVY-8788])
* Improvements have been made to accessing fields within Maps.
(link:https://issues.apache.org/jira/browse/GROOVY-6144[GROOVY-6144],
link:https://issues.apache.org/jira/browse/GROOVY-5001[GROOVY-5001])
[[Groovy5.0-requirements]]
== JDK requirements
Groovy 5 requires JDK16+ to build and JDK11 is the
minimum version of the JRE that we support.
Groovy 5 has been tested on JDK versions 11 through 20.
[[Groovy5.0-more-info]]
== More information
You can browse all the link:../changelogs/changelog-5.0.0-unreleased.html[tickets closed for Groovy 5.0 in JIRA].