| ////////////////////////////////////////// |
| |
| 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-semantics[] |
| :core-semantics: core-semantics.adoc |
| endif::[] |
| ifndef::core-syntax[] |
| :core-syntax: core-syntax.adoc |
| endif::[] |
| |
| = Differences with Java |
| |
| Groovy tries to be as natural as possible for Java developers. We’ve |
| tried to follow the principle of least surprise when designing Groovy, |
| particularly for developers learning Groovy who’ve come from a Java |
| background. |
| |
| Here we list all the major differences between Java and Groovy. |
| |
| == Default imports |
| |
| All these packages and classes are imported by default, i.e. you do not |
| have to use an explicit `import` statement to use them: |
| |
| * java.io.* |
| * java.lang.* |
| * java.math.BigDecimal |
| * java.math.BigInteger |
| * java.net.* |
| * java.time.* |
| * java.util.* |
| * groovy.lang.* |
| * groovy.util.* |
| |
| == Multi-methods |
| |
| In Groovy, the methods which will be invoked are chosen at runtime. This is called runtime dispatch or multi-methods. It |
| means that the method will be chosen based on the types of the arguments at runtime. In Java, this is the opposite: methods |
| are chosen at compile time, based on the declared types. |
| |
| The following code, written as Java code, can be compiled in both Java and Groovy, but it will behave differently: |
| |
| [source,java] |
| ---- |
| include::../test/DifferencesFromJavaTest.groovy[tags=multimethods,indent=0] |
| ---- |
| |
| In Java, you would have: |
| |
| [source,java] |
| ---- |
| include::../test/DifferencesFromJavaTest.groovy[tags=multimethods_java,indent=0] |
| ---- |
| |
| Whereas in Groovy: |
| |
| [source,java] |
| ---- |
| include::../test/DifferencesFromJavaTest.groovy[tags=multimethods_groovy,indent=0] |
| ---- |
| |
| That is because Java will use the static information type, which is that `o` is declared as an `Object`, whereas |
| Groovy will choose at runtime, when the method is actually called. Since it is called with a `String`, then the |
| `String` version is called. |
| |
| == Array initializers |
| |
| In Java, array initializers take either of these two forms: |
| |
| [source,java] |
| ---- |
| include::../test/DifferencesFromJavaTest.groovy[tags=arraycreate_fail,indent=0] |
| ---- |
| |
| In Groovy, the `{ ... }` block is reserved for closures. |
| That means that you cannot create array literals using Java's array initializer shorthand syntax. |
| You instead borrow Groovy's literal list notation like this: |
| |
| [source,groovy] |
| ---- |
| include::../test/DifferencesFromJavaTest.groovy[tags=arraycreate_success,indent=0] |
| ---- |
| |
| For Groovy 3+, you can optionally use the Java's array initializer long syntax: |
| |
| [source,groovy] |
| ---- |
| include::../test/DifferencesFromJavaTest.groovy[tags=arraycreate3_success,indent=0] |
| ---- |
| |
| == Package scope visibility |
| |
| In Groovy, omitting a modifier on a field doesn't result in a package-private field like in Java: |
| |
| [source,groovy] |
| ---- |
| include::../test/DifferencesFromJavaTest.groovy[tags=packageprivate_property,indent=0] |
| ---- |
| |
| Instead, it is used to create a _property_, that is to say a _private field_, an associated _getter_ and an associated |
| _setter_. |
| |
| It is possible to create a package-private field by annotating it with `@PackageScope`: |
| |
| [source,groovy] |
| ---- |
| include::../test/DifferencesFromJavaTest.groovy[tags=packageprivate_field,indent=0] |
| ---- |
| |
| == ARM blocks |
| |
| Java 7 introduced ARM (Automatic Resource Management) blocks (also know as try-with-resources) blocks like this: |
| |
| [source,java] |
| ---- |
| Path file = Paths.get("/path/to/file"); |
| Charset charset = Charset.forName("UTF-8"); |
| try (BufferedReader reader = Files.newBufferedReader(file, charset)) { |
| String line; |
| while ((line = reader.readLine()) != null) { |
| System.out.println(line); |
| } |
| |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| ---- |
| |
| Such blocks are supported from Groovy 3+. |
| However, Groovy provides various methods relying on closures, which have the same effect while being more idiomatic. For example: |
| |
| [source,groovy] |
| ---- |
| new File('/path/to/file').eachLine('UTF-8') { |
| println it |
| } |
| ---- |
| |
| or, if you want a version closer to Java: |
| |
| [source,groovy] |
| ---- |
| new File('/path/to/file').withReader('UTF-8') { reader -> |
| reader.eachLine { |
| println it |
| } |
| } |
| ---- |
| |
| == Inner classes |
| |
| WARNING: The implementation of anonymous inner classes and nested classes follow Java closely, |
| but there are some differences, e.g. |
| local variables accessed from within such classes don't have to be final. |
| We piggyback on some implementation details we use for `groovy.lang.Closure` |
| when generating inner class bytecode. |
| |
| === Static inner classes |
| |
| Here's an example of static inner class: |
| |
| [source,groovy] |
| --------------------- |
| include::../test/DifferencesFromJavaTest.groovy[tags=innerclass_1,indent=0] |
| --------------------- |
| |
| The usage of static inner classes is the best supported one. If you |
| absolutely need an inner class, you should make it a static one. |
| |
| === Anonymous Inner Classes |
| |
| [source,groovy] |
| --------------------- |
| include::../test/DifferencesFromJavaTest.groovy[tags=innerclass_2,indent=0] |
| --------------------- |
| |
| === Creating Instances of Non-Static Inner Classes |
| |
| In Java you can do this: |
| |
| [source,java] |
| ---------------------------------- |
| include::../test/DifferencesFromJavaTest.groovy[tags=innerclass_3_java,indent=0] |
| ---------------------------------- |
| |
| Before 3.0.0, Groovy doesn't support the `y.new X()` syntax. Instead, you have to write `new X(y)`, like in the code below: |
| |
| [source,groovy] |
| ---------------------------------- |
| include::../test/DifferencesFromJavaTest.groovy[tags=innerclass_3,indent=0] |
| ---------------------------------- |
| |
| [WARNING] |
| Caution though, Groovy supports calling methods with one |
| parameter without giving an argument. The parameter will then have the |
| value null. Basically the same rules apply to calling a constructor. |
| There is a danger that you will write new X() instead of new X(this) for |
| example. Since this might also be the regular way we have not yet found |
| a good way to prevent this problem. |
| |
| [NOTE] |
| Groovy 3.0.0 supports Java style syntax for creating instances of non-static inner classes. |
| |
| == Lambda expressions and the method reference operator |
| |
| Java 8+ supports lambda expressions and the method reference operator (`::`): |
| |
| [source,java] |
| ---- |
| Runnable run = () -> System.out.println("Run"); // Java |
| list.forEach(System.out::println); |
| ---- |
| |
| Groovy 3 and above also support these within the Parrot parser. |
| In earlier versions of Groovy you should use closures instead: |
| |
| [source,groovy] |
| ---- |
| Runnable run = { println 'run' } |
| list.each { println it } // or list.each(this.&println) |
| ---- |
| |
| Since Groovy 6.0, intersection-type casts (https://docs.oracle.com/javase/specs/jls/se21/html/jls-15.html#jls-15.16[JLS §15.16]) |
| are supported on lambdas and method references, so a lambda can opt into `Serializable` |
| or other marker interfaces, just like in Java: |
| |
| [source,groovy] |
| ---- |
| Runnable r = (Runnable & Serializable) () -> println('hi') // serializable lambda |
| ---- |
| |
| Groovy additionally accepts the `as` form, where parentheses are required around |
| the intersection: |
| |
| [source,groovy] |
| ---- |
| def r = { -> println 'hi' } as (Runnable & Serializable) |
| ---- |
| |
| See the <<{core-semantics}#intersection-cast,Intersection-type cast and coercion>> section in the semantics guide. |
| |
| |
| == GStrings |
| |
| As double-quoted string literals are interpreted as `GString` values, Groovy may fail |
| with compile error or produce subtly different code if a class with `String` literal |
| containing a dollar character is compiled with Groovy and Java compiler. |
| |
| While typically, Groovy will auto-cast between `GString` and `String` if an API declares |
| the type of a parameter, beware of Java APIs that accept an `Object` parameter and then |
| check the actual type. |
| |
| == String and Character literals |
| |
| Singly-quoted literals in Groovy are used for `String`, and double-quoted result in |
| `String` or `GString`, depending whether there is interpolation in the literal. |
| |
| [source,groovy] |
| ---- |
| include::../test/DifferencesFromJavaTest.groovy[tags=type_depends_on_quoting_AND_whether_we_actually_interpolate,indent=0] |
| ---- |
| |
| Groovy will automatically cast a single-character `String` to `char` only when assigning to |
| a variable of type `char`. When calling methods with arguments of type `char` we need |
| to either cast explicitly or make sure the value has been cast in advance. |
| |
| [source,groovy] |
| ---- |
| include::../test/DifferencesFromJavaTest.groovy[tags=single_char_strings_are_autocasted,indent=0] |
| ---- |
| |
| Groovy supports two styles of casting and in the case of casting to `char` there |
| are subtle differences when casting a multi-char strings. The Groovy style cast is |
| more lenient and will take the first character, while the C-style cast will fail |
| with exception. |
| |
| [source,groovy] |
| ---- |
| include::../test/DifferencesFromJavaTest.groovy[tags=chars_c_vs_groovy_cast,indent=0] |
| ---- |
| |
| == Behaviour of `==` |
| |
| In Java, `==` means equality of primitive types or identity for objects. |
| In Groovy, `==` means equality in all places. |
| For non-primitives, it translates to `a.compareTo(b) == 0`, |
| when evaluating equality for `Comparable` objects, |
| and `a.equals(b)` otherwise. |
| |
| To check for identity (reference equality), use the `is` method: `a.is(b)`. |
| From Groovy 3, you can also use the `===` operator (or negated version): `a === b` (or `c !== d`). |
| |
| == Primitives and wrappers |
| |
| In a pure object-oriented language, everything would be an object. |
| Java takes the stance that primitive types, such as int, boolean and double, |
| are used very frequently and worthy of special treatment. |
| Primitives can be efficiently stored and manipulated but can't be used in all contexts where an object could be used. |
| Luckily, Java auto boxes and unboxes primitives when they are passed as parameters or used as return types: |
| |
| [source,java] |
| ---- |
| public class Main { // Java |
| |
| float f1 = 1.0f; |
| Float f2 = 2.0f; |
| |
| float add(Float a1, float a2) { return a1 + a2; } |
| |
| Float calc() { return add(f1, f2); } // <1> |
| |
| public static void main(String[] args) { |
| Float calcResult = new Main().calc(); |
| System.out.println(calcResult); // => 3.0 |
| } |
| } |
| ---- |
| <1> The `add` method expects wrapper then primitive type arguments, |
| but we are supplying parameters with a primitive then wrapper type. |
| Similarly, the return type from `add` is primitive, but we need the wrapper type. |
| |
| Groovy does the same: |
| |
| [source,groovy] |
| ---- |
| class Main { |
| |
| float f1 = 1.0f |
| Float f2 = 2.0f |
| |
| float add(Float a1, float a2) { a1 + a2 } |
| |
| Float calc() { add(f1, f2) } |
| } |
| |
| assert new Main().calc() == 3.0 |
| ---- |
| |
| Groovy, also supports primitives and object types, however, it goes a little further |
| in pushing OO purity; it tries hard to treat _everything_ as an object. |
| Any primitive typed variable or field can be treated like an object, and it |
| will be link:core-object-orientation.html#_primitive_types[auto-wrapped] as needed. |
| While primitive types might be used under the covers, |
| their use should be indistinguishable from normal object use whenever possible, |
| and they will be boxed/unboxed as needed. |
| |
| Here is a little example using Java trying to (incorrectly for Java) dereference a primitive `float`: |
| |
| [source,java] |
| ---- |
| public class Main { // Java |
| |
| public float z1 = 0.0f; |
| |
| public static void main(String[] args){ |
| new Main().z1.equals(1.0f); // DOESN'T COMPILE, error: float cannot be dereferenced |
| } |
| } |
| ---- |
| |
| The same example using Groovy compiles and runs successfully: |
| |
| [source,groovy] |
| ---- |
| class Main { |
| float z1 = 0.0f |
| } |
| assert !(new Main().z1.equals(1.0f)) |
| ---- |
| |
| Because of Groovy's additional use of un/boxing, it does not follow |
| Java's behavior of widening taking priority over boxing. |
| Here's an example using `int` |
| |
| [source,groovy] |
| ---- |
| include::../test/PrimitiveTest.groovy[tags=widening_vs_boxing,indent=0] |
| ---- |
| <1> This is the method that Java would call, since widening has precedence over unboxing. |
| <2> This is the method Groovy actually calls, since all primitive references use their wrapper class. |
| |
| [[primitive-optimisation-numbers]] |
| === Numeric Primitive Optimisation with `@CompileStatic` |
| |
| Since Groovy converts to wrapper classes in more places, you might wonder |
| whether it produces less efficient bytecode for numeric expressions. |
| Groovy has a highly optimised set of classes for doing math computations. |
| When using `@CompileStatic`, expressions involving only primitives |
| uses the same bytecode that Java would use. |
| |
| [[zero-edge-case]] |
| === Positive/Negative zero edge case |
| |
| Java float/double operations for both primitives and wrapper classes follow the IEEE 754 standard |
| but there is an interesting edge case involving positive and negative zero. |
| The standard supports distinguishing between these two cases and while in many scenarios |
| programmers may not care about the difference, in some mathematical or data science scenarios |
| it is important to cater for the distinction. |
| |
| For primitives, Java maps down onto a special |
| https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dcmp_op[bytecode instruction] |
| when comparing such values which has the property that |
| "Positive zero and negative zero are considered equal". |
| |
| [source,ruby] |
| ---- |
| jshell> float f1 = 0.0f |
| f1 ==> 0.0 |
| |
| jshell> float f2 = -0.0f |
| f2 ==> -0.0 |
| |
| jshell> f1 == f2 |
| $3 ==> true |
| ---- |
| |
| For the wrapper classes, e.g. jdk:java.base/java.lang.Float#equals(java.lang.Object)[Float#equals], |
| the result is `false` for this same case. |
| |
| [source,ruby] |
| ---- |
| jshell> Float f1 = 0.0f |
| f1 ==> 0.0 |
| |
| jshell> Float f2 = -0.0f |
| f2 ==> -0.0 |
| |
| jshell> f1.equals(f2) |
| $3 ==> false |
| ---- |
| |
| Groovy on the one hand tries to follow Java behavior closely, but on the other |
| switches automatically between primitives and wrapped equivalents in more places. |
| To avoid confusion we recommend the following guidelines: |
| |
| * If you wish to distinguish between positive and negative zero, use the `equals` |
| method directly or cast any primitives to their wrapper equivalent before using `==`. |
| * If you wish to ignore the difference between positive and negative zero, use the `equalsIgnoreZeroSign` |
| method directly or cast any non-primitives to their primitive equivalent before using `==`. |
| |
| These guidelines are illustrated in the following example: |
| |
| [source,groovy] |
| ---- |
| include::../test/PrimitiveTest.groovy[tags=pos_neg_zero,indent=0] |
| ---- |
| <1> Recall that for non-primitives, `==` maps to `.equals()` |
| |
| == Conversions |
| |
| Java does automatic widening and narrowing |
| https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html[conversions]. |
| |
| [cols=">2s,^1a,^1a,^1a,^1a,^1a,^1a,^1a,^1a"] |
| .Java Conversions |
| |============================================================================================ |
| | 8+|*Converts to* |
| | Converts from | *boolean* | *byte* | *short* | *char* | *int* | *long* | *float* | *double* |
| | boolean | - | N | N | N | N | N | N | N |
| | byte | N | - | Y | C | Y | Y | Y | Y |
| | short | N | C | - | C | Y | Y | Y | Y |
| | char | N | C | C | - | Y | Y | Y | Y |
| | int | N | C | C | C | - | Y | T | Y |
| | long | N | C | C | C | C | - | T | T |
| | float | N | C | C | C | C | C | - | Y |
| | double | N | C | C | C | C | C | C | - |
| |============================================================================================ |
| * 'Y' indicates a conversion Java can make + |
| * 'C' indicates a conversion Java can make when there is an explicit cast + |
| * 'T` indicates a conversion Java can make but data is truncated + |
| * 'N' indicates a conversion Java can't make |
| |
| Groovy expands greatly on this. |
| |
| [%autowidth] |
| [cols=">2s,^1a,^1a,^1a,^1a,^1a,^1a,^1a,^1a,^1a,^1a,^1a,^1a,^1a,^1a,^1a,^1a,^1a,^1a"] |
| .Groovy Conversions |
| |================================================================================================================================================================================================================= |
| | 18+|*Converts to* |
| | Converts from |[.rotate]`boolean` |[.rotate]`Boolean` |[.rotate]`byte` |[.rotate]`Byte` |[.rotate]`short` |[.rotate]`Short` |[.rotate]`char` |[.rotate]`Character` |[.rotate]`int` |[.rotate]`Integer` |[.rotate]`long` |[.rotate]`Long` |[.rotate]`BigInteger` |[.rotate]`float` |[.rotate]`Float` |[.rotate]`double` |[.rotate]`Double` |[.rotate]`BigDecimal` |
| | `boolean` | - | B | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N |
| | `Boolean` | B | - | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N |
| | `byte` | T | T | - | B | Y | Y | Y | D | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
| | `Byte` | T | T | B | - | Y | Y | Y | D | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
| | `short` | T | T | D | D | - | B | Y | D | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
| | `Short` | T | T | D | T | B | - | Y | D | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
| | `char` | T | T | Y | D | Y | D | - | D | Y | D | Y | D | D | Y | D | Y | D | D |
| | `Character` | T | T | D | D | D | D | D | - | D | D | D | D | D | D | D | D | D | D |
| | `int` | T | T | D | D | D | D | Y | D | - | B | Y | Y | Y | Y | Y | Y | Y | Y |
| | `Integer` | T | T | D | D | D | D | Y | D | B | - | Y | Y | Y | Y | Y | Y | Y | Y |
| | `long` | T | T | D | D | D | D | Y | D | D | D | - | B | Y | T | T | T | T | Y |
| | `Long` | T | T | D | D | D | T | Y | D | D | T | B | - | Y | T | T | T | T | Y |
| | `BigInteger` | T | T | D | D | D | D | D | D | D | D | D | D | - | D | D | D | D | T |
| | `float` | T | T | D | D | D | D | T | D | D | D | D | D | D | - | B | Y | Y | Y |
| | `Float` | T | T | D | T | D | T | T | D | D | T | D | T | D | B | - | Y | Y | Y |
| | `double` | T | T | D | D | D | D | T | D | D | D | D | D | D | D | D | - | B | Y |
| | `Double` | T | T | D | T | D | T | T | D | D | T | D | T | D | D | T | B | - | Y |
| | `BigDecimal` | T | T | D | D | D | D | D | D | D | D | D | D | D | T | D | T | D | - |
| |================================================================================================================================================================================================================= |
| * 'Y' indicates a conversion Groovy can make + |
| * 'D' indicates a conversion Groovy can make when compiled dynamically or explicitly cast + |
| * 'T' indicates a conversion Groovy can make but data is truncated + |
| * 'B' indicates a boxing/unboxing operation + |
| * 'N' indicates a conversion Groovy can't make. |
| |
| The truncation uses <<{core-semantics}#the-groovy-truth,Groovy Truth>> when converting to `boolean`/`Boolean`. Converting |
| from a number to a character casts the `Number.intvalue()` to `char`. Groovy constructs `BigInteger` and `BigDecimal` |
| using `Number.doubleValue()` when converting from a `Float` or `Double`, otherwise it constructs using `toString()`. |
| Other conversions have their behavior defined by `java.lang.Number`. |
| |
| == Extra keywords |
| |
| Groovy has many of the same keywords as Java and Groovy 3 and above also has the same `var` reserved type as Java. |
| In addition, Groovy has the following keywords: |
| |
| * `as` |
| * `def` |
| * `in` |
| * `trait` |
| * `it` // within closures |
| |
| Groovy is less stringent than Java in that it allows some keywords to appear in places that would be illegal in Java, |
| e.g. the following is valid: `var var = [def: 1, as: 2, in: 3, trait: 4]`. |
| Never-the-less, you are discouraged from using the above keywords in places that might cause confusion even when |
| the compiler might be happy. In particular, avoid using them for variable, method and class names, |
| so our previous `var var` example would be considered poor style. |
| |
| Additional documentation is available for <<{core-syntax}#_keywords,keywords>>. |