blob: 0b0704787a9ee5c28ca342b7560f95e5bb005226 [file] [log] [blame]
//////////////////////////////////////////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
//////////////////////////////////////////
ifndef::core-metaprogramming[]
:core-metaprogramming: core-metaprogramming.adoc
endif::[]
ifndef::guide-integrating[]
:guide-integrating: guide-integrating.adoc
endif::[]
= Program structure
This chapter covers the program structure of the Groovy programming language.
== Package names
Package names play exactly the same role as in Java. They allow us to separate the code base without any conflicts. Groovy classes must specify their package before the class definition, else the default package is assumed.
Defining a package is very similar to Java:
[source,groovy]
----
include::../test/PackageTest.groovy[tags=package_statement,indent=0]
----
To refer to some class `Foo` in the `com.yoursite.com` package you will need to use the fully qualified name `com.yoursite.com.Foo`, or else you can use an `import` statement as we'll see below.
== Imports
In order to refer to any class you need a qualified reference to its package. Groovy follows Java's notion of allowing `import` statement to resolve class references.
For example, Groovy provides several builder classes, such as `MarkupBuilder`. `MarkupBuilder` is inside the package `groovy.xml` so in order to use this class, you need to `import` it as shown:
[source,groovy]
----
include::../test/PackageTest.groovy[tags=import_statement,indent=0]
----
=== Default imports
Default imports are the imports that Groovy language provides by default. For example look at the following code:
[source,groovy]
----
include::../test/PackageTest.groovy[tags=default_import,indent=0]
----
The same code in Java needs an import statement to `Date` class like this: ++import java.util.Date++. Groovy by default imports these classes for you.
The below imports are added by groovy for you:
[source,groovy]
----
import java.lang.*
import java.util.*
import java.io.*
import java.net.*
import java.time.*
import groovy.lang.*
import groovy.util.*
import java.math.BigInteger
import java.math.BigDecimal
----
This is done because the classes from these packages are most commonly used. By importing these boilerplate code is reduced.
=== Simple import
A simple import is an import statement where you fully define the class name along with the package. For example the import statement `import groovy.xml.MarkupBuilder` in the code below is a simple import which directly refers to a class inside a package.
[source,groovy]
----
include::../test/PackageTest.groovy[tags=import_statement,indent=0]
----
=== Star import
Groovy, like Java, provides a special way to import all classes from a package using `*`, the so-called on-demand or star import. `MarkupBuilder` is a class which is in package `groovy.xml`, alongside another class called `StreamingMarkupBuilder`. In case you need to use both classes, you can do:
[source,groovy]
----
include::../test/PackageTest.groovy[tags=multiple_import,indent=0]
----
That's perfectly valid code. But with a `*` import, we can achieve the same effect with just one line. The star imports all the classes under package `groovy.xml`:
[source,groovy]
----
include::../test/PackageTest.groovy[tags=star_import,indent=0]
----
One problem with `*` imports is that they can clutter your local namespace. But with the kinds of aliasing provided by Groovy, this can be solved easily.
=== Static import
Groovy's static import capability allows you to reference imported classes as if they were static methods in your own class:
[source,groovy]
----
include::../test/PackageTest.groovy[tags=static_imports,indent=0]
----
This is similar to Java's static import capability but is a more dynamic than Java in that it allows you to define methods with the same name as an imported method as long as you have different types:
[source,groovy]
----
include::../test/PackageTest.groovy[tags=static_import_same_method_name_different_parameter_type,indent=0]
----
<1> static import of method
<2> declaration of method with same name as method statically imported above, but with a different parameter type
<3> compile error in Java, but is valid Groovy code
If you have the same signature, the imported method takes precedence.
=== Static import aliasing
Static imports with the `as` keyword provide an elegant solution to namespace problems. Suppose you want to get a `Calendar` instance, using its `getInstance()` method. It's a static method, so we can use a static import. But instead of calling `getInstance()` every time, which can be misleading when separated from its class name, we can import it with an alias, to increase code readability:
[source,groovy]
----
include::../test/PackageTest.groovy[tags=static_importswithas,indent=0]
----
Now, that's clean!
=== Static star import
A static star import is very similar to the regular star import. It will import all the static members from the given class.
For example, let's say we need to calculate sines and cosines for our application. The class `java.lang.Math` has static methods named `sin` and `cos` which fit our need. With the help of a static star import, we can do:
[source,groovy]
----
include::../test/PackageTest.groovy[tags=static_importswithstar,indent=0]
----
As you can see, we were able to access the methods `sin` and `cos` directly, without the `Math.` prefix.
=== Import aliasing
With type aliasing, we can refer to a fully qualified class name using a name of our choice. This can be done with the `as` keyword, as before.
For example we can import `java.sql.Date` as `SQLDate` and use it in the same file as `java.util.Date` without having to use the fully qualified name of either class:
[source,groovy]
----
include::../test/PackageTest.groovy[tags=alias_import,indent=0]
----
=== Namespace conflicts
Similar to Java, it is an error in Groovy to specify multiple imports with the same name but different types:
[source,groovy]
----
import java.awt.List
import java.util.List // error: name already declared
----
And to declare an import and a top-level type with the same name:
[source,groovy]
----
import java.util.List
class List { } // error: name already declared
----
However, inner types can shadow names from the unit scope:
[source,groovy]
----
import java.util.List
class Main {
class List { } // allowed; "List" refers to this type within `Main`'s scope and `java.util.List` elsewhere
}
----
== Scripts versus classes
Groovy supports both scripts and classes. From Groovy 5,
Groovy also supports https://openjdk.org/jeps/445[JEP 445] compatible scripts.
=== Motivation for scripts
Take the following code for example:
[source,groovy]
.Main.groovy
----
include::../test/ScriptsAndClassesSpecTest.groovy[tags=groovy_class_with_main_method,indent=0]
----
<1> define a `Main` class, the name is arbitrary
<2> the `public static void main(String[])` method is usable as the main method of the class
<3> the main body of the method
This is typical code that you would find coming from Java, where code *has* to be embedded into a class to be executable.
Groovy makes it easier, the following code is equivalent:
[source,groovy]
.Main.groovy
----
include::../test/ScriptsAndClassesSpecTest.groovy[tags=groovy_script,indent=0]
----
A script can be considered as a class without needing to explicitly declare it.
There are some differences which we'll cover next. First, we'll cover Groovy's
main `Script` class. Then, we'll cover JEP 445 compatible classes.
=== `Script` class
A gapi:groovy.lang.Script[script] is always compiled into a class. The Groovy compiler will compile the class for you,
with the body of the script copied into a `run` method. The previous example is therefore compiled as if it was the
following:
[source,groovy]
.Main.groovy
----
include::../test/ScriptsAndClassesSpecTest.groovy[tags=groovy_script_equiv,indent=0]
----
<1> The `Main` class extends the `groovy.lang.Script` class
<2> `groovy.lang.Script` requires a `run` method returning a value
<3> the script body goes into the `run` method
<4> the `main` method is automatically generated
<5> and delegates the execution of the script on the `run` method
If the script is in a file, then the base name of the file is used to determine the name of the generated script class.
In this example, if the name of the file is `Main.groovy`, then the script class is going to be `Main`.
=== Methods
It is possible to define methods into a script, as illustrated here:
[source,groovy]
----
include::../test/ScriptsAndClassesSpecTest.groovy[tags=method_in_script,indent=0]
----
You can also mix methods and code. The generated script class will carry all methods into the script class, and
assemble all script bodies into the `run` method:
[source,groovy]
----
include::../test/ScriptsAndClassesSpecTest.groovy[tags=multiple_methods_assembly,indent=0]
----
<1> script begins
<2> a method is defined within the script body
<3> and script continues
Statements 1 and 3 are sometimes referred to as "loose" statements.
They are not contained within an explicit enclosing method or class.
Loose statements are assembled sequentially into the `run` method.
So, the above code is internally converted into:
[source,groovy]
----
include::../test/ScriptsAndClassesSpecTest.groovy[tags=multiple_methods_assembly_equiv,indent=0]
----
<1> the `power` method is copied as-is into the generated script class
<2> first statement is copied into the `run` method
<3> second statement is copied into the `run` method
TIP: Even if Groovy creates a class from your script, it is totally transparent for the user. In particular, scripts
are compiled to bytecode, and line numbers are preserved. This implies that if an exception is thrown in a script,
the stack trace will show line numbers corresponding to the original script, not the generated code that we have shown.
=== Variables
Variables in a script do not require a type definition. This means that this script:
[source,groovy]
----
include::../test/ScriptsAndClassesSpecTest.groovy[tags=script_with_variables,indent=0]
----
will behave the same as:
[source,groovy]
----
include::../test/ScriptsAndClassesSpecTest.groovy[tags=script_with_untyped_variables,indent=0]
----
However, there is a semantic difference between the two:
* if the variable is declared as in the first example, it is a _local variable_. It will be declared in the `run`
method that the compiler will generate and will *not* be visible outside of the script main body. In particular, such
a variable will *not* be visible in other methods of the script
* if the variable is undeclared, it goes into the gapi:groovy.lang.Script#getBinding()[script binding]. The binding is
visible from the methods, and is especially important if you use a script to interact with an application and need to
share data between the script and the application. Readers might refer to the
<<{guide-integrating}#_integrating_groovy_in_a_java_application,integration guide>> for more information.
TIP: Another approach to making a variable visible to all methods, is to use the
<<{core-metaprogramming}#xform-Field,@Field annotation>>.
A variable annotated this way will become a field of the generated script class and,
as for local variables, access won't involve the script `Binding`.
If you have a local variable or script field with the same name as a binding variable,
we recommend renaming one of them to avoid potential confusion. If that's not possible,
you can use `binding.varName` to access the binding variable.
=== Convenience variations
As mentioned previously, normally, `public static void main` and `run` methods
are automatically added to your script, so it is normally illegal to add your own versions
of either of those; you would see a duplicate method compiler error if you tried.
However, there are some exceptions where the above rules don't apply.
If your script contains _only_ a compatible main method and no other loose statements,
or _only_ a no-arg `run` instance method (from Groovy 5), then it is allowed.
In this case, no loose statements (because there aren't any) are collected into the `run` method.
The method you supplied is used instead of Groovy adding the respective method(s).
This can be useful if you need to add an annotation to the otherwise implicitly added
`main` or `run` methods as this example shows:
[source,groovy]
----
include::../test/ScriptsAndClassesSpecTest.groovy[tags=script_with_explicit_static_main,indent=0]
----
To be recognised as a convenience variation, as well as having no loose statements,
the parameter for the `main` method should be:
* untyped as above (`Object` type),
* or of type `String[]`,
* or have no arguments (from Groovy 5).
From Groovy 5, a no-arg instance `run` variant is also supported.
This also allows annotations to be added.
The `run` variant follows the JEP 445 rules for field declarations
(hence doesn't need to use the `@Field` annotation)
as this example involving Jackson JSON serialization shows:
[source,groovy]
----
include::../test/ScriptsAndClassesSpecTest.groovy[tags=script_with_explicit_instance_run,indent=0]
----
The `run` variant is recommended if you need your script to extend the `Script` class
and have access to the script context and bindings. If you don't have that requirement,
providing one of the `main` variants will create a JEP 445 compatible class which won't
extend `Script`. We'll cover JEP 445 compatible scripts in more detail next.
== JEP 445 compatible scripts
From Groovy 5, support has been added for JEP 445 compatible scripts containing
a `main` method. Such scripts have several differences to normal Groovy `Script` classes:
* they won't have a `public static void main` method added
* they won't extend the `Script` class and hence won't have access to the script
context or binding variables
* allows additional class-level _fields_ and _methods_ to be defined in addition to `main`
* can't have "loose" statements outside the `main` method (excluding any field definitions)
A simple example might look like:
[source,groovy]
----
include::../test/ScriptsAndClassesSpecTest.groovy[tags=jep445_barescript,indent=0]
----
An example with additional fields and methods might look like:
[source,groovy]
----
include::../test/ScriptsAndClassesSpecTest.groovy[tags=jep445_script,indent=0]
----
<1> Note that multi-assignment syntax is supported and results in separate field definitions for each component.
=== Differences with Java JEP 445 behavior
There are some differences with Groovy's JEP 445 support and that offered by Java:
* Java supports either a no-arg `main` method or one containing a single `String[]` parameter.
Groovy also adds support for a single untyped (`Object`) parameter, e.g. `def main(args) { ... }`.
This addition is known by the Groovy runner but would not be known by the
Java launch protocol for a JDK supporting JEP 445.
* Java supports `void` main methods. Groovy also adds support for untyped `def` (`Object`) methods,
e.g. `def main(...)` as well as `void main(...)`.
This addition is known by the Groovy runner but would not be known by the Java launch protocol
for a JDK supporting JEP 445.
* For static `main` variants, Groovy _promotes_ the no-arg or untyped variants to have the
standard `public static void main(String[] args)` signature. This is for compatibility
with versions of Groovy prior to Groovy 5 (where JEP 445 support was added).
As a consequence, such classes are compatible with the Java launch protocol prior to JEP 445 support.
* Groovy's runner has been made aware of JEP 445 compatible classes and can run all variations
for JDK11 and above and without the need for preview mode to be enabled.