Programming language: Apache Groovy 4 adopts Java innovations


Version 4 of the programming language was released two years after Apache Groovy 3.0. The fresh major release of the JVM (Java Virtual Machine) language introduces some of the Java language features of recent years, albeit with slight variations. It also gets its own query language with GINQ and relies on design by contract.

Beyond the linguistic innovations, the release says goodbye to the legacy packages that existed as duplicates of the packages newly introduced in Groovy 3 for interaction with the Java Platform Module System (JPMS). The coexistence should ensure a smooth transition to the new conventions. Details on the old and new names can be found in the release notes for version 3.0.

Java introduced switch expressions in version 13 as a supplement to switch statements and stabilized them in Java 14. The variant of the language construct that has now been implemented in Groovy is based on the Java procedure. While switch statements for each case case with a : and subsequent code, switch expressions can, among other things, directly assign a value to a variable. After case usually stands in each case ->. Many typical switch-Blocks are now clearer:

// Switch Statement
def result
switch(i) 
  case 0: result = 'Null'; break
  case 1: result = 'Eins'; break
  case 2: result = 'Zwei'; break
  default: throw new IllegalStateException('unbekannt')


// Switch Expression
def result = switch(i) 
    case 0 -> 'Null'
    case 1 -> 'Eins'
    case 2 -> 'Zwei'
    default -> throw new IllegalStateException('unbekannt')


Behind -> must be exactly a single expression. However, multiple statements can be combined in a block enclosed in curly brackets. It is also possible to program switch expressions in the old colon notation. Every case has to have one yield included to assign the value to the variable. Mixing the two forms with : and -> is not allowed.

In contrast to Java, the current implementation does not have to cover all cases. If no case-Row reflects the value and no default exists, Groovy implicitly adds null added as a default. However, there are probably already strict requirements for mandatory coverage of all cases and the avoidance of null in planning.

Sealed classes have existed in the Java world since version 15 and have been considered stable since Java 17. Groovy also now allows classes, interfaces, and traits to be sealed to specify which types are eligible as children. Sealing is done using the keyword sealed or the annotation @Sealed. The permitted child types can be specified explicitly permits respectively permittedSubclasses determine. In addition, the compiler implicitly allows the subtypes defined in the same file.

In interaction with Java from the current version 17, Groovy creates the bytecode as sealed classes that are compatible with those in Java. For older Java variants, the Groovy compiler recognizes the sealed types, but the Java compiler treats them as regular ones. In Groovy 4.0, sealed classes are marked as incubating. This means that changes to the concrete implementation are potentially possible.

Records made their debut in Java 14 and have been considered stable since Java 16. They serve as an object-oriented construct for storing simple values ​​in the form of immutable data, i.e. unchangeable data. Groovy has known about annotation for quite some time @Immutable for immutable content.

With the current release, Groovy leads the keyword record and the annotation @RecordType which are implemented as native records in interaction with Java from version 16. As with the sealed types, an emulated implementation applies to earlier Java variants, which behave in the same way, but which the Java compiler does not recognize as records.

Another innovation marked as incubating is the query language Groovy-Integrated Query (GINQ). It can be used to create queries on collections in a similar way to SQL, as the following code example from the release notes shows:

from p in persons
leftjoin c in cities on p.city.name == c.name
where c.name == 'Shanghai'
select p.name, c.name as cityName

from p in persons
groupby p.gender
having p.gender == 'Male'
select p.gender, max(p.age)

from p in persons
orderby p.age in desc, p.name
select p.name

from n in numbers
where n > 0 && n <= 3
select n * 2

from n1 in nums1
innerjoin n2 in nums2 on n1 == n2
select n1 + 1, n2

The Groovy documentation shows further examples in addition to the description of GINQ.

The module is also new and incubating groovy.contracts, which introduces design-by-contract (DbC) programming. The procedure comes from the Eiffel programming language and is based on a type of contract for using the interfaces that are intended to ensure the interaction of the individual program modules. For C++, contracts were actually planned for C++20, but it is now questionable whether they will at least make it into the upcoming C++23.

The contracts in the DbC specify the pre-conditions and the post-conditions. The invoker must observe the former and the invoked the latter. As the third building block, the invariants describe conditions that must always apply when executing. Groovy provides its own annotations for the three elements. An implementation can be found in the following code example from the release notes:

import groovy.contracts.*

@Invariant( speed() >= 0 )
class Rocket 
    int speed = 0
    boolean started = true

    @Requires( isStarted() )
    @Ensures( old.speed < speed )
    def accelerate(inc)  speed += inc 

    def isStarted()  started 

    def speed()  speed 


def r = new Rocket()
r.accelerate(5)

An external project for design by contract, GContracts, has existed for Groovy for a long time, which is not being further developed and is archived.

Further innovations in Groovy 4 such as the JavaShell and POJO annotations (Plain Old Java Object) for a leaner implementation of static Groovy objects can be found in the release notes. The download page refers to the source code and binaries for different operating systems.


(rm)

To home page



Source link -64