Skript
parent
13afd81615
commit
affa624e20
21
README.md
21
README.md
|
@ -5,12 +5,21 @@ Aus zeitlichen Gründen empfehlen wir die Verwendung eines [Online Compilers][2]
|
|||
|
||||
## Themenüberblick:
|
||||
|
||||
1. Ana: Einführung
|
||||
2. Yase: Datentypen, Schleifen
|
||||
3. Yuliya: Methoden, Klassen
|
||||
4. Luka: Bedingungen, Datenstrukturen
|
||||
5. Yannick: IO, Exception
|
||||
6. Ileyan: Schlüsselwörter, Tests
|
||||
| # | Thema |
|
||||
| --- | ----- |
|
||||
| 1. |[Schlüsselwörter](./skript/keywords.md)|
|
||||
| 2. |[Datentypen](./skript/datentypen.md)|
|
||||
| 3. |[Operatoren](./skript/operatoren.md)|
|
||||
| 4. |[Schleifen](./skript/schleifen.md) |
|
||||
| 5. |[Bedingungen](./skript/bedingungen.md)|
|
||||
| 6. | [Klassen](./skript/klassen.md) |
|
||||
| 7. | [Methoden](./skript/methoden.md)|
|
||||
| 8. | [Exception Handling](./skript/exceptions.md) |
|
||||
| 9. | [I/O]()|
|
||||
| 10. | [Datenstrukturen](./skript/datenstrukturen.md)|
|
||||
| 11. | [Testen]()|
|
||||
|
||||
|
||||
|
||||
## Live Übungen:
|
||||
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
## Schlüsselwörter
|
||||
|
||||
- Folgende Schlüsselwörter gibt es in Groovy:
|
||||
|
||||
<img src="img/keywords.png" width="350">
|
||||
|
||||
Diese Wörter (und auch die Bezeichner für die Datentypen) dürfen im allgemeinen, wie in vielen anderen Sprachen nicht als Variablennamen benutzt werden. Jedoch gibt es in Groovy einen Weg dies zu umgehen:
|
||||
|
||||
```Groovy
|
||||
def "this"(){print "Diese Methode hat den Namen \'this\' }
|
||||
//-> "Diese Methode hat den Namen 'this'"
|
||||
```
|
||||
|
||||
Hierbei muss man den Methodennamen in Anführungszeichen setzen. Ein Methodenaufruf würde dann wie folgt aussehen:
|
||||
|
||||
```Groovy
|
||||
this.this(); //(Es wird angenommen das Objekt ruft selbst die Methode auf)
|
||||
```
|
||||
|
||||
Es wird jedoch stark davon abgeraten dieses Groovy - Feature zu benutzen, da es in den meisten Fällen keine Vorteile bietet und nur für Verwirrung sorgt.
|
||||
|
||||
### Kontextuelle Schlüsselwörter:
|
||||
|
||||
- as
|
||||
- in
|
||||
- permitsrecord
|
||||
- sealed
|
||||
- trait
|
||||
- var
|
||||
- yields
|
||||
|
||||
Da diese Schlüsselwörter in weder in Java, noch in früheren Groovy Versionen existierten, kann man sie für Variablen- / Methodennamen benutzen, ohne sich des oben gezeigten Tricks bedienen zu müssen. Es wird empfohlen beim Aufrufen dieser Methoden / beim Zugriff auf diese Variablen ein "this." davorzusetzen, damit es nicht Verwirrungen kommt. Am besten lässt man es jedoch direkt sein und überlegt sich andere Namen.
|
||||
|
||||
## Tests
|
||||
|
||||
- Groovy bietet nicht nur Support für JUnit 5 (und älter), sondern liefert auch einen eigenen Satz an Testmethoden, um die testgetriebene Programmierung zu vereinfachen.
|
||||
|
||||
Ein Beispiel hierfür wäre Spock. Mit Spock kann man sowohl Java, als auch Groovy Code testen. Getestet wird mit sogenannten Spezifikationen. Die Testklasse muss also von **spock.lang.Specification** erben.
|
||||
|
||||
<img src="img/spock.png" width="500">
|
||||
|
||||
#### Erklärung:
|
||||
|
||||
1. Der Name der Testmethode muss ein String sein, der beschreibt, was beim Testen erwartet wird.
|
||||
2. Das Schlüsselwort **setup** dient dazu, lokale Variablen zu initialisieren, die für den Test gebraucht werden (vgl. @BeforeEach Annotation, JUnit)
|
||||
3. Das **when** Schlüsselwort beschreibt den Codeabschnitt, der zu einem bestimmten Verhalten führen soll
|
||||
4. **then** beschreibt das erwartete Verhalten.
|
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
Binary file not shown.
After Width: | Height: | Size: 104 KiB |
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
|
@ -32,7 +32,7 @@ die Variable haben soll.
|
|||
Wenn man z.B. eine grosse Zahl hat, wird der Typ automatisch an die grössere Zahl angepasst
|
||||
|
||||
Beispiel
|
||||
```
|
||||
```Groovy
|
||||
def a = 100
|
||||
boolean isInteger = a Instance of Integer
|
||||
println(isInteger) //-> true
|
||||
|
@ -46,7 +46,7 @@ Man kann einen String auf verschiedene Arten definieren.
|
|||
Sie reichen von einfachen einzeiligen Strings ...
|
||||
|
||||
Beispiel
|
||||
```
|
||||
```Groovy
|
||||
def einfach = 'einfache Anführungszeichen'
|
||||
def doppelt = "doppelte Anführungszeichen"
|
||||
def slashy = /ein "Slashy String" ohne 'Escape'/
|
||||
|
@ -61,7 +61,7 @@ println(dollar) // weitere Möglichkeit und Einfügen von '/'
|
|||
...bis hin zu komplexen mehrzeiligen Textblöcken
|
||||
|
||||
Beispiel
|
||||
```
|
||||
```Groovy
|
||||
def dreifach = '''
|
||||
Ich bin
|
||||
ein String der über
|
||||
|
@ -88,7 +88,7 @@ Java = statisch typisiert d.h. der Typ der Variablen
|
|||
wird zur Kompilierungszeit festgelegt und kann sich nicht ändern
|
||||
|
||||
Beispiel
|
||||
```
|
||||
```Groovy
|
||||
String s = "Hallo Welt";
|
||||
System.out.println(s); // Hallo Welt
|
||||
|
||||
|
@ -100,8 +100,8 @@ Groovy = dynamisch typisiert d.h. der Typ der Variablen
|
|||
wird zur Laufzeit bestimmt. Daher ist es auch möglich
|
||||
einer Variablen die zuvor einen String-Wert hatte, einen Integer-Wert zuzuweisen
|
||||
|
||||
Beispiel
|
||||
```
|
||||
Beispiel:
|
||||
```Groovy
|
||||
def s = "Hallo Welt"
|
||||
println s.getClass() // -> class java.lang.String
|
||||
|
||||
|
@ -116,8 +116,8 @@ Dabei wird ein Platzhalter im String durch seinen tatsächlichen Wert ersetzt, w
|
|||
der String ausgewertet wird.
|
||||
Platzhalter werden durch `${ausdruck}` gekennzeichnet
|
||||
|
||||
Beispiel
|
||||
```
|
||||
Beispiel:
|
||||
```Groovy
|
||||
def person = [name: 'Thomas Smits', lehrt: 'PR3']
|
||||
def hochschule = "Hochschule Mannheim"
|
||||
def ausdruck = "Hallo, mein Name ist ${person.name}. Ich unterrichte ${person.lehrt}. An der ${hochschule}."
|
||||
|
@ -130,7 +130,7 @@ Man nennt dies einen `GString`
|
|||
Jeder String kann mit `+` konkateniert werden
|
||||
|
||||
Beispiel
|
||||
```
|
||||
```Groovy
|
||||
def eins = "Ein String"
|
||||
def zwei = ' wird konkateniert'
|
||||
|
||||
|
@ -142,7 +142,7 @@ Man kann auf Zeichen eines Strings mit positiven und negativen Indizes zugreifen
|
|||
Zählungen beginnen bei null und negative Indizes beginnen den String am Ende
|
||||
|
||||
Beispiel
|
||||
```
|
||||
```Groovy
|
||||
def greeter = "Hallo Welt"
|
||||
|
||||
println(greeter[1]) // -> 'a'
|
|
@ -0,0 +1,50 @@
|
|||
# Exception-Handling:
|
||||
Grundsätzlich ähnelt sich die Ausnahmebehandlung in Groovy der in Java sehr, weshalb dies ein eher kurzes Kapitel wird. Dennoch wollen wir hier auf einige Grundlagen und Besonderheiten eingehen.
|
||||
|
||||
#### Vererbungshierarchie:
|
||||
<img src="./../img/exceptions.png" width="350">
|
||||
|
||||
Es gilt die selbe Vererbungshierarchie, wie in Java. Alle Exception Klassen sind von der Klasse `Exception`, und somit auch indirekt von `Throwable` abgeleitet. Will man eigene Exceptions schreiben, müssen diese also von `Exception` erben.
|
||||
|
||||
#### Ausnahmen fangen:
|
||||
Zum Fangen von Ausnahmen werden auch in Groovy try-catch-finally Blöcke benutzt.
|
||||
- `try:` Dieser Block enthält den gefährlichen Code, der potentiell Ausnahmen werfen könnte.
|
||||
- `catch:` Hier findet die eigentliche Fehlerbehandlung statt. Die Exceptions sollen hier nicht einfach ausgegeben bzw. ignoriert werden, sondern sinnvoll behandelt werden
|
||||
- `finally:` Dieser Block ist gedacht für Aufräumarbeiten und wird immer ausgeführt, egal ob das Programm abrubt beendet wird durch Eintritt in den **catch**-Block, oder normal beendet wird.
|
||||
|
||||
Es können auch mehrere catch-Blocke nach einem try-Block folgen. Der Compiler wählt hier den ersten passenden Block aus. Deshalb gilt: **Man soll beim Schreiben der catch-Blöcke immer vom Spezifischen zum Allgemeinen gehen.**
|
||||
|
||||
````Groovy
|
||||
try {
|
||||
// Code der die Exception werfen kann
|
||||
} catch (IOException e) {
|
||||
// Fehlerbehandlung
|
||||
} catch (Exception e) {
|
||||
// Fehlerbehandlung
|
||||
}
|
||||
````
|
||||
|
||||
#### Dynamische Ausnahmebehandlung:
|
||||
Anders als in Java muss in Groovy der Typ der Exception nicht angegeben werden. Da Groovy eine dynamisch typisierte Sprache ist, kann hier auch einfach nur der Variablenname angegeben werden. Der Compiler wählt dann die passende Exception zur Laufzeit aus.
|
||||
|
||||
````Groovy
|
||||
try {
|
||||
def result = 1 / 0
|
||||
} catch (e) {
|
||||
e.printStackTrace()
|
||||
// Ausgabe: Caught an exception: Divison by zero
|
||||
}
|
||||
````
|
||||
|
||||
#### GString-Interpolation bei Exceptions:
|
||||
Groovy erlaubt es die Exception Message dierekt durch die GString Interpolation auszugeben. Dadurch kann man Individuellere Konsolenausgaben bei Fehlerbehandlungen erreichen.
|
||||
|
||||
````Groovy
|
||||
try {
|
||||
def file = new File("gibtesnicht.txt")
|
||||
file.text
|
||||
} catch (e) {
|
||||
println "Datei wurde nicht gefunden: ${e.message}"
|
||||
}
|
||||
// Ausgabe: Datei wurde nicht gefunden: gibtesnicht.txt (No such file or directory)
|
||||
````
|
|
@ -0,0 +1,76 @@
|
|||
## Schlüsselwörter
|
||||
|
||||
- Folgende Schlüsselwörter gibt es in Groovy:
|
||||
|
||||
<img src="./../img/keywords.png" width="350">
|
||||
|
||||
Diese Wörter (und auch die Bezeichner für die Datentypen) dürfen im allgemeinen, wie in vielen anderen Sprachen nicht als Variablennamen benutzt werden. Jedoch gibt es in Groovy einen Weg dies zu umgehen:
|
||||
|
||||
```Groovy
|
||||
def "this"(){print "Diese Methode hat den Namen \'this\' }
|
||||
//-> "Diese Methode hat den Namen 'this'"
|
||||
```
|
||||
|
||||
Hierbei muss man den Methodennamen in Anführungszeichen setzen. Ein Methodenaufruf würde dann wie folgt aussehen:
|
||||
|
||||
```Groovy
|
||||
this.this(); //(Es wird angenommen das Objekt ruft selbst die Methode auf)
|
||||
```
|
||||
|
||||
Es wird jedoch stark davon abgeraten dieses Groovy - Feature zu benutzen, da es in den meisten Fällen keine Vorteile bietet und nur für Verwirrung sorgt.
|
||||
|
||||
### Kontextuelle Schlüsselwörter:
|
||||
|
||||
- as
|
||||
- in
|
||||
- def
|
||||
- permitsrecord
|
||||
- sealed
|
||||
- trait
|
||||
- var
|
||||
- yields
|
||||
|
||||
Da diese Schlüsselwörter in weder in Java, noch in früheren Groovy Versionen existierten, kann man sie für Variablen- / Methodennamen benutzen, ohne sich des oben gezeigten Tricks bedienen zu müssen. Es wird empfohlen beim Aufrufen dieser Methoden / beim Zugriff auf diese Variablen ein "this." davorzusetzen, damit es nicht Verwirrungen kommt. Am besten lässt man es jedoch direkt sein und überlegt sich andere Namen.
|
||||
|
||||
### Das Schlüsselwort "def":
|
||||
Eines der wichtigsten Groovy-Features ist die dynamische Typisierung. Ihr bester Freund wird hier das Schlüsselwort "def" sein. Es wird benutzt, um eine Variable oder eine Methode zu definieren, ohne hierbei den Typ explizit angeben zu müssen. Würden wir in Java z.B. folgendes versuchen:
|
||||
````Java
|
||||
public class MyClass {
|
||||
public static void main(String args[]) {
|
||||
int a = 5;
|
||||
a = "Hallo";
|
||||
|
||||
System.out.println(a);
|
||||
}
|
||||
}
|
||||
````
|
||||
Dann würden wir folgende Ausgabe erhalten:
|
||||
````
|
||||
MyClass.java:4: error: incompatible types: String cannot be converted to int
|
||||
a = "Hallo";
|
||||
^
|
||||
1 error
|
||||
````
|
||||
|
||||
In Groovy gibt es für genau solche Fälle jedoch das Schlüsselwort "def". Folgendes ist also in Groovy möglich:
|
||||
|
||||
````Groovy
|
||||
def a = 10
|
||||
a = "Hallo"
|
||||
println a // Ausgabe: Hallo
|
||||
````
|
||||
Dieses Feature kann vor allem bei Methoden nützlich sein, bei denen man sich noch nicht auf einen Rückgabewert festgelegt hat. So ist es also möglich, dass eine Methode in einem Fall eine Zahl zurückgibt, in einem anderen Fall jedoch einen String. Folgendes Beispiel verdeutlicht das ganze nochmal:
|
||||
|
||||
````Groovy
|
||||
def divide(x, y){
|
||||
if (y == 0) {
|
||||
return "Keine Division durch 0 erlaubt!"
|
||||
}
|
||||
x/y
|
||||
}
|
||||
|
||||
println divide(6,3) //Ausgabe: class java.math.BigDecimal
|
||||
println divide(1,0) //Ausgabe: class java.lang.String
|
||||
````
|
||||
|
||||
Wie sie vielleicht bemerkt haben, wurde bei diesem Beispiel absichtlich auf die Angabe von Typen bei den Methodenparametern verzichtet. Groovy erlaubt es einem nämlich auch hier auf die explizite Angabe von Typen zu verzichten.
|
|
@ -0,0 +1,142 @@
|
|||
# Klassen
|
||||
|
||||
### Definition einer Klasse
|
||||
|
||||
|
||||
Klassen in Groovy werden ähnlich wie in Java definiert, jedoch mit einigen
|
||||
syntaktischen Erleichterungen: mit dem class-Schlüsselwort, ohne Semikolon
|
||||
|
||||
Beispiel
|
||||
```Groovy
|
||||
class Person {
|
||||
String name
|
||||
int age
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Konstruktoren
|
||||
|
||||
Groovy fügt automatisch einen Standardkonstruktor (="Default-Konstruktor") hinzu, wenn
|
||||
keine Konstruktoren explizit definiert sind. Dieser Standardkonstruktor initialisiert alle
|
||||
Eigenschaften der Klasse mit ihren Standardwerten.
|
||||
Man kann auch benutzerdefinierte Konstruktoren definieren oder die beiden Konstruktorarten
|
||||
auch gleichzeitig nutzen. Man spricht dabei von Konstruktorüberladung (auch wie in Java,
|
||||
C++, C#...)
|
||||
|
||||
```Groovy
|
||||
class Person {
|
||||
String name
|
||||
int age
|
||||
|
||||
//Default-Konstruktor
|
||||
def person = new Person()
|
||||
|
||||
//benutzerdefinierter Konstruktor
|
||||
Person(String name, int age) {
|
||||
this.name = name
|
||||
this.age = age
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```Groovy
|
||||
class Person {
|
||||
String name
|
||||
int age
|
||||
|
||||
Person() {
|
||||
// Standardkonstruktor
|
||||
}
|
||||
|
||||
Person(String name, int age) {
|
||||
this.name = name
|
||||
this.age = age
|
||||
}
|
||||
}
|
||||
|
||||
def person1 = new Person()
|
||||
println(person1.name) // Ausgabe: null
|
||||
println(person1.age) // Ausgabe: 0
|
||||
|
||||
def person2 = new Person("Alice", 30)
|
||||
println(person2.name) // Ausgabe: Alice
|
||||
println(person2.age) // Ausgabe: 30
|
||||
```
|
||||
|
||||
|
||||
### Standardkonstruktor mit Map
|
||||
|
||||
Groovy bietet eine spezielle Initialisierungsform mit einer Map an. Dieser Konstruktor
|
||||
ermöglicht Objekte einfach und übersichtlich zu initialisieren, indem die Eigenschaften
|
||||
des Objekts direkt in Form von Schlüssel-Wert-Paaren in der Map angegeben werden.
|
||||
Groovy kümmert sich dann um die Zuordnung der Werte zu den entsprechenden Feldern der Klasse.
|
||||
Der Map-Konstruktor muss nicht explizit definiert werden (hier nur als Beispiel), da es ein eingebautes Standard-Feature von Groovy ist.
|
||||
```Groovy
|
||||
class Person {
|
||||
String name
|
||||
int age
|
||||
|
||||
// Map-Konstruktor
|
||||
Person(Map properties) {
|
||||
properties.each { key, value -> this."$key" = value }
|
||||
}
|
||||
}
|
||||
|
||||
Person person = new Person(name: 'John', age: 30)
|
||||
println(person.name) // Ausgabe: John
|
||||
println(person.age) // Ausgabe: 30
|
||||
```
|
||||
|
||||
|
||||
### Eigenschaften
|
||||
|
||||
Eigenschaften können direkt als Felder definiert werden. Groovy
|
||||
generiert automatisch Getter- und Setter-Methoden (wie in Ruby)
|
||||
|
||||
Beispiel
|
||||
```Groovy
|
||||
Person person = new Person()
|
||||
person.name = "John"
|
||||
person.age = 30
|
||||
println person.name
|
||||
//John
|
||||
```
|
||||
|
||||
|
||||
Getter und Setter können aber auch manuell überschrieben werden
|
||||
|
||||
Beispiel
|
||||
|
||||
```Groovy
|
||||
class Fruits {
|
||||
private String fruitName
|
||||
private String fruitColor
|
||||
|
||||
def setFruitName(String name) {
|
||||
fruitName = name
|
||||
}
|
||||
|
||||
def getFruitName() {
|
||||
return "The fruitname is $fruitName"
|
||||
}
|
||||
|
||||
def setFruitColor(String color) {
|
||||
fruitColor = color
|
||||
}
|
||||
|
||||
def getFruitColor(){
|
||||
return "The color is $fruitColor"
|
||||
}
|
||||
|
||||
static void(args) {
|
||||
//Instanz erstellen
|
||||
Fruits apple = new Fruits()
|
||||
apple.setFruitName("apple")
|
||||
apple.setFruitColor("red")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -1,156 +1,15 @@
|
|||
# Klassen
|
||||
|
||||
### Definition einer Klasse
|
||||
|
||||
|
||||
Klassen in Groovy werden ähnlich wie in Java definiert, jedoch mit einigen
|
||||
syntaktischen Erleichterungen: mit dem class-Schlüsselwort, ohne Semikolon
|
||||
|
||||
Beispiel
|
||||
```
|
||||
class Person {
|
||||
String name
|
||||
int age
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Konstruktoren
|
||||
|
||||
Groovy fügt automatisch einen Standardkonstruktor (="Default-Konstruktor") hinzu, wenn
|
||||
keine Konstruktoren explizit definiert sind. Dieser Standardkonstruktor initialisiert alle
|
||||
Eigenschaften der Klasse mit ihren Standardwerten.
|
||||
Man kann auch benutzerdefinierte Konstruktoren definieren oder die beiden Konstruktorarten
|
||||
auch gleichzeitig nutzen. Man spricht dabei von Konstruktorüberladung (auch wie in Java,
|
||||
C++, C#...)
|
||||
|
||||
```
|
||||
class Person {
|
||||
String name
|
||||
int age
|
||||
|
||||
//Default-Konstruktor
|
||||
def person = new Person()
|
||||
|
||||
//benutzerdefinierter Konstruktor
|
||||
Person(String name, int age) {
|
||||
this.name = name
|
||||
this.age = age
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
class Person {
|
||||
String name
|
||||
int age
|
||||
|
||||
Person() {
|
||||
// Standardkonstruktor
|
||||
}
|
||||
|
||||
Person(String name, int age) {
|
||||
this.name = name
|
||||
this.age = age
|
||||
}
|
||||
}
|
||||
|
||||
def person1 = new Person()
|
||||
println(person1.name) // Ausgabe: null
|
||||
println(person1.age) // Ausgabe: 0
|
||||
|
||||
def person2 = new Person("Alice", 30)
|
||||
println(person2.name) // Ausgabe: Alice
|
||||
println(person2.age) // Ausgabe: 30
|
||||
```
|
||||
|
||||
|
||||
### Standardkonstruktor mit Map
|
||||
|
||||
Groovy bietet eine spezielle Initialisierungsform mit einer Map an. Dieser Konstruktor
|
||||
ermöglicht Objekte einfach und übersichtlich zu initialisieren, indem die Eigenschaften
|
||||
des Objekts direkt in Form von Schlüssel-Wert-Paaren in der Map angegeben werden.
|
||||
Groovy kümmert sich dann um die Zuordnung der Werte zu den entsprechenden Feldern der Klasse.
|
||||
Der Map-Konstruktor muss nicht explizit definiert werden (hier nur als Beispiel), da es ein eingebautes Standard-Feature von Groovy ist.
|
||||
```
|
||||
class Person {
|
||||
String name
|
||||
int age
|
||||
|
||||
// Map-Konstruktor
|
||||
Person(Map properties) {
|
||||
properties.each { key, value -> this."$key" = value }
|
||||
}
|
||||
}
|
||||
|
||||
Person person = new Person(name: 'John', age: 30)
|
||||
println(person.name) // Ausgabe: John
|
||||
println(person.age) // Ausgabe: 30
|
||||
```
|
||||
|
||||
|
||||
### Eigenschaften
|
||||
|
||||
Eigenschaften können direkt als Felder definiert werden. Groovy
|
||||
generiert automatisch Getter- und Setter-Methoden (wie in Ruby)
|
||||
|
||||
Beispiel
|
||||
```
|
||||
Person person = new Person()
|
||||
person.name = "John"
|
||||
person.age = 30
|
||||
println person.name
|
||||
//John
|
||||
```
|
||||
|
||||
|
||||
Getter und Setter können aber auch manuell überschrieben werden
|
||||
|
||||
Beispiel
|
||||
|
||||
```
|
||||
class Fruits {
|
||||
private String fruitName
|
||||
private String fruitColor
|
||||
|
||||
def setFruitName(String name) {
|
||||
fruitName = name
|
||||
}
|
||||
|
||||
def getFruitName() {
|
||||
return "The fruitname is $fruitName"
|
||||
}
|
||||
|
||||
def setFruitColor(String color) {
|
||||
fruitColor = color
|
||||
}
|
||||
|
||||
def getFruitColor(){
|
||||
return "The color is $fruitColor"
|
||||
}
|
||||
|
||||
static void(args) {
|
||||
//Instanz erstellen
|
||||
Fruits apple = new Fruits()
|
||||
apple.setFruitName("apple")
|
||||
apple.setFruitColor("red")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
# Methoden
|
||||
|
||||
### Definition einer Methode
|
||||
|
||||
Methoden werden ähnlich wie in Java definiert, können aber optional einen Rückgabetyp
|
||||
haben
|
||||
```
|
||||
class Calculator {
|
||||
int add(int a, int b) {
|
||||
return a + b
|
||||
}
|
||||
```Groovy
|
||||
class Calculator {
|
||||
int add(int a, int b) {
|
||||
return a + b
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -159,9 +18,9 @@ werden.
|
|||
|
||||
|
||||
Beispiel 1
|
||||
```
|
||||
def printHello() {
|
||||
println "Hello..."
|
||||
```Groovy
|
||||
def printHello() {
|
||||
println "Hello..."
|
||||
}
|
||||
|
||||
printHello()
|
||||
|
@ -179,7 +38,7 @@ sum(5,2)
|
|||
|
||||
Methoden können Instanzmethoden sein und auf Instanzvariablen zugreifen, indem man $-Zeichen und optional eckige Klammern nutzt.
|
||||
|
||||
```
|
||||
```Groovy
|
||||
class Person {
|
||||
String name
|
||||
int age
|
||||
|
@ -197,7 +56,7 @@ person.introduce()
|
|||
|
||||
|
||||
### Statische Methoden
|
||||
```
|
||||
```Groovy
|
||||
class MathUtils {
|
||||
// Definition einer statischen Methode
|
||||
static int add(int a, int b) {
|
||||
|
@ -223,7 +82,7 @@ Beispiel
|
|||
Groovy ermöglicht es, Methoden zur Laufzeit hinzuzufügen, was eine hohe Flexibilität bei der Gestaltung
|
||||
von Klassen und deren Verhalten bietet. Diese Fähigkeit ist Teil der dynamischen Natur von Groovy und
|
||||
wird durch die metaClass-Eigenschaft ermöglicht
|
||||
```
|
||||
```Groovy
|
||||
class DynamicExample {}
|
||||
|
||||
// Hinzufügen einer Methode(=closure) zur Laufzeit
|
||||
|
@ -239,7 +98,7 @@ Expando ist eine spezielle Klasse in Groovy, die ermöglicht, Objekten zur Laufz
|
|||
hinzuzufügen. Dadurch ist die vorherige Deklaration der Felder in der Klasse nicht nötig
|
||||
|
||||
Beispiel
|
||||
```
|
||||
```Groovy
|
||||
// Erstellen eines Expando-Objekts
|
||||
def expando = new Expando()
|
||||
|
||||
|
@ -259,7 +118,7 @@ Methodenparameter können Standdardwerte haben (wie in Ruby). Diese werden einge
|
|||
gesetzt werden.
|
||||
|
||||
Beispiel 1
|
||||
```
|
||||
```Groovy
|
||||
class Greeter {
|
||||
void greet(String name = "World") {
|
||||
println "Hello, $name!"
|
||||
|
@ -270,7 +129,7 @@ class Greeter {
|
|||
Ruft man die Methode ohne Parameter auf, werden die Default-Paramter eingesetzt
|
||||
|
||||
Beispiel 2
|
||||
```
|
||||
```Groovy
|
||||
def sum(int a=10, int b=3) {
|
||||
println "Sum is "+(a+b)
|
||||
}
|
||||
|
@ -289,7 +148,7 @@ Closures enthalten Parameter, den Pfeil -> und den auszuführenden Code. Paramet
|
|||
optional und werden, sofern angegeben, durch Kommas getrennt.
|
||||
|
||||
1. Parameter
|
||||
```
|
||||
```Groovy
|
||||
//closure takes one parameter - name - and prints it when invoked
|
||||
|
||||
def greet = { String name -> println "Hello, $name!" }
|
||||
|
@ -302,7 +161,7 @@ zugreifen und diese beibehalten können. Somit wird es Closures ermöglicht, Zus
|
|||
zwischen verschiedenen Aufrufen beizubehalten.
|
||||
|
||||
|
||||
```
|
||||
```Groovy
|
||||
def createCounter() {
|
||||
def count = 0 //lokale var
|
||||
return { ->
|
||||
|
@ -323,7 +182,7 @@ println(counter()) // Ausgabe: 2
|
|||
|
||||
|
||||
3. Übergabe als Parameter
|
||||
```
|
||||
```Groovy
|
||||
def performOperation(int x, Closure operation) {
|
||||
return operation(x)
|
||||
}
|
||||
|
@ -337,7 +196,7 @@ println(result) // Ausgabe: 10
|
|||
|
||||
Eine Closure kann sowohl als eine reguläre Methode als auch mit call aufrufen werden
|
||||
|
||||
```
|
||||
```Groovy
|
||||
// Closure-Definition
|
||||
def greet = { name ->
|
||||
return "Hello, ${name}!"
|
||||
|
@ -354,7 +213,7 @@ println(greet.call("Bob")) // Ausgabe: Hello, Bob!
|
|||
Methoden können auch auf Maps und Listen angewendet werden, besonders nützlich mit Closures.
|
||||
|
||||
|
||||
```
|
||||
```Groovy
|
||||
def myMap = [ 'subject': 'groovy', 'topic': 'closures']
|
||||
println myMap.each { it }
|
||||
|
||||
|
@ -383,7 +242,7 @@ selbst (normalerweise mit this) zurückgegeben wird.
|
|||
Methoden denselben oder ähnlichen Kontext haben. (wie in JS, )
|
||||
|
||||
Beispiel
|
||||
```
|
||||
```Groovy
|
||||
class FluentPerson {
|
||||
String name
|
||||
int age
|
||||
|
@ -415,7 +274,7 @@ Man kann Funktionalität zu Klassen hinzufügen, ohne Vererbung zu verwenden, in
|
|||
Mixins verwendet.
|
||||
|
||||
Beispiel
|
||||
```
|
||||
```Groovy
|
||||
class ExtraMethods {
|
||||
String shout(String str) {
|
||||
return str.toUpperCase()
|
|
@ -0,0 +1,146 @@
|
|||
# Operatoren
|
||||
## Grundlegende Operatoren
|
||||
Grundsätzlich bietet Groovy dieselben Operatoren an, die es auch in Java gibt. Groovy erweiteret die Menge an Operatoren, die Java einem zur Verfügung stellt jedoch nochmal um einige mehr, auf hier eingegangen wird. Zu den grundlegenden Operatoren gehören:
|
||||
|
||||
- Arithmetische Operatoren (-, +, *, **,…)
|
||||
- Relationale Operatoren (==, !=, <, >,…)
|
||||
- Logische Operatoren (&&, ||, !)
|
||||
- Bitweise Operatoren (&, |, ^, ~)
|
||||
- Bitweise Verschiebung (<<, >>,>>>)
|
||||
- Konditionale Operatoren (!, ? :, ?: )
|
||||
|
||||
Wie bereits erwähnt, gibt es die meisten dieser Operatoren schon in Java. Deshalb setzen wir diese an dieser Stelle als bereits bekannt voraus. Auf die bisher unbekannten Operatoren wollen wir jedoch etwas genauer eingehen.
|
||||
|
||||
---
|
||||
### 1. Exponentialoperator (**)
|
||||
Dies ist ein relativ simpler Operator. Er wird verwendet, um eine gegebene Zahl auf eine bestimmte Potenz zu erhöhen:
|
||||
````Groovy
|
||||
def a = 2
|
||||
def b = 3
|
||||
println a**b // Ausgabe: 8 (Weil 2^3 = 8)
|
||||
````
|
||||
|
||||
### 2. SicherheitsnavigationsOperator (?.):
|
||||
Der Sicherheitsnavigationsoperator wird verwendet, um null-sichere Zugriff auf Objekteigenschaften, Methoden oder Array-Elemente zu ermöglichen. Ein bestimmter Ausdruck wird also nur ausgeführt, wenn das Objekt, auf das gerade zugegriffen wird nicht null ist. Somit wird eine NullPointerException vermieden. Ist das Objekt nämlich null, wird einfach null zurückgegeben und keine Exception geworfen.
|
||||
- Verwendung: **< Objekt >?.< Eigenschaft >**
|
||||
|
||||
````Groovy
|
||||
class Person {
|
||||
String name
|
||||
}
|
||||
Person person = null
|
||||
def name = person?.name
|
||||
println name // Ausgabe: null
|
||||
````
|
||||
|
||||
### 3. Methodenreferenzoperator (.&):
|
||||
Der Methodenreferenzoperator wird verwendet, um eine Methode als Closure zu referenzieren. Auf Closures wird später im Skript nochmal eingegangen. Dieser Operator ermöglicht es dem Programmierer Methoden als Objekte zu behandeln, die später wieder aufgerufen werden können. Dieses Feature ist mit Lambdas in Java vergleichbar.
|
||||
Verwendung: **< Methode > { < Objekt >.&< Methode >()}**
|
||||
|
||||
````Groovy
|
||||
class Person {
|
||||
def name
|
||||
}
|
||||
|
||||
def getName(){
|
||||
name
|
||||
}
|
||||
|
||||
def personen = [new Person(name: "Tom"), new Person(name: "Max"), new Person(name: "Tim")]
|
||||
def names = personen.collect {it.&getName()} //Ausgabe: [Tom, Max, Tim]
|
||||
````
|
||||
|
||||
### 4. Spread-Operator(*.):
|
||||
Der Spread Operator wird verwendet, um eine Methode auf alle Elemente einer Colletion anzuwenden.
|
||||
- Verwendung:
|
||||
**< Collection >*.< Methode >**
|
||||
|
||||
````Groovy
|
||||
class Person {
|
||||
def name
|
||||
}
|
||||
|
||||
def getName(){
|
||||
name
|
||||
}
|
||||
|
||||
def personen = [new Person(name: "Tom"), new Person(name: "Max"), new Person(name: "Tim")]
|
||||
def names = personen*.getName()
|
||||
println names //Ausgabe: [Tom, Max, Tim]
|
||||
````
|
||||
|
||||
### 5. Spread-Map-Operator(*: )
|
||||
Ermöglicht es die Inhalte einer Map in eine andere zu kopieren. Gibt es in der Map, in die hineinkopiert werden soll einen bestimmten Key schon, dann wird der Wert an der Stelle einfach ersetzt.
|
||||
Verwendung: **def map2 = [< Element1 >, < Element2 >,… \*:< map1 >]**
|
||||
|
||||
````Groovy
|
||||
def map1: = [a: 1, b:3]
|
||||
def map2= [c: 4, b: 2, *:map1]
|
||||
println map2 //Ausgabe: [c:4, b:3, a:1]
|
||||
````
|
||||
|
||||
### 6. Raumschiff-Operator (<=>):
|
||||
Der Raumschiffoperator ist ähnlich zur compare() Methode in Java und wird verwendet, um zwei Werte miteinander zu vergleichen.
|
||||
Verwendung: **< Wert1 > <=> < Wert2 >**
|
||||
|
||||
- Gilt Wert1 < Wert2: Rückgabewert: -1
|
||||
- Gilt Wert1 > Wert2: Rückgabewert: 1
|
||||
- Gilt Wert1 == Wert2: Rückgabewert: 0
|
||||
|
||||
````Groovy
|
||||
println 3 <=> 5 // Ausgabe: -1
|
||||
println 5 <=> 5 // Ausgabe: 0
|
||||
println 7 <=> 5 // Ausgabe: 1
|
||||
````
|
||||
|
||||
### 7. Range-Operator(..)
|
||||
Der Range Operator wird in Groovy verwendet, um eine Sequenz von Werten (Ganzzahlen/Chars) innerhalb eines bestimmten Bereiches zu erstellen. Hierbei gilt: Beide Werte sind inklusiv. Will man jedoch den rechten Wert ausschließen, muss man den Range-Exclusive-Operator (..<) verwenden. Es ist auch möglich innerhalb einer Range Sprünge in einem bestimmten Intervall zu machen. Dies erreicht man mit der **(< range >).step(n)**-Methode.
|
||||
- Verwendung **< Wert1 >..< Wert2 >**
|
||||
|
||||
````Groovy
|
||||
def range = 1..5
|
||||
println range.collect() // Ausgabe: [1, 2, 3, 4, 5]
|
||||
|
||||
def range2 = 1..<5
|
||||
println range2.collect() // Ausgabe: [1, 2, 3, 4]
|
||||
|
||||
def range3 = (1..10).step 2
|
||||
println range3.collect() // Ausgabe: [1, 3, 5, 7, 9]
|
||||
````
|
||||
|
||||
### 8. Regex-Operatoren (=\~) / (==\~):
|
||||
Die Regex-Operatoren werden verwendet, um zu prüfen, ob ein String mit einem Regulären Ausdruck (*kurz regex*) übereinstimmt.
|
||||
- **(\=~)**: Prüft, ob ein Teil des Strings mit dem regulären Ausdruck übereinstimmt.
|
||||
- **(==\~)**: Prüft, ob der gesamte String mit dem regulären Ausdruck übereinstimmt.
|
||||
- **(\~/String/)**: Wird zum definieren ddes regulären Ausdrucks verwendet
|
||||
|
||||
- Verwendung: **< String > ~= < Muster >**
|
||||
|
||||
````Groovy
|
||||
def text = "Groovy ist toll!"
|
||||
def pattern = ~/Groovy/
|
||||
|
||||
if (text =~ pattern) {
|
||||
println "Der Text entält das Muster."
|
||||
}
|
||||
else {
|
||||
println "Der Text enthält das Muster nicht."
|
||||
}
|
||||
// Ausgabe: Der Text enthält das Muster.
|
||||
````
|
||||
|
||||
Würde man jedoch den **(==\~)**-Operator benutzen, würde die Ausgabe wie folgt aussehen:
|
||||
|
||||
````Groovy
|
||||
def text = "Groovy ist toll!"
|
||||
def pattern = ~/Groovy/
|
||||
|
||||
if (text ==~ pattern) {
|
||||
println "Der Text entält das Muster."
|
||||
}
|
||||
else {
|
||||
println "Der Text enthält das Muster nicht."
|
||||
}
|
||||
// Ausgabe: Der Text enthält das Muster nicht.
|
||||
````
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
# Schleifen
|
||||
## While-Schleife
|
||||
Syntax Kopfgesteuerte `while-Schleife`
|
||||
|
||||
```Groovy
|
||||
def y = 0
|
||||
while (y < 5){
|
||||
print(y + " ") // Ausgabe: 1 2 3 4
|
||||
y++
|
||||
}
|
||||
```
|
||||
|
||||
## for-Schleife
|
||||
Syntax der `for-Schleife` wie in Java
|
||||
|
||||
```Groovy
|
||||
for (int i = 0; i < 5; i++){
|
||||
print(i + " ") // Ausgabe: 1 2 3 4
|
||||
}
|
||||
```
|
||||
|
||||
## times()-Schleife:
|
||||
In Groovy besitzen Zahlen die Methode `n.times()`. Sie bekommt ein Closure als Parameter und führt die übergebene Methode **n-mal** aus. Man kann den Variablen innerhalb der Closure Namen geben, kann es jedoch auch sein lassen. Gibt man den Variablen keinen Namen, wird der Name *it* benutzt.
|
||||
````Groovy
|
||||
5.times {def e -> print e + " "} // Ausgabe: 0 1 2 3 4
|
||||
5.times {println it + " "} // Ausgabe: 0 1 2 3 4
|
||||
````
|
||||
|
||||
## each()-Schleife:
|
||||
Man kann über Elemente von Collections iterieren, indem man die collection `.each()` Methode benutzt. Sie bekommt ein Closure als Parameter und wendet den Ausdruck auf alle Elemente der Liste an.
|
||||
|
||||
````Groovy
|
||||
def map = [name: "Thomas", nachname: "Smits"]
|
||||
map.each {key, value -> println "$key: $value"} // Ausgabe: name: Thomas nachname: Smits
|
||||
````
|
||||
|
||||
## for-in Schleife
|
||||
```Groovy
|
||||
for (variable in iterable) {body}
|
||||
```
|
||||
|
||||
Die `for-in Schleife` folgt dieser einfachen Struktur. Sie durchläuft das Objekt `iterable`. Häufig verwendete Iterables sind Ranges, Collections, Maps, Arrays, Iterators und Enumerationen. Jedes Objekt kann ein Groovy ein Iterable sein. Klammern um den Body sind optinal, wenn er nur aus einer Anweisung besteht.
|
||||
Im Folgenden Beispiele für Iterationen:
|
||||
|
||||
### Über Ranges:
|
||||
```Groovy
|
||||
for (i in 0..5){
|
||||
print(i + " ") // Ausgabe: 1 2 3 4 5
|
||||
}
|
||||
for (i in 'a'..<'d'){
|
||||
print(i + " ") // Ausgabe: letzter Buchstabe exklusiv
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Über eine Liste:
|
||||
```Groovy
|
||||
def list = [1, 2, 3, 4, 5]
|
||||
for (element in list){
|
||||
print element + " " // Ausgabe: 1 2 3 4 5
|
||||
}
|
||||
```
|
||||
|
||||
### über eine Array:
|
||||
```Groovy
|
||||
def arr = ['a', 'b', 'c']
|
||||
for (ch in arr){
|
||||
print (ch + " ") // Ausgabe: a b c
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Über eine Map:
|
||||
```Groovy
|
||||
def map = [name: "Alice", age: 18]
|
||||
for (entry in map){
|
||||
print(${entry.key}: ${entry.value}) // Ausgabe: name: Alice age: 18
|
||||
}
|
||||
```
|
||||
|
||||
### Über eine Zeichenfolge:
|
||||
```Groovy
|
||||
def text = "Groovy"
|
||||
for (ch in text){
|
||||
print(ch + " ") // Ausgabe: G r o o v y
|
||||
}
|
||||
```
|
|
@ -0,0 +1,60 @@
|
|||
|
||||
## Tests
|
||||
##### Power Assertions
|
||||
Genau wie in Java, gibt es in Groovy auch das Schlüsselwort `assert`. Im Gegensatz zu Java sind Assertions in Groovy jedoch standardmäßig aktiviert. Sie erleichtern die Fehlersuche und das Debugging und zeigen eine Übersicht des Ausdrucks mitsamt der **aktuellen** Variablenwerten. Der Entwickler sieht also genau, welcher Teil des Ausdrucks das Problem verursacht hat.
|
||||
|
||||
````Groovy
|
||||
def a = 1
|
||||
def b = 2
|
||||
def c = 3
|
||||
|
||||
// Ausdruck liefert 9 statt 10
|
||||
assert a + b * c == 10
|
||||
````
|
||||
<img src="./../img/power-assertions.png" width="350">
|
||||
|
||||
Es kann während der Auswertung von Power-Assertions zu inkonsistenten Fehlermeldungen kommen. Das liegt daran, dass bei den Power-Assertions nur die Referenzen auf die Variablen gespeichert. Werden diese Werte also verändert, werden nur die aktuellen Werte angezeigt, da über die Referenz auf die Variable zugegriffen wird.
|
||||
|
||||
````Groovy
|
||||
def getLastAndRemove(list) {
|
||||
return list.remove(list.size() - 1)
|
||||
}
|
||||
def list = [1, 2, 3]
|
||||
assert getLastAndRemove(list) == 4
|
||||
````
|
||||
|
||||
<img src="./../img/assertion2.png" width="350">
|
||||
|
||||
````Groovy
|
||||
assert [[1,2,3,3,3,3,4]].first().unique() == [1,2,3]
|
||||
````
|
||||
<img src="./../img/assertion3.png" width="350">
|
||||
|
||||
---
|
||||
|
||||
#### Spock:
|
||||
- Groovy bietet nicht nur Support für JUnit 5 (und älter), sondern liefert auch einen eigenen Satz an Testmethoden, um die testgetriebene Programmierung zu vereinfachen.
|
||||
|
||||
Ein Beispiel hierfür wäre Spock. Mit Spock kann man sowohl Java, als auch Groovy Code testen. Getestet wird mit sogenannten Spezifikationen. Die Testklasse muss also von `spock.lang.Specification` erben.
|
||||
|
||||
|
||||
````Groovy
|
||||
class StackSpec extends Specification {
|
||||
def "Ein Element zum Stack hinzufuegen führt zur erhoehung von size"() {
|
||||
setup: "Eine neue Stack-Instanz wurde erstellt"
|
||||
def stack = new Stack()
|
||||
when:
|
||||
stack.push 42
|
||||
|
||||
then:
|
||||
stack.size() == 1
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
##### Erklärung:
|
||||
|
||||
- Der Name der Testmethode muss ein String sein, der beschreibt, was beim Testen erwartet wird.
|
||||
- Das Schlüsselwort `setup` dient dazu, lokale Variablen zu initialisieren, die für den Test gebraucht werden (vgl. **@BeforeEach** Annotation, JUnit)
|
||||
3. Das `when` Schlüsselwort beschreibt den Codeabschnitt, der zu einem bestimmten Verhalten führen soll
|
||||
4. `then` beschreibt das erwartete Verhalten.
|
|
@ -1,100 +0,0 @@
|
|||
# Schleifen
|
||||
|
||||
## while-Schleife
|
||||
Syntax Kopfgesteuerte `while-Schleife`
|
||||
|
||||
```
|
||||
class Beispiel {
|
||||
static void main(String[] args){
|
||||
def y = 0
|
||||
while (y < 5){
|
||||
print(y + " ") // -> 1 2 3 4
|
||||
y++
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## for-Schleife
|
||||
Syntax der `for-Schleife` wie in Java
|
||||
|
||||
```
|
||||
class Beispiel {
|
||||
static void main(String[] args){
|
||||
for (int i = 0; i < 5; i++){
|
||||
print(i + " ") // -> 1 2 3 4
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## for-in Schleife
|
||||
```
|
||||
for (variable in iterable) {body}
|
||||
```
|
||||
|
||||
Die `for-in Schleife` folgt dieser einfachen Struktur. Sie durchläuft das Objekt `iterable`. Häufig verwendete Iterables sind Ranges, Collections, Maps, Arrays, Iterators und Enumerationen. Jedes Objekt kann ein Groovy ein Iterable sein. Klammern um den Body sind optinal, wenn er nur aus einer Anweisung besteht.
|
||||
Im Folgenden Beispiele für Iterationen:
|
||||
|
||||
### über Ranges:
|
||||
```
|
||||
class Beispiel {
|
||||
static void main(String[] args){
|
||||
for (i in 0..5){
|
||||
print(i + " ") // -> 1 2 3 4 5
|
||||
}
|
||||
for (i in 'a'..<'d'){
|
||||
print(i + " ") // -> a b c letzter Buchstabe exklusiv
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### über eine Liste:
|
||||
```
|
||||
class Beispiel {
|
||||
static void main(String[] args){
|
||||
def list = [1, 2, 3, 4, 5]
|
||||
for (element in list){
|
||||
print element + " " // -> 1 2 3 4 5
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### über eine Array:
|
||||
```
|
||||
class Beispiel {
|
||||
static void main(String[] args){
|
||||
def arr = ['a', 'b', 'c']
|
||||
for (ch in arr){
|
||||
print (ch + " ") // -> a b c
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### über eine Map:
|
||||
```
|
||||
class Beispiel {
|
||||
static void main(String[] args){
|
||||
def map = [name: "Alice", age: 18]
|
||||
for (entry in map){
|
||||
print(${entry.key}: ${entry.value}) // -> name: Alice age: 18
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### über eine Zeichenfolge:
|
||||
```
|
||||
class Beispiel {
|
||||
static void main(String[] args){
|
||||
def text = "Groovy"
|
||||
for (ch in text){
|
||||
print(ch + " ") // -> G r o o v y
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
Loading…
Reference in New Issue