blob: 1a391a073e81c7ca18927d5fbf45ef4a8755de94 [file] [log] [blame]
= Program structure (Anto Aravinth)
This chapter covers the program structure of the Groovy programming language.
== Package names
Package names play exactly the same role as in Java. They allows 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::{projectdir}/src/spec/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::{projectdir}/src/spec/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::{projectdir}/src/spec/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. There are six packages that groovy imports for you, they are:
[source,groovy]
----
import java.lang.*
import java.util.*
import java.io.*
import java.net.*
import groovy.lang.*
import groovy.util.*
import java.math.BigInteger
import java.math.BigDecimal
----
=== 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 the a class inside a package.
[source,groovy]
----
include::{projectdir}/src/spec/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 ++*++, a so called 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::{projectdir}/src/spec/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, where `*` imports all the classes under package ++groovy.xml++:
[source,groovy]
----
include::{projectdir}/src/spec/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. This is similar to Java's static import capability but works with Java 1.4 and above and is a little 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. If you have the same types, the imported class takes precedence. Here is a sample of its usage:
[source,groovy]
----
include::{projectdir}/src/spec/test/PackageTest.groovy[tags=static_imports,indent=0]
----
As you can see, now we can able to refer to the static variable ++FALSE++ in our code base cleanly.
=== 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::{projectdir}/src/spec/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 methods from the given class.
For example, lets 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::{projectdir}/src/spec/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.
Consider the following class, provided by some third-party library code:
[source,groovy]
----
include::{projectdir}/src/spec/test/thirdpartylib/MultiplyTwo.groovy[tags=third_party_lib,indent=0]
----
Suppose we have some existing code using the library:
[source,groovy]
----
include::{projectdir}/src/spec/test/PackageTest.groovy[tags=using_third_party_lib,indent=0]
----
Now suppose that, after using this library throughout your codebase, we discover that it doesn't give correct results. How can we fix it in one place, outside of the original class, without changing all the code that's using it? Groovy has an elegant solution to this problem.
With a simple import aliasing, we can fix the bug like this:
[source,groovy]
----
include::{projectdir}/src/spec/test/PackageTest.groovy[tags=fixing_thrid_party_lib,indent=0]
----
Thats it! Notice how Groovy allowed us to use the ++as++ keyword to rename our imported class and solve this common problem.
Type aliasing is also useful to solve name collision problems which can occur with ++*++ imports.
== Scripts versus classes (TBD)
== Initializers (TBD)
=== Static initializers (TBD)
=== Instance initializers (TBD)