groovy-lecture/yuliya/klassen&methoden.md

9.9 KiB

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
    }
}

In Groovy können Methoden direkt ohne Klasse und main-Methode definiert und aufgerufen werden.

Beispiel 1

def printHello() {
println "Hello..."
}

printHello()

def sum(int a, int b) {
println "Sum is "+(a+b)
}

sum(5,2)
//Hello...
// 7

Instanzmethoden

Methoden können Instanzmethoden sein und auf Instanzvariablen zugreifen, indem man $-Zeichen und optional eckige Klammern nutzt.

class Person {
    String name
    int age

    // Instanzmethode, um die Person vorzustellen
    def introduce() {
        println("Hello, my name is $name and I am ${age} years old.")
    }
}

Person person = new Person(name: 'Alice', age: 30)
person.introduce()
// Hello, my name is Alice and I am 30 years old.

Statische Methoden

class MathUtils {
    // Definition einer statischen Methode
    static int add(int a, int b) {
        return a + b
    }
    
    static void main(String[] args) {
        // Aufruf der statischen Methode ohne Instanz der Klasse
        int result = MathUtils.add(5, 10)
        println("Sum is $result")  // Ausgabe: Sum is 15
    }
}

Dynamische Methoden

Groovy erlaubt es, Methoden zur Laufzeit hinzuzufügen. In diesem Beispiel wird die Methode sayHello zur Klasse DynamicExample hinzugefügt, nachdem die Klasse bereits definiert wurde

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

class DynamicExample {}

// Hinzufügen einer Methode(=closure) zur Laufzeit
DynamicExample.metaClass.sayHello = { -> println "Hello, World!" }

def example = new DynamicExample()
example.sayHello()

Expando

Expando ist eine spezielle Klasse in Groovy, die ermöglicht, Objekten zur Laufzeit dynamisch Methoden und Eigenschaften hinzuzufügen. Dadurch ist die vorherige Deklaration der Felder in der Klasse nicht nötig

Beispiel

// Erstellen eines Expando-Objekts
def expando = new Expando()

// Hinzufügen einer Eigenschaft 'name' und Zuweisung des Wertes "Groovy"
expando.name = "Groovy"

// Hinzufügen einer Methode 'sayHello', die eine Nachricht ausgibt, die die Eigenschaft 'name' verwendet
expando.sayHello = { -> println "Hello from $name" }

// Aufrufen der Methode 'sayHello', was die Nachricht "Hello from Groovy" ausgibt
expando.sayHello()  // Ausgabe: Hello from Groovy

Default-Parameter

Methodenparameter können Standdardwerte haben (wie in Ruby). Diese werden eingesetzt, falls beim Aufruf keine Parameter gesetzt werden.

Beispiel 1

class Greeter {
    void greet(String name = "World") {
        println "Hello, $name!"
    }
}

Ruft man die Methode ohne Parameter auf, werden die Default-Paramter eingesetzt

Beispiel 2

def sum(int a=10, int b=3) {
  println "Sum is "+(a+b)
}

sum()
// Sum is 13

Closures

Closures können auf Variablen aus ihrem umgebenden Gültigkeitsbereich zugreifen und diese „einfangen“. Dadurch können sie auf Werte zugreifen, die zum Zeitpunkt ihrer Erstellung existierten. Closures enthalten Parameter, den Pfeil -> und den auszuführenden Code. Parameter sind optional und werden, sofern angegeben, durch Kommas getrennt.

  1. Parameter
//closure takes one parameter - name - and prints it when invoked

def greet = { String name -> println "Hello, $name!" }
greet.call("John")
  1. Referenzierung von Variablen und Rückgabewerte Dieses Beispiel zeigt, wie Closures auf Variablen im auf Variablen im umgebenden Kontext zugreifen und diese beibehalten können. Somit wird es Closures ermöglicht, Zustände zwischen verschiedenen Aufrufen beizubehalten.
def createCounter() {
  def count = 0  //lokale var
  return { ->
      count += 1
      return count
  }
}

//Ergebnis bzw. closure wird counter zugewiesen
def counter = createCounter() 

//counter hat zugriff auf count *innerhalb des Kontexts*
//in dem sie erstellt wurde

println(counter())  // Ausgabe: 1
println(counter())  // Ausgabe: 2
  1. Übergabe als Parameter
def performOperation(int x, Closure operation) {
  return operation(x)
}

def closure = { y -> y * 2 }
def result = performOperation(5, closure)
println(result)  // Ausgabe: 10

Wie ruft man eine Closure auf

Eine Closure kann sowohl als eine reguläre Methode als auch mit call aufrufen werden

// Closure-Definition
def greet = { name ->
    return "Hello, ${name}!"
}

// Aufruf der Closure als reguläre Methode
println(greet("Alice")) // Ausgabe: Hello, Alice!

// Aufruf der Closure mit call
println(greet.call("Bob")) // Ausgabe: Hello, Bob!

Methoden können auch auf Maps und Listen angewendet werden, besonders nützlich mit Closures.

def myMap = [ 'subject': 'groovy', 'topic': 'closures']
println myMap.each { it }

def myList = [1, 2, 3, 4, 5]
println myList.find { item -> item == 3 }  // 3
println myList.findAll { item -> item > 3 }  // [4, 5]
println myList.any { item -> item > 5 }  // false
println myList.every { item -> item > 3 }  // false
println myList.collect { item -> item * 2 }  // [2, 4, 6, 8, 10]

Im Gegensatz zu einer regulären Groovy-Methode:

  • Wir können eine Closure als Argument an eine Methode übergeben
  • Wir können eine Closure einer Variablen zuweisen und später ausführen, entweder als Methode oder mit call.
  • Groovy bestimmt den Rückgabewert der Closures zur Laufzeit.
  • Wir können Closures innerhalb einer Closure deklarieren und aufrufen.
  • Closures geben immer einen Wert zurück

Methodenverkettung

Methodenverkettung ermöglicht,dass Methodenaufrufe direkt nacheinander aufeinanderfolgen können, indem das Objekt selbst (normalerweise mit this) zurückgegeben wird. Dies ermöglicht eine flüssige und verständliche Art, Methoden aufzurufen und zu kombinieren, insbesondere wenn diese Methoden denselben oder ähnlichen Kontext haben. (wie in JS, )

Beispiel

class FluentPerson {
  String name
  int age
  
  FluentPerson setName(String name) {
      this.name = name
      return this
  }
  
  FluentPerson setAge(int age) {
      this.age = age
      return this
  }
}

// Erstellen einer neuen FluentPerson-Instanz und Methodenverkettung
def person = new FluentPerson()
  .setName("Alice")
  .setAge(30)

println "Name: ${person.name}, Age: ${person.age}" // Ausgabe: Name: Alice, Age: 30

Mixin(Misching)

Man kann Funktionalität zu Klassen hinzufügen, ohne Vererbung zu verwenden, indem man Mixins verwendet.

Beispiel

class ExtraMethods {
  String shout(String str) {
      return str.toUpperCase()
  }
}

@Mixin(ExtraMethods)
class MyClass {}

def myObject = new MyClass()
println myObject.shout("hello")