>  기사  >  Java  >  Java에 비해 Groovy의 기능 및 장점

Java에 비해 Groovy의 기능 및 장점

DDD
DDD원래의
2024-09-18 14:32:06406검색

Features and Advantages of Groovy Over Java

Groovy는 JVM용으로 만들어진 동적 언어입니다. Java 코드 및 라이브러리와 원활하게 통합되어 Java의 장점을 기반으로 구축하는 동시에 Python 및 Ruby와 같은 언어에서 영감을 받은 기능을 추가합니다.
C, C, Python과 같은 독립적인 프로그래밍 언어와 달리 Groovy는 Java에 의존합니다. Java에 도입된 새로운 기능은 Groovy에도 적용됩니다. Groovy의 주요 목표는 반복 작업과 상용구 코드를 줄여서 JVM 스크립팅에 탁월한 선택이 되도록 하는 것입니다.

자바 간의 차이점

강력한 기능으로 잘 알려진 Java는 종종 복잡하고 무거운 프로그래밍 언어로 간주됩니다. 이와 대조적으로 Groovy는 Java 개발에 대한 보다 효율적인 접근 방식을 제공합니다. 정적 및 동적 타이핑 기능을 모두 갖춘 JVM 기반 언어인 Groovy는 코딩의 여러 측면을 단순화합니다.

Java 개발자는 일반적으로 Groovy를 쉽게 익힐 수 있어 기술을 빠르게 향상하고 Java 기능을 확장할 수 있습니다. Groovy의 주목할만한 장점은 단위 테스트를 단순화한다는 평판입니다.

아래 프로그램을 고려하여 Java와 Groovy의 출력 차이를 해석해 보겠습니다.

int method(String arg) {
    return 1;
}
int method(Object arg) {
    return 2;
}
Object o = "Object"; 
int result = method(o);

위 방법은 Java와 Groovy에서 서로 다른 출력을 제공합니다.

산출

Java 출력: 2
그루비 출력: 1

이러한 이유는
Java에서 메서드 오버로드는 인수의 컴파일 타임 유형에 따라 컴파일 타임에 해결됩니다.
그러나 그루비 메서드에서는 런타임에 해결이 발생합니다.

Java와 Groovy의 또 다른 주목할만한 차이점은 다음과 같습니다.

기본 가져오기:
Groovy는 일반적으로 사용되는 패키지에 대한 기본 가져오기 세트를 제공하므로 코드를 단순화하고 명시적인 import 문의 필요성을 줄일 수 있습니다. 예를 들어 Groovy는 java.util, java.io, groovy.lang 등의 패키지에서 클래스를 자동으로 가져옵니다. 이와 대조적으로 Java에서는 필요한 모든 가져오기를 명시적으로 지정해야 합니다.

폐쇄

클로저는 매개변수를 받아들이고 코드를 실행할 수 있는 독립적인 코드 블록입니다. 한 번 작성하고 나중에 사용할 수 있습니다. 표준 메소드나 함수와 달리 클로저는 주변 컨텍스트에서 변수를 캡처하고 사용할 수 있습니다. 기술적으로는 객체와 클로저 사이에 큰 차이가 없지만 클로저는 코드의 가독성과 유연성을 크게 향상시킵니다.

클로저 호출

그루비 클로저에서는 두 가지 방법으로 호출할 수 있습니다.

  • 일반 함수처럼 호출할 수 있습니다.
  • 클로저의 .call() 메소드를 사용합니다.
def sumTwoNums = {
    a,b -> 
    println "Adding ${a} and ${b}"
    return a+b
}

println sumTwoNums(2,4)
println sumTwoNums.call(5,2)

산출

6

def updateCounter = {
    def counter = 0
    return {
        return counter = counter + 1;
    }
}

def updateCounterFunc = updateCounter.call()

println updateCounterFunc()
println updateCounterFunc()

산출

1
2

위 프로그램에서 updateCounter는 지역 변수 counter를 정의하고 또 다른 클로저를 반환합니다. 반환된 클로저는 클로저가 주변 컨텍스트를 캡처하는 방식으로 인해 카운터 변수에 액세스할 수 있으며 이에 대해서는 이 블로그의 뒷부분에서 설명합니다.

updateCounter.call()이 실행되면 카운터를 0으로 초기화하고 호출될 때마다 카운터를 1씩 증가시키는 새 클로저를 반환합니다.

클로저의 특징

그루비의 클로저를 완전히 이해하려면 이것이 무엇인지와 클로저의 소유자를 이해해야 합니다.

이것

groovy에서 이 키워드는 인클로징 클래스를 나타냅니다. 예를 들어, User라는 클래스 내부에 있는 클로저 내에서 이에 액세스하면 이는 User 클래스를 참조하게 됩니다.

다른 클로저에 중첩된 클로저 내부에서 이것을 사용하고 두 클로저가 모두 클래스 내부에 존재하는 경우 이는 가장 가까운 외부 클래스를 참조합니다.

소유자

클로저의 소유자도 이와 유사하지만 소유자 키워드는 둘러싸는 객체 또는 클로저의 클로저를 나타냅니다.

소유자와 클로저에 대한 이해를 명확하게 이해하기 위한 예를 살펴보겠습니다.

class Example{
    def outerClosure = {
        def innerClosure = {
            println "Inner closure ---> $this"
        }
        innerClosure()
        println "Outer closure ---> $this"
    }

    def printCurrentObject(){
        println "Current object ---> $this"
    }
}

Example example = new Example()

example.outerClosure.call()

example.printCurrentObject()

산출

내부 폐쇄 ---> 예시@6e57e95e
외부 폐쇄 ---> 예시@6e57e95e
현재 객체 ---> 예시@6e57e95e

위 중첩 클로저가 선언된 경우 여러 레이어의 클로저가 포함되어 있어도 현재 객체(this)에 대한 참조는 여전히 외부 클래스 인스턴스를 가리킵니다. 이를 통해 중첩된 클로저 내에서 바깥쪽 클래스의 속성과 메서드에 액세스할 수 있습니다.

class Example{
    def outerClosure = {
        def innerClosure = {
            println "Inner closure owner ---> " + getOwner()
        }
        innerClosure()
        println "Outer closure owner ---> " + getOwner()
    }

    def printThis(){
        println "Current object ---> $this"
    }
}

Example example = new Example()

example.outerClosure.call()

example.printThis()

산출

내부 폐쇄 소유자 ---> 예$_closure1@410954b
외부 폐쇄 소유자 ---> 예시@46cc127b
현재 객체 ---> 예시@46cc127b

Inner Closure Owner:
The innerClosure is enclosed within the outerClosure, so getOwner() in the innerClosure returns the outerClosure.

Outer Closure Owner:
The outerClosure itself is enclosed within the instance of the Example class. Therefore, getOwner() in the outerClosure returns the Example class instance

Delegation

In Groovy, delegation is a mechanism that allows an object to pass on method calls to another object (known as the delegate). This concept is particularly useful when working with closures. Groovy provides the delegate property in closures to allow you to specify another object that will handle method calls not defined within the closure.

The below is an example of how delegation works in groovy

class ServiceLogger{
    def log(message){
        println "Service: $message"
    }
}

class DatabaseLogger{
    def log(message){
        println "Database: $message"
    }
}

def logMessage = {
    log(it)
}

logMessage.delegate = new ServiceLogger()     -> 1

logMessage("User created successfully")

logMessage.delegate = new DatabaseLogger()

logMessage("User fetched from DB successfully")

Output

Service: User created successfully
Database: User fetched from DB successfully

In the above example, there are two classes ServiceLogger and DatabaseLogger and a closure named logMessage.

First we are assigning the ServiceLogger as a delegate for the closure. So when the closure logMessage is called then the delegate's (log) function is invoked. Later when we change the delegate of the closure to DatabaseLogger the log method present inside the DatabaseLogger is invoked.

Let's see another example to understand delegation in detail.

class MediaPlayer{
    def fileName
    def play = { "Playing ${fileName}" }
}

class VideoPlayer{
    def fileName
}

MediaPlayer mediaPlayer = new MediaPlayer(fileName:"theme-music.mp3")
VideoPlayer videoPlayer = new VideoPlayer(fileName:"trailer.mp4")

println mediaPlayer.play()

mediaPlayer.play.delegate = videoPlayer

println mediaPlayer.play()

Initially, when mediaPlayer.play() is called, it uses the fileNamefrom the MediaPlayer instance, resulting in the output: Playing theme-music.mp3. Even after changing the delegate of the closure to VideoPlayer, the play closure still prints the MediaPlayer file name. This behaviour is due to the default delegation strategy in Groovy closures. To understand this, let's explore the different delegation strategies in Groovy closures.

Delegation Strategy

In Groovy, the delegation strategy defines how a closure resolves method calls or property references that are not explicitly defined within the closure itself.

Groovy provides several delegation strategies that dictate how a closure resolves calls to methods or properties:

Closure.OWNER_FIRST (default): The closure first tries to resolve the call in the owner, then the delegate.
Closure.DELEGATE_FIRST: The closure first looks for the method/property in the delegate, and if not found, it checks in the owner.
Closure.OWNER_ONLY: The closure only looks for method/property in the owner and ignores the delegate.
Closure.DELEGATE_ONLY: The closure only resolves method/property in the delegate and ignores the owner.

So in the above program though we have changed the delegate of the closure while executing the closure will use its owner's methods or properties. Here the owner of the closure play is MediaPlayer.

class MediaPlayer{
    def fileName
    def play = { "Playing ${fileName}" }
}

class VideoPlayer{
    def fileName
}

MediaPlayer mediaPlayer = new MediaPlayer(fileName:"theme-music.mp3")
VideoPlayer videoPlayer = new VideoPlayer(fileName:"trailer.mp4")

println mediaPlayer.play()

mediaPlayer.play.resolveStrategy = Closure.DELEGATE_FIRST
mediaPlayer.play.delegate = videoPlayer

println mediaPlayer.play()

Output

Playing theme-music.mp3
Playing trailer.mp4

Here we have changed the resolution strategy of the closure to DELEGATE_FIRST, now the closure uses the delegate's methods and properties.

Another advantage of using Closures is lazy evaluation of strings. Here is an example

def name = "Walter"
def greetingMsg = "Welcome! ${name}"

name = "White"

println greetingMsg

Output

Welcome! Walter

def name = "Walter"
def greetingMsg = "Welcome! ${->name}"

name = "White"

println greetingMsg

Output

Welcome! White

In the first script, the GString ${name} is evaluated when greetingMsg is defined, capturing the value of name at that moment, which is "Walter".

In the second script, the GString ${->name} uses a closure. The closure is evaluated at the moment of printing, not when greetingMsg is defined. Since name has changed to "White" by the time the closure is executed, it reflects the updated value.

Currying in Groovy

Currying in Groovy is a technique that allows you to create a new closure by pre-filling some of the parameters of an existing closure. This effectively reduces the number of arguments needed to call the new closure, making it a convenient way to create specialized functions from a general one.

def getUsers = { groupId, role, status ->
    // Simulate fetching users from a database or API
    println "Fetching users from group ${groupId} with role ${role} and status ${status}"
}

def getHRUsers = getUsers.curry("HR")

getHRUsers("Admin", "Active")
getHRUsers("Viewer", "Inactive")

def getActiveUsers = getUsers.rcurry("ACTIVE")

getActiveUsers("Development", "Tester")

def getAllDevelopers = getUsers.ncurry(1, "Developer")

getAllDevelopers("IT", "Active")
getAllDevelopers("Marketing", "Suspended")

Output

Fetching users from group HR with role Admin and status Active
Fetching users from group HR with role Viewer and status Inactive
Fetching users from group Development with role Tester and status ACTIVE
Fetching users from group IT with role Developer and status Active
Fetching users from group Marketing with role Developer and status Suspended

In Groovy, there are three currying methods used to partially apply parameters to closures:

curry(): Fixes the leftmost parameters of a closure.

Example: closure.curry(value1) fixes value1 for the first parameter.
rcurry(): Fixes the rightmost parameters of a closure.

Example: closure.rcurry(valueN) fixes valueN for the last parameter.
ncurry(): Fixes parameters at a specific index in the closure.

Example: closure.ncurry(index, value) fixes the parameter at the given index.
These methods simplify repeated calls by pre-filling some arguments, improving code readability and maintainability.

Metaprogramming

In simple terms metaprogramming refers to writing code that can create, modify, generate and analyze other programs.It accepts other codes as its data and do some operations with it.

A best example for metaprogamming is the eval function in JavaScript, which accepts a string of JavaScript code and executes it.

Metaprograms are frequently used in everyday applications. For instance, integrated development environments (IDEs) like Visual Studio Code and IntelliJ IDEA use metaprogramming techniques to analyze code for syntax or compile-time errors even before the code is executed. The IDEs essentially act as programs that process and check the user's code for errors before runtime.

Metaprogramming in Groovy

Groovy supports two types of metaprogramming:

Runtime Metaprogramming:

Runtime metaprogramming in Groovy allows us to modify or extend the behaviour of classes and objects dynamically at runtime.

In Groovy, the invokeMethod() is a special method available in Groovy objects that is triggered when an undefined method is called on an object. By overriding invokeMethod(), we can customize how undefined method calls are handled.

Additionally, Groovy provides getProperty() and setProperty() methods. These methods intercept operations related to getting or setting properties in a class, allowing you to implement custom logic, such as validation, before retrieving or modifying property values.

class User{
    def name
    def age
    def email

    void setProperty(String name, Object value){
        if (value == null){
            value = ""
        }
        this.@"$name" = value.toString()
    }

    void print(){
        println "Name : ${name}, age : ${age}, email : ${email}"
    }
}

User user = new User()

user.name = "arun"
user.age = 2
user.email = null

user.print()

Output

Name : arun, age : 2, email :

Command Chains

Command chains in Groovy offer a concise and expressive way to call methods without using parentheses or dots (.) between method calls. This feature can make your code more readable, especially when you want to write more fluid and natural-looking expressions.

A command chain lets you call methods as if you were writing a sentence. Let’s look at an example:

class Car {
    def start() {
        println "Car started"
        return this
    }

    def drive() {
        println "Driving"
        return this
    }

    def stop() {
        println "Car stopped"
        return this
    }
}

def car = new Car()
car start drive stop

Output

Car started
Driving
Car stopped

In the above example, car start drive stop demonstrates a command chain where methods are called in sequence. Each method returns this, allowing the next method in the chain to be called on the same Car object.

In this blog, we explored various features of Groovy, from its metaprogramming capabilities and dynamic method handling to expressive features like command chains and delegation strategies. Groovy's ability to enhance code readability and flexibility through dynamic behaviour makes it a powerful tool for developers. By understanding and leveraging these features, you can write more intuitive and maintainable code.

If you have any questions, suggestions, or additional insights about Groovy or any other programming topics, please share your feedback in the comments below ?.

위 내용은 Java에 비해 Groovy의 기능 및 장점의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.