'2016/09'에 해당되는 글 10건

  1. 2016.09.09 Domain-Specific Language
  2. 2016.09.09 DSL(Domain Specific Language) 이해하기
  3. 2016.09.09 particular, specific, certain
  4. 2016.09.09 자바 람다식(Lambda Expressions in Java)
  5. 2016.09.09 Metaprogramming
  6. 2016.09.09 그루비의 동적 객체지향 - 1
  7. 2016.09.08 그루비의 동적 객체지향 - 2
  8. 2016.09.08 What does .delegate mean in groovy?
  9. 2016.09.08 ExpandoMetaClass
  10. 2016.09.05 Groovy - 문법 (Java 와 비교해서 다른 점 위주로)

Domain-Specific Language

Language/Groovy 2016. 9. 9. 15:35

Domain-Specific Language(DSL)은 특정 도메인에 특화된 비교적 작고 간단한 프로그래밍 언어이다. 도메인 문제를 해결 할 때, 문제 해결 아이디어와 최대한 유사한 형태의 문법으로 프로그래밍 할 수 있도록 하는 것이 주요 컨셉트이다.

DSL의 반대 개념은 General Purpose Language(GPL)이며 Java나 C++같은 보편적인 언어를 지칭한다. GPL로는 컴퓨터로 해결 가능한 모든 종류의 문제를 풀 수 있지만 그것이 항상 최선의 방법은 아니며 DSL 바로 그 특정한 종류(도메인 영역)의 문제를 해결하기에 더 적합한 언어가 되는 것을 그 목적으로 한다.

이클립스에서는 Xtext를 이용하여 DSL을 정의하고 에디터 및 DSL 기반 인터프리터 또는 제네레이터를 공급할 수 있다. GPL 중에서도 Java Script등과 같은 다이니믹 타이핑 언어등을 개발할 때에는 DLTK를 이용할 수 있다.


출처 - http://eclipse.or.kr/wiki/Domain-Specific_Language

'Language > Groovy' 카테고리의 다른 글

DSL(Domain Specific Language) 이해하기  (0) 2016.09.09
Metaprogramming  (0) 2016.09.09
그루비의 동적 객체지향 - 1  (0) 2016.09.09
그루비의 동적 객체지향 - 2  (0) 2016.09.08
What does .delegate mean in groovy?  (0) 2016.09.08
:

DSL(Domain Specific Language) 이해하기

Language/Groovy 2016. 9. 9. 15:34

DSL(Domain Specific Language) 이해하기

1. DSL이란
특정 도메인(산업, 분야등 특정 영역)에 특화된 언어를 말한다. 
"문제 영역의 해결에는 그 영역의 언어를 전제로 둬야하며, 거기에서 프로그래밍 솔루션을 꺼내는 것이 중요하다." 라고 Dave Thomas가 한 말을 생각하면 이해하기 쉽다.

특정 영역의 문제 해결에는 그 영역에 맞는 특화된 도구를 사용하자라는 것이다. 어찌보면 과도로 끝내도 될 일을 맥가이버칼을 들이대는 격이다. 그리고 표현 방식은 해당 도메인의 전문가가 이해할 수 있는 형태(고급 언어)여야 한다.

실제로 Ruby처럼 그 코드가 일반 자연어를 읽는 것과 같이 쉽게 이해되기 때문에 도메인 전문가와 프로그래머가 아이디어를 공유하기 좋고 거기에 Lisp 계열이어서 DSL에 많이 활용된다.

그리고 DSL은 개발 생산성과 도메인 전문가와 커뮤니케이션을 원할하게 하기 위해서 도입되는 경향이 많지만, No Silver Bullet.

참고로 UNIX에서도 특정한 응용 영역의 문제를 해결하기 위해 그 영역에만 적용할 수 있는 특수한 언어를 만들어 문제를 해결하는 오랜 전통이 존재한다. 이런 언어를 "작은 언어(little language)", 또는 "미니 언어(mini language)"라고 부르는데 DSL도 이와 유사하다고 볼 수 있다.

2. 내부 DSL과 외부 DSL
2.1 내부 DSL
- 호스트 언어 구문을 이용하여 자체적으로 의존하는 무언가를 만드는 경우에 해당된다.
- 내부 DSL에서는 API와 DSL의 경계가 모호해 비슷하게 생각하는 경향이 있다. 
    . 좀 더 일반 사용자가 알아보기 쉬운 API가 내부 DSL로 생각해도 될 듯 하다.
- 호스트 언어 능력과 지금까지 사용하던 도구를 그대로 사용할 수 있다는 점, 처리 결과를 쉽게 예측할 수 있어서 해당 언어를 잘 알면 친근할 수 있다.
- 형태
    . 메타 프로그래밍의 형태로 언어에 미니 언어를 만들 수 있다. 
    . 원래 언어로 새로운 구문으로 도입 된다. 그래서 언어 확장을 일으켜 다른 언어가 된다.
    . 인라인 코드 형태로 표현될 수도 있다.
- 적합한 언어 : Lisp, Ruby, Smalltalk

2.2 외부 DSL
- 호스트 언어와 다른 언어 (XML, Makefile과 같은 고유 형식)에서 생성된 DSL.
- GUI 도구를 제공해 주는 것이 특징.
- 외부 DSL에서는 DSL과 범용 언어(GPL : General Purpose Language)과의 경계가 모호해지는 경향이 있다.
    . 그 차이는 언어 작성자와 언어 사용자의 목적에 있다. 특정 영역에서 언어의 작성자가 아닌 사용자의 목적에 부합하는, 이해를 할 수 있으면 외부 DSL이다.
- 외부 DSL 개발자가 자유롭게 DSL의 형식을 결정할 수 있다.
- 형태
    . 실행 파일에서 DSL 을 동적 로딩할 수 있다.
    . DSL 컴파일러를 만들어서 표현할 수 있다.
    . DSL을 범용 언어로 코드로 변환한다.
- 적합한 언어 : Java, C#, C++

3. DSL의 장점과 단점
3.1 장점
- 반복이 제거되고 비슷한 처리 코드는 자동 생성(템플릿) 된다.
- 프로그래밍 코드의 양이 적고 가독성이 높다.
- 특정 프로그래머(lay programer - martin fowler)들과 커뮤니케이션이 쉽다.
    . XML, CSS, SQL 등

3.2 단점
- 설계가 어렵다.
- 잘 설계가 되지 않는다면 읽기 어려운 코드가 될 수 있다.
- 하위 호환성을 유지해야 한다.

4. 우리 주변에 있는 DSL
- java 
    . ANT, Maven, struts-config.xml, Seasar2 S2DAO, HQL(Hibernate Query Language), JMock
- Ruby
    . Rails Validations, Rails ActiveRecord, Rake, RSpec, Capistrano, Cucumber
- 기타
    . SQL, CSS, Regular Expression(정규식), Make, graphviz


[참조 사이트]
출처 - http://www.mimul.com/pebble/default/2013/06/21/1371806174467.html


'Language > Groovy' 카테고리의 다른 글

Domain-Specific Language  (0) 2016.09.09
Metaprogramming  (0) 2016.09.09
그루비의 동적 객체지향 - 1  (0) 2016.09.09
그루비의 동적 객체지향 - 2  (0) 2016.09.08
What does .delegate mean in groovy?  (0) 2016.09.08
:

particular, specific, certain

etc 2016. 9. 9. 15:31

particularspecificcertain의 차이점은 뭔가요?


=>particular : '한정적'으로 명사 앞에 쓰여 '특별한'이라는 의미를 나타냅니다.
                      (ex)We must pay particular attention to this point.
(우리는 이 점에 특별한 관심을 기울여야 한다.)
                     
                      '서술적'으로 쓰여 '까다로운'이라는 의미를 나타냅니다.
                       (ex)She is very particular about her clothes.
(그녀는 옷에 대해 매우 까다롭다.)
 
 
   specific : '한정적'으로 명사 앞에 쓰여 '구체적인(=명확한)', '특정한'이라는 의미입니다.
                   (ex)I gave you specific instructions.
(난 네게명확한 지시를 내렸다.)
                         There are a lot of children with specific learning difficulties.
 
                  '서술적'으로 쓰여 '특유의(=독특한)'이라는 의미입니다.
                   (ex)It is a belief that is specific to this part of Africa.
                         (그것은 아프리카 이 지역 특유의 신앙이다.)
 
 
   certain : '한정적'으로 명사 앞에 쓰여 불특정한 사람이나 사물을 나타내어
                 '어떤', '무슨'이라는 의미입니다.
                  (ex)Certain people might disagree with this.
                        (어떤 사람들은 이것에 동의하지 않을 것이다.)
 
                 '서술적'으로 쓰여 '확실한(=틀림없는)', '확신하는'이라는 의미입니다.
                  (ex)It is certain that he will agree.
                        (그가 동의하리라는 것은 확실하다.)
                        I'm not certain who was there.
                        (거기에 누가 있었는지 확신이 서지 않는다.)


'etc' 카테고리의 다른 글

부동 소수점 설명  (0) 2017.01.18
부동 소수점의 이해  (0) 2017.01.18
폰트, BitstreamVeraSansMono  (0) 2015.05.11
받히다, 받치다, 바치다  (0) 2014.02.24
건전지 자가 교체  (1) 2013.10.28
:

자바 람다식(Lambda Expressions in Java)

Language/JAVA 2016. 9. 9. 10:24

자바 람다식(Lambda Expressions in Java)

Java2015-07-23 

자바 8에서 람다식(Lambda Expressions)이 추가 되었습니다. 이번 포스팅은 간단하게 람다식에 대해 알아보고자 합니다.

1. Lambda Expressions

람다식을 간단히 정의하면 다음과 같습니다.

식별자 없이 실행 가능한 함수 표현식

요즘은 정의만 보면 잘 모르겠어요. 부연 설명을 조금 해봅시다. 람다식은 자바 8의 가장 특징적인 기능입니다. 또한 기존의 불필요한 코드를 줄이고 가독성을 향상시키는것에 목적을 두고 있습니다.

대표적으로 반복문이나 비교문이 있겠습니다. 어떤식으로 코드를 줄이는지 한번 알아봅시다.

2. Lambda Expressions Example

자바에서 람다식을 사용하려면 다음과 같은 방법으로 사용이 가능합니다.

( parameters ) -> expression body
( parameters ) -> { expression body }
() -> { expression body }
() -> expression body
...

너무 단순한가요? 실제 코드로 확인을 해봅시다.

2.1. Basic

"Hello World."라는 단어를 출력하고 종료하는 쓰레드를 만들어봅시다. 처음으로는 기존에 존재하던 방식으로 작성해보고 다음은 람다식으로 작성해봅니다.

2.1.1. Traditional Code

기존에 있던 방식으로 만든 코드는 다음과 같습니다.

// Thread - traditional
new Thread(new Runnable() {
	@Override
	public void run() {
		System.out.println("Hello World.");
	}
}).start();

쓰레드를 돌리기 위해 Runnable 인터페이스를 새롭게 작성해서 매개변수로 넣었습니다. 이 코드를 람다식으로 바꾸면 어떻게 될까요?

2.1.2. Lambda Expression Code

아래에 있는 코드는 위에 있던 코드를 람다식으로 다시 작성한 것입니다.

// Thread - Lambda Expression
new Thread(()->{
	System.out.println("Hello World.");
}).start();

기존에 있던 코드량이 줄어든 것을 볼 수 있습니다. 인자가 없기 때문에 ()로 작성하고 실제로 동작할 코드를 ->{ ... }의 내부에 작성했습니다. 처음에 적어두었던 () -> { expression body } 구조입니다. 하지만 이것은 꽤나 단순한 예제이고 이것으로 람다식이 끝은 아닙니다.

2.2. Using @FunctionalInterface

객체지향 언어인 자바에서 값이나 객체가 아닌 하나의 함수(Funtion)을 변수에 담아둔다는 것은 이해가 되지 않을 것입니다. 하지만 자바 8에서 람다식이 추가 되고 나서는 하나의 변수에 하나의 함수를 매핑할 수 있습니다.

실제로 다음과 같은 구문을 실행시키고자 한다면 어떻게 해야할까요?

Func add = (int a, int b) -> a + b;

분명히 int형 매개 변수 a,b를 받아 그것을 합치는 것을 람다식으로 표현한것입니다. 그러면 Func는 무엇이어야 할까요?

답은 interface입니다. 위와 같은 람다식을 구현하려면 Func 인터페이스를 아래처럼 작성합니다.

interface Func {
	public int calc(int a, int b);
}

이 인터페이스에서는 하나의 추상 메소드를 가지고 있습니다. 바로 calc라는 메소드입니다. 이 메소드는 int형 매개 변수 2개를 받아 하나의 int형 변수를 반환합니다. 아직 내부 구현은 어떻게 할지 정해지지 않았죠.

이 내부 구현을 람다식으로 만든것이 처음에 보셨던 코드입니다. 아래의 코드죠.

Func add = (int a, int b) -> a + b;

여기까지는 진행에 무리가 없어보입니다. 그러면 혹시 Func 인터페이스에 메소드를 추가하게 되면 어떻게 될까요?

람다식으로 구현했던 add 함수 코드에서 오류가 납니다. 기본적으로 람다식을 위한 인터페이스에서 추상 메소드는 단 하나여야 합니다. 하지만 이러한 사실을 알고 있다 하더라도 람다식으로 사용하는 인터페이스나 그냥 메소드가 하나뿐인 인터페이스나 구별을 하기 힘들뿐더러 혹시라도 누군가 람다식으로 사용하는 인터페이스에 메소드를 추가하더라도 해당 인터페이스에서는 오류가 나지 않습니다.

따라서 이 인터페이스는 람다식을 위한 것이다라는 표현을 위해 어노테이션 @FunctionalInterface을 사용합니다. 실제로 저 어노테이션을 선언하면 해당 인터페이스에 메소드를 두 개 이상 선언하면 유효하지 않다는 오류를 냅니다. 즉, 컴파일러 수준에서 오류를 확인 할 수 있습니다.

다음처럼 Func 인터페이스의 코드가 변경됩니다.

@FunctionalInterface
interface Func {
	public int calc(int a, int b);
}

2.2.1. Various uses

물론 add 함수처럼 내부 구현을 할 수 있지만 조금 더 다양하게 구현을 해봅시다. 다음의 코드는 조금씩 내부 구현을 바꿔본 예제 코드입니다.

Func sub = (int a, int b) -> a - b;
Func add2 = (int a, int b) -> { return a + b; };

뺄셈을 위한 sub 함수도 만들었고 add 함수와 비슷하지만 expression body에 return 키워드를 붙인 add2 함수도 있습니다.

2.2.2. Using Function

그러면 실제로 람다식을 통해 내부를 구현한 함수 add는 어떻게 사용할까요? 다음의 코드를 봅시다.

int result = add.calc(1,2) + add2.calc(3, 4); // 10

위에서 만들어뒀던 add 함수와 add2 함수를 사용했습니다. 따라서 result 변수의 결과값으로 10이라는 것을 알 수 있습니다.

3. Stream API

람다식을 소개하면서 Stream API를 소개 하지 않을 수 없겠지요. 람다식을 추가하면서 같이 추가된 Stream API를 살펴봅시다. 이번 포스팅은 람다식이 주요 내용이었기 때문에 간단하게 사용법만 알아봅시다.

3.1. Get Stream

먼저 Stream API를 사용하려면 stream을 얻어와야 합니다. 얻는 방법은 다음과 같습니다.

Arrays.asList(1,2,3).stream(); // (1)
Arrays.asList(1,2,3).parallelStream(); // (2)

콜렉션 관련 객체라면 stream을 얻어올 수 있습니다. (1)번 방법은 일반적인 stream을 가져오는 것이고 (2)번 방법은 병렬로 stream을 가져옵니다.

3.2. Working Stream

실제로 얻어온 stream에 연산을 해봅시다. 주요하게 쓰이는 몇가지 API만 살펴봅시다.

3.2.1. forEach

stream의 요소를 순회해야 한다면 forEach를 활용할 수 있습니다.

Arrays.asList(1,2,3).stream()
					.forEach(System.out::println); // 1,2,3

3.2.2. map

stream의 개별 요소마다 연산을 할 수 있습니다. 아래의 코드는 리스트에 있는 요소의 제곱 연산을 합니다.

Arrays.asList(1,2,3).stream()
					.map(i -> i*i)
					.forEach(System.out::println); // 1,4,9

3.2.3. limit

stream의 최초 요소부터 선언한 인덱스까지의 요소를 추출해 새로운 stream을 만듭니다.

Arrays.asList(1,2,3).stream()
					.limit(1)
					.forEach(System.out::println); // 1

3.2.4. skip

stream의 최초 요소로부터 선언한 인덱스까지의 요소를 제외하고 새로운 stream을 만듭니다.

Arrays.asList(1,2,3).stream()
					.skip(1)
					.forEach(System.out::println); // 2,3

3.2.5. filter

stream의 요소마다 비교를 하고 비교문을 만족하는 요소로만 구성된 stream을 반환합니다.

Arrays.asList(1,2,3).stream()
					.filter(i-> 2>=i)
					.forEach(System.out::println); // 1,2

3.2.6. flatMap

stream의 내부에 있는 객체들을 연결한 stream을 반환합니다.

Arrays.asList(Arrays.asList(1,2),Arrays.asList(3,4,5),Arrays.asList(6,7,8,9)).stream()
					.flatMap(i -> i.stream())
					.forEach(System.out::println); // 1,2,3,4,5,6,7,8,9

3.2.7. reduce

stream을 단일 요소로 반환합니다.

Arrays.asList(1,2,3).stream()
					.reduce((a,b)-> a-b)
					.get(); // -4

이 코드는 조금 설명이 필요할 것 같습니다. 우선, 첫번째 연산으로 1과 2가 선택되고 계산식은 앞의 값에서 뒤의 값을 빼는 것이기 때문에 결과는 -1이 됩니다. 그리고 이상태에서 -1과 3이 선택되고 계산식에 의해 -1-3이 되기 때문에 결과로 -4가 나옵니다. 뒤로 추가 요소가 있다면 차근차근 앞에서부터 차례대로 계산식에 맞춰 계산하면 됩니다.

3.2.8. collection

아래의 코드들은 각각의 메소드로 콜렉션 객체를 만들어서 반환합니다.

Arrays.asList(1,2,3).stream()
					.collect(Collectors.toList());
Arrays.asList(1,2,3).stream()
					.iterator();


출처 - http://jdm.kr/blog/181

'Language > JAVA' 카테고리의 다른 글

왜 자바에서 static의 사용을 지양해야 하는가?  (6) 2018.02.22
Generic 메서드  (0) 2017.10.20
자바 JVM 정리(Java Virtual Machine)  (0) 2016.08.05
GC  (0) 2016.08.05
Do You Really Get Classloaders?  (0) 2016.08.04
:

Metaprogramming

Language/Groovy 2016. 9. 9. 10:23

http://docs.groovy-lang.org/docs/latest/html/documentation/core-metaprogramming.html#_delegating_metaclass

'Language > Groovy' 카테고리의 다른 글

Domain-Specific Language  (0) 2016.09.09
DSL(Domain Specific Language) 이해하기  (0) 2016.09.09
그루비의 동적 객체지향 - 1  (0) 2016.09.09
그루비의 동적 객체지향 - 2  (0) 2016.09.08
What does .delegate mean in groovy?  (0) 2016.09.08
:

그루비의 동적 객체지향 - 1

Language/Groovy 2016. 9. 9. 10:16

그루비에서 필드 변수의 기본 접근 영역은 특별하게 다루어진다.


1. 필드를 정의할 때 접근 제한자를 지정하지 않으면 이름에 맞는 ‘프로퍼티’가 만들어진다.(geter,seter)



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class SomeClass {
     
    public    public_var='old_public_var_val'  
    String    string_var='old_string_var_val'
    def       def_var   ='old_def_var_val'//필드를 정의할 때 접근 제한자를 지정하지 않으면 이름에 맞는 ‘프로퍼티’가 만들어진다.(geter,seter)
    static    static_var='old_static_var_val'
    protected protected_var1, protected_var2, protected_var3
    private   assignedField = new Date()
    public static final String CONSTA = 'a', CONSTB = 'b'
     
 
}
    SomeClass somclass = new SomeClass();
    println 'b_somclass.public_var : '+somclass.public_var;
    println 'b_somclass.string_var : '+somclass.string_var;
    println 'b_somclass.def_var : '+somclass.def_var;
     
    println '';
    somclass.public_var = 'new_public_var_val';
    somclass.string_var = 'new_string_var_val';
    somclass.def_var = 'new_def_var_val';
    println '';
     
     
    println 'a_somclass.public_var : '+somclass.public_var;
    println 'a_somclass.string_var : '+somclass.string_var;
    println 'a_somclass.def_var : '+somclass.def_var;
     
     
    println '';
    println SomeClass.class.methods.name.grep(~/[get].*/);//get으로 시작하는 메서드이름가져와보자
    println SomeClass.class.methods.name.grep(~/[set].*/);//set으로 시작하는 메서드이름가져와보자
        
    /* 결과
        b_somclass.public_var : old_public_var_val
        b_somclass.string_var : old_string_var_val
        b_somclass.def_var : old_def_var_val
         
         
        a_somclass.public_var : new_public_var_val
        a_somclass.string_var : new_string_var_val
        a_somclass.def_var : new_def_var_val
         
        [getMetaClass, this$dist$invoke$2, this$dist$set$2, this$dist$get$2, getString_var, getDef_var, getStatic_var, getProperty, equals, toString, getClass]
        [setMetaClass, this$dist$invoke$2, this$dist$set$2, this$dist$get$2, super$1$wait, super$1$wait, super$1$wait, super$1$toString, super$1$notify, super$1$notifyAll, super$1$getClass, super$1$equals, super$1$clone, super$1$hashCode, super$1$finalize, setString_var, setDef_var, setStatic_var, setProperty, equals, toString]
    */


위결과를 보면 자동으로 생성된 프로퍼티는 getString_var, getDef_var, getStatic_var setString_var, setDef_var, setStatic_var 가생성된걸 볼수있다. 




2. 그루비는 자바의 접근제한자를 쓸수있다?


1
2
3
4
5
6
7
8
package _2.sub
 
class GClass {
    private private_var='private_var';
    public public_var='public_var';
    protected protected_var='protected_var';
    def def_var ='def_var';
}



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package _2
 
import _2.sub.GClass;
GClass g = new GClass();
println "${g.private_var.class}    :    ${g.private_var}";
println "${g.public_var.class}    :    ${g.public_var}"; //자바에서는 이것만 허용이 될것이다.
println "${g.protected_var.class}    :    ${g.protected_var}";
println "${g.def_var.class}    :    ${g.def_var}";
/*
결과
class java.lang.String    :    private_var
class java.lang.String    :    public_var
class java.lang.String    :    protected_var
class java.lang.String    :    def_var
*/


접근제한자를 썼지만 자유롭게 접근이 가능하다. 


3. 배열 첨자 연사자로 필드 변수 사용하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package _3
 
class SomeClass {
     
    public    public_var='old_public_var_val'  
    String    string_var='old_string_var_val'
    def       def_var   ='old_def_var_val'//필드를 정의할 때 접근 제한자를 지정하지 않으면 이름에 맞는 ‘프로퍼티’가 만들어진다.(geter,seter)
    static    static_var='old_static_var_val'
    protected protected_var1, protected_var2, protected_var3
    private   assignedField = new Date()
    public static final String CONSTA = 'a', CONSTB = 'b'
     
 
}
    SomeClass somclass = new SomeClass();
    println 'b_somclass.public_var : '+somclass['public_var'];
    println 'b_somclass.string_var : '+somclass['string_var'];
    println 'b_somclass.def_var : '+somclass['def_var'];
     
    println '';
    somclass['public_var'] = 'new_public_var_val';
    somclass['string_var'] = 'new_string_var_val';
    somclass['def_var'] = 'new_def_var_val';
    println '';
     
     
    println 'a_somclass.public_var : '+somclass['public_var'];
    println 'a_somclass.string_var : '+somclass['string_var'];
    println 'a_somclass.def_var : '+somclass['def_var'];
     
     
    println '';
 
    /* 결과
    b_somclass.public_var : old_public_var_val
    b_somclass.string_var : old_string_var_val
    b_somclass.def_var : old_def_var_val
     
     
    a_somclass.public_var : new_public_var_val
    a_somclass.string_var : new_string_var_val
    a_somclass.def_var : new_def_var_val
    */




4. 필드 접근 메커니즘 확장하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package _4
 
class SomeClass {
     
    public    public_var='old_public_var_val'  
    String    string_var='old_string_var_val'
    def       def_var   ='old_def_var_val'//필드를 정의할 때 접근 제한자를 지정하지 않으면 이름에 맞는 ‘프로퍼티’가 만들어진다.(geter,seter)
    static    static_var='old_static_var_val'
    protected protected_var1, protected_var2, protected_var3
    private   assignedField = new Date()
    public static final String CONSTA = 'a', CONSTB = 'b'
     
    def notVar_GetCallCount=0;
    def notVar_SetCallCount=0;
     
    Object get (String name) {
        notVar_GetCallCount++;
        println "get : ${name}";
        return 'pretend value'
    }
    void set (String name, Object value) {
        notVar_SetCallCount++
        println "set  name : ${name},   value : ${value}";
    }
 
}
    SomeClass somclass = new SomeClass();
    println 'b_somclass.public_var : '+somclass.public_var;
    println 'b_somclass.string_var : '+somclass.string_var;
    println 'b_somclass.def_var : '+somclass.def_var;
    println 'b_somclass.not_var: '+somclass.not_var;
     
    println '';
    somclass.public_var = 'new_public_var_val';
    somclass.string_var = 'new_string_var_val';
    somclass.def_var = 'new_def_var_val';
    somclass.not_var = 'new_not_var_val';
    println '';
     
     
    println 'a_somclass.public_var : '+somclass.public_var;
    println 'a_somclass.string_var : '+somclass.string_var;
    println 'a_somclass.def_var : '+somclass.def_var;
    println 'a_somclass.not_var : '+somclass.not_var;
     
     
    println '';
    println 'somclass.notVar_GetCallCount   :   ' + somclass.notVar_GetCallCount;
    println 'somclass.notVar_SetCallCount   :   ' + somclass.notVar_SetCallCount;
        
    /* 결과
        b_somclass.public_var : old_public_var_val
        b_somclass.string_var : old_string_var_val
        b_somclass.def_var : old_def_var_val
        get : not_var
        b_somclass.not_var: pretend value
         
        set  name : not_var,   value : new_not_var_val
         
        a_somclass.public_var : new_public_var_val
        a_somclass.string_var : new_string_var_val
        a_somclass.def_var : new_def_var_val
        get : not_var
        a_somclass.not_var : pretend value
         
        somclass.notVar_GetCallCount   :   2
        somclass.notVar_SetCallCount   :   1
 
    */


여기서 중요하게 볼것이 존재하지 않는 프로퍼티를 접근할 때 get 이라는 메서드를 통하여 접근한다 
존재하지 않는 프로퍼티에 변수값을 지정할 때 set(‘name’,Object)로 접근한다는것도 잊지말어라. 


메서드와인자


자바의 접근제한자를 쓸수 있으며 리턴형은 생략가능하다. 접근제한자나 리턴형을 지정하지 않을 때는 키워드 def를 사용한다. 
def를 쓰면 리턴값의 자료형이 지정되지 않았다고 생각할수 있다 (물론, 리턴이 없는 void메서드일수도 있다) 
내부적으로 java.lang.Object가 리턴된다. 접근제한자는 def로 선언시 public선언된다. 

5.메서드정의하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package _5
 
class SomeClass {
    static void main (args){                        //#1 자동으로 public 접근제한자 없으므로  자바 메인메서드가 된다.
    println "call main"
        def some = new SomeClass()
        some.publicVoidMethod()
        assert 'hi' == some.publicUntypedMethod()//true
        assert 'ho' == some.publicTypedMethod()//true
          combinedMethod()                            //#2 현재 클래스의 정적 메서드 호출
    }
    static void main2 (args){
        args+'before';
        args+'after';;
    }
    static main3 (args){
        args+'before';
        args+'after';;
    }
    void publicVoidMethod(){
    }
    def publicUntypedMethod(){
        return 'hi'   
    }
    String publicTypedMethod() {       
        return 'ho'      
    }
    protected static final void combinedMethod(){
    }   
}
//println '-----';  //이부분 주석을 풀게되면  이클래시는 Script를 상속받게된다 따라서 run메서드를 실행시키게된다.
/* 결과
call main
*/



main 메서드에 흥므로운 것이 있다 
첫째는 : public디폴트이기 때문에 생략 
둘째는 : 실행할 수 있는 클래스의 메인메서드는 인자가 String[] 이어야 한다. 여기서 arg가 암묵적으로Object 가 되는데도 그루비의 메서드 디스패치 덕분에 메인 메서드로 동작한다. 
여기서 void도 생략가능하다 메인 메서드로 동작한다.static main(args) 





6.인자리스트 정의 인자에 따른 메서드 호출.다르게


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package _6
 
class SomeClass {   
    static void main (args){ 
        assert 'untyped' == method(1)                  
        assert 'typed'   == method('whatever')       
        assert 'two args'== method(1,2)
        println  method(1)                
        println  method('whatever')       
        println  method(1,2)              
        println  method(1,2,3)              
    }   
    static method(arg) {       
        return 'untyped'      
    }
    static method(String arg){    
        return 'typed' 
    }
    static method(arg1, Number arg2){    
        return 'two args' 
    }
     def method(arg1,arg2,arg3){    
        return 'boolean args' 
    }
}
/*
결과
untyped
typed
two args
Caught: groovy.lang.MissingMethodException: No signature of method: static _6.SomeClass.method() is applicable for argument types: (java.lang.Integer, java.lang.Integer, java.lang.Integer) values: [1, 2, 3]
Possible solutions: method(java.lang.Object), method(java.lang.String), method(java.lang.Object, java.lang.Number), method(java.lang.Object, java.lang.Object, java.lang.Object)
    at _6.SomeClass.main(_6.groovy:11)
 
*/


오버로딩 같은개념 
여기서 맨마지막 method(1,2,3) 호출이 오류난건 자바에서처럼 메인메서드에서 호출할려는 메서드는 static이여야한다 그걸 위반했다. 

7.고급인자 사용 기술


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package _7
 
import java.util.List;
import java.util.Map;
 
class Summer {
    def sumWithDefaults(a, b, c=0){                         //#1
        return a + b + c
    }
    def sumWithList(List args){                             //#2
        return args.inject(0){sum,i -> sum += i}//0초기값으로주고(누적값sum) list인자만큼 돈다.(인자값:i)
    }   
    def sumWithOptionals(a, b, Object[] optionals){         //#3
        return a + b + sumWithList(optionals.toList())
    }
    def sumNamed(Map args){                                 //#4
        ['a','b','c'].each{args.get(it,0)}      //초기값부여
        return args.a + args.b + args.c
    }
    def sumNamed(Map args,Map args1,Map args2){                                 //#4
        return 3;
    }
}
 
def summer = new Summer()
 
//#1호출
assert 2 == summer.sumWithDefaults(1,1)    
assert 3 == summer.sumWithDefaults(1,1,1)  
println  'summer.sumWithDefaults(1,1)       :'+summer.sumWithDefaults(1,1)     
println  'summer.sumWithDefaults(1,1,1)     :'+summer.sumWithDefaults(1,1,1)
 
//#2호출
assert 2 == summer.sumWithList([1,1])      
assert 3 == summer.sumWithList([1,1,1])    
println 'summer.sumWithList([1,1])      : '+summer.sumWithList([1,1])  
println 'summer.sumWithList([1,1,1])    : '+summer.sumWithList([1,1,1])
 
 
//#3호출
assert 2 == summer.sumWithOptionals(1,1)
assert 3 == summer.sumWithOptionals(1,1,1)
assert 6 == summer.sumWithOptionals(1,1,1,1,1,1)
println    'summer.sumWithOptionals(1,1)          :'+summer.sumWithOptionals(1,1)         
println    'summer.sumWithOptionals(1,1,1)        :'+summer.sumWithOptionals(1,1,1)       
println    'summer.sumWithOptionals(1,1,1,1,1,1)  :'+summer.sumWithOptionals(1,1,1,1,1,1) 
 
 
//map호출
assert 2 == summer.sumNamed(a:1, b:1)      
assert 3 == summer.sumNamed(a:1, b:1, c:1)
assert 1 == summer.sumNamed(c:1)
assert 3 == summer.sumNamed([a:1, b:1, c:1])
assert 3 == summer.sumNamed([a:1, b:1, c:1],[a:1, b:1, c:1],[a:1, b:1, c:1])
println 'summer.sumNamed(a:1, b:1)                                       :'+summer.sumNamed(a:1, b:1)                                       
println 'summer.sumNamed(a:1, b:1, c:1)                                  :'+summer.sumNamed(a:1, b:1, c:1)                                   
println 'summer.sumNamed(c:1)                                            :'+summer.sumNamed(c:1)                                             
println 'summer.sumNamed([a:1, b:1, c:1])                                :'+summer.sumNamed([a:1, b:1, c:1])                                 
println 'summer.sumNamed([a:1, b:1, c:1],[a:1, b:1, c:1],[a:1, b:1, c:1]):'+summer.sumNamed([a:1, b:1, c:1],[a:1, b:1, c:1],[a:1, b:1, c:1]) 
 
/*
결과
summer.sumWithDefaults(1,1)     :2
summer.sumWithDefaults(1,1,1)     :3
summer.sumWithList([1,1])       : 2
summer.sumWithList([1,1,1]) : 3
summer.sumWithOptionals(1,1)          :2
summer.sumWithOptionals(1,1,1)        :3
summer.sumWithOptionals(1,1,1,1,1,1)  :6
summer.sumNamed(a:1, b:1)                                        :2
summer.sumNamed(a:1, b:1, c:1)                                  :3
summer.sumNamed(c:1)                                            :1
summer.sumNamed([a:1, b:1, c:1])                                :3
summer.sumNamed([a:1, b:1, c:1],[a:1, b:1, c:1],[a:1, b:1, c:1]):3
 
*/



여기서 눈여겨볼것이… 3가지있는데. 
1. 디폴트값 
def sumWithDefaults(a, b, c=0){} 
2. 파라미터 집합형자료형 ,로 바로넣기(동적 메시지 디스패처가 넘치는 인자들을 배열에 담아서 전달) 
def sumWithOptionals(a, b, Object[] optional){} 
assert 6 == summer.sumWithOptionals(1,1,1,1,1,1) 
3. 집합자료형 , 바로넣기 2 (동적 메시지 디스패처가 넘치는 인자들을 배열에 담아서 전달) 
def sumNamed(Map args){} 
sumNamed(Map args,Map args1,Map args2){} 
assert 3 == summer.sumNamed(a:1, b:1, c:1) 
assert 3 == summer.sumNamed([a:1, b:1, c:1]) 
assert 3 == summer.sumNamed([a:1, b:1, c:1], [a:1, b:1, c:1], [a:1, b:1, c:1]) 

고급명명기법
1
2
3
4
5
6
class g{
    def g=55;
}
Map args = [ a:1,b:2,c:3,d:4]
println args.'size'();
println new g().'g';

역시 여기서도 스크립트의 파워풀한기능을 볼수있다. 




8.안전한 참조연산자


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package _8
 
def map = [a:[b:[c:1]]]          
 
assert map.a.b.c == 1       //일반적인접근
 
if (map && map.a && map.a.x){           //#1        평가단축기법   
    assert false;
    assert map.a.x.c == null
}   
             
//널포인트 발생에따른 예외처리를 해줘야한다.. 일반적인처리..  a안에는x가 없으니..
try {
    assert map.a.x.c == null
} catch (NullPointerException npe){     //#2
    println "NullPointerException : ${npe}"
}
  
//안전하게 참조하는  ? 연산자를 제공한다 이연산자는 앞에 있는 참조 변수가 null이면 현재 해석중인 표현식을 중지하고 null리턴한다.
assert map?.a?.x?.c == null             //#3
println 'map?.a?.x?.c '+map?.a?.x?.c
/*
결과
NullPointerException : java.lang.NullPointerException: Cannot get property 'c' on null object
map?.a?.x?.c null
*/


괜찮은 기능이기긴 하지만 저 안전한 참조연산자를 쓸정도로 애매모호한 구조를 설계하지말아야 될것같은 느낌이다. 


생성자들


생성자는 세가지 방법으로 호출할수 있다 
1. 자바에서 하던방식 
2. 키워드 as, asType를 이용한 강제형변환 
3. 암묵적 형변환 방식 

9.위치기반 인자를 이용한 생성자


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package _9
 
import java.util.Map;
 
class VendorWithCtor {                     
    String name, product
    VendorWithCtor(){
        println "디폴트 생성자"
    }
     
    VendorWithCtor(Map map){
        println "Map 파라미터1개 생성자호출 : ${map},  ${map.class.name}"
        this.name = name;
    }
    VendorWithCtor(name){
        println "파라미터1개 생성자호출 : ${name},  ${name.class.name}"
        this.name = name;
    }
    VendorWithCtor(name, product) {             //#1
        println "파라미터2개 생성자호출  : ${name}, ${product}"
        this.name    = name
        this.product = product
    }
}
 
def first = new VendorWithCtor('Canoo','ULC_일반적인 생성')   //#2 일반적은 생성자호출
def first2 = new VendorWithCtor()   //#2 일반적은 생성자호출
 
def second = ['Canoo','ULC_강제 형변환'] as VendorWithCtor  //#3 강제 형변환
//def second = ['Canoo','ULC'].asType(VendorWithCtor)  //#3 강제 형변환
def second2 = [] as VendorWithCtor  //#3 강제 형변환
Map map = [a:1,b:2,c:3];
def second3 = map as VendorWithCtor  //#3 강제 형변환 이건안된다..디폴트가탄다.. Map이있지만도..
 
VendorWithCtor third = ['Canoo','ULC_암묵적 형변환']          //#4 암묵적 형변환
VendorWithCtor third2 = ['Canoo 암묵적 형변환']          //#4 암묵적 형변환
 
 
/*
결과
파라미터2개 생성자호출  : Canoo, ULC_일반적인 생성
디폴트 생성자
파라미터1개 생성자호출 : [Canoo, ULC_강제 형변환],  java.util.ArrayList
파라미터1개 생성자호출 : [],  java.util.ArrayList
디폴트 생성자
파라미터2개 생성자호출  : Canoo, ULC_암묵적 형변환
파라미터1개 생성자호출 : Canoo 암묵적 형변환,  java.lang.String
*/


역시 자바와다르게 형변환할때도 생성자 호출이된다… 하지만 저 해쉬맵은 왜안될까… 
역시 자바와다르게 스크립트기반언어기 때문에 개발시 부분부분 테스트가 더욱더 세밀하게 이루워져야할것같다. 




이름기반 이자로 생성자 호출하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package _10
 
class Vendor {                     
    //String name, product
    def name, product
    //private String name, product  //private 해도 적용됨
}
 
new Vendor()                           
new Vendor(name:   'Canoo')             
new Vendor(product:'ULC')               
new Vendor(name:   'Canoo', product:'ULC')            
new Vendor(name:   'Canoo', product:1)         //자동케스팅   
//new Vendor(name:   'Canoo', product2:1) //오류남            
 
def vendor = new Vendor(name: 'Canoo')
assert 'Canoo' == vendor.name
println vendor.name;
 
 
//암묵적 생성
java.awt.Dimension area;
area = [20,100];
assert area.width ==20;
assert area.height == 100;
println    'area.width :    '+area.width ;  
println    'area.height : '+area.height ;
 
/*
결과
Canoo
area.width :    20.0
area.height : 100.0
 
*/


위처럼 사용할수도있다.. 하지만 위 같은 상황을 쓸일이 많이 생길지 의문이다.. 

클래스와 스크립트 구성하기



그루비 클래스 생성을 할수 있다. 

1. 그루비 파일에 클래스 정의가 ‘하나도 없다면’ 그파일은 스크립트로 동작한다. 
다시말해 자동으로 Script 클래스를 상속받는다. 
자동으로 생성된 클래스의 이름은 확장자를 뺀 소스 파일의 이름과 같다. 
파일의 코드는run메서드에 들어가고, 실행할 수 있겠끔 main 메서드도 만들어진다. 
2. 그루비 파일에 클래스가 ‘하나’ 만 있고, 그 클래스 이름이 확장자를 제외한 소스 파일의 이름과 같다면 자바와 같은 일대일 대응 관계가 된다. 
3. 그루비는 그루비 파일에 클래스가 ‘몇개’ 정의되어있든 그것들이 public이든 private이든 소스파일의 이름과 같은 클래스가 있던 없던 다수용할수 있다. 그루비 컴파일러인 groovyc는 파일에 정의된 클래스 각각에 대해 *.class 파일을 생성한다. 이파일을 프롬프트에서 goovy를 통해서 실행하거나 IDE에서 호출하는 식으로 스크립트로서 사용할 때는 파일에서 첫번째로 정의된 클래스에 main 메서드가 있어야한다. 
4. 그루비 파일에 클래스 정의와 스크립트 코드를 섞어서 쓸수도 있다 이렇게 하면 실행시에 주 클래스는 스크립트 코드가 된다 따로 소스 파일이 이름과 같은 이름으로 클래스를 만들 필요는 없다. 

명시적으로 컴파일 하지 않았다면 그루비에서 클래스를 찾을 때는 클래스 이름 과 같은 *.groovy소스 파일을 검색한다. 이때 이름이 중요하다 그루비는 찾고 있는 클래스와 이름이 같은 파일만 검색한다 , 파일을 발견하면 그파일 안에 모든 클래스를 분석해서 로딩한다. 



패키지로 구조화하기


*.groovy소스 파일들은 *.class파일로 컴파일하지 않아도 쓸수있기때문에 클래스를 찾을때 *.groovy 파일도 함께 찾는다. 이때도 같은 방식으로 검색한다 즉 그루비는 business/Vendor.groovy파일에서 business 패키지에 속한 Vendor 클래스를 검색할것이다. 

클래스패스


그루비가 *.groovy 파일을 찾을때 클래스 패스를 사용한다. 
주어진 클래스를 찾다가 *.class 와 *.groovy 모두 발견됐다면 둘중 최근에 변경된 파일을 사용한다 즉 *.groovy 가 *.class보다 최근에 변경됐다면 *.groovy 컴파일한후 *.class를 사용한다. 

패키지예제 

1
2
3
4
5
6
7
8
9
10
11
12
package business
 
class Vendor {
    public String  name
    public String  product
    public Address address = new Address()
}
 
class Address  {
    public String  street, town, state
    public int     zip
}


임포트예제 

1
2
3
4
5
6
7
import business.*
 
def canoo = new Vendor()
canoo.name          = 'Canoo Engineering AG'
canoo.product       = 'UltraLightClient (ULC)'
 
assert canoo.dump() =~ /ULC/


몇몇 스크립트 언어와 달리 임포트를 해도 클래스나 파일을 실제로 포함하는것이 아니다 다만 클래스 이름을 해석할 때 참고할 정보를 주는것뿐이다. 

그루비는 패키지 6개와 클래스2개를 자동 임포트한다. 
java.lang.* 
java.util.* 
java.io.* 
java.net.* 
groovy.lang.* 
groovy.util.* 
java.math.BigInteger 
java.mat.BigDecimal 


14.클래스 별칭


as 를통하여 클래스 별칭(type aliasing)을 만들수 있다. 
이것은 클래스 이름 충돌을 해결하거나 지역적인 수정 혹은 써드파트 라이브러리 버그를 수정하는데 사용한다. 

1
2
3
4
5
6
7
package _14.oldpack
 
public class _14_A {
    public def calc(def a, def b){
        return a+b;
    }
}


1
2
3
4
5
6
7
8
9
10
11
package _14
 
import _14.oldpack._14_A as GO;
 
class A_14_A extends GO {
    public def calc(def a, def b){
        return a*b;
    }
}
def  f =  new A_14_A();
println f.calc(4,4);




클래스 패스에 관련된 추가 사항


그루비가 *.class 파일과 *.groovy 파일에서 클래스를 찾아낸다는 점은 그루비를 다를때 이해하고 있어야 하는 중요한 부분이다. 안타깝게도 여기서 문제가 종종 발생하는데... 

그루비의 클래스패스는 %GROOVY_HOME%\conf 디렉터리의 특별한 설정파일이있다. 
groovy-starter.conf 

맨마지막줄 #지워서 활성화시키면 좋은 기능이 살아난다. 
user.home으로 상징되는 사용자의 홈 디렉터리에서 서브디렉터리로 .groovy/lib를 만들고 *.class나 *.jar 파일 넣어두면 그루비가 쓸때마다 로딩되도록한다. 
user.home찾기힘들다면 
groovy –e “println System.properties.’user.home’” 

셀제목셀제목셀제목
구분정의목적과 사용법
JDK/JRE%JAVA_HOME%/lib,%JAVA_HOME%/lib/extJRE부트 클래스패스와 확장 라이브러리들
OS설정CLASSPATH변수일반적인 기본설정
커맨드라인CLASSPATH 변수특수설정
java-cp,--classpath,option실행시 설정
Groovy%GROOVY_HOME%/lib그루비 실행환경
Groovy-cp그루비 실행시 설정
Groovy.현재 디렉터리 클래스패스로 하는 디폴트 클래스 패스


고급 객체지향 기능


1. 상속하기 
그루비에서는 그루비와 자바의 클래스나 인터페이스를 상속받아서 확장할 수 있다. 자바 쪽에서도 그루비 클래스나 인터페이스를 상속받을수 있다. 
2. 인터페이스 
자바의 인터페이스를 완벽하게 지원한다. 
자바의 추상 메서드도 지원한다. 
그루비는 더 동적으로 인터페이스를 사용할 수 있는 기능을 제공한다. 메서드가 하나만 있는 인터페이스인 MyInterface와 클로저 myClosure가 있다면 이 myClosure를 키워드 as 이용하여 MyInterface로 강제 형변환할수도있다 

16.멀티메서드


자바에 메서드를 호출하면 명시한 자료형을 참조해서 메서드를 찾는다 그에 반해 그루비에서는 인자의 동적 자료형을 고려해서 적절한 메서드를 찾아낸다 그루비의 이런 기능을 멀티메서드(multimethod)라고한다 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package _16
 
def oracle(Object o) { return 'object' }
def oracle(String o) { return 'string' }
 
Object x = 1    
Object y = 'foo'
 
assert 'object' == oracle(x)
assert 'string' == oracle(y) //#1 자바라면 ‘object’를 호출할것이다.
println oracle(x);
println oracle(y);
println oracle(y as Object);
/* 결과
object
string
object
*/


인자x는 Object로 표시됐지만 동적으로는 Integer이다. 
인자y는 Object로 표시됐지만 동적으로는 String이다. 
명시적으로 가도록 선언하고 싶다면 명시적으로 형변환을 해주면된다. 
그루비에서는 인자의 자료형을 동적으로 검사한다. 

17.equals를 선택적으로 재정의하는 메서드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package _17
 
class Equalizer {
    boolean equals(Equalizer e){ //이건 equals의 오버라이딩 된것이 아니라.  오버로딩된것이다.
        println 'equals Equalizer : '+e.class
        return true
    }
//  boolean equals(e){ //이건 equals의 오버라이딩 된것이 아니라.  오버로딩된것이다.
//      println 'equals Object : '+e.class
//      return true
//  }
}
 
Object same  = new Equalizer()         
Object other = new Object()
//여기 객체를 Object로 받았다.
 
assert   new Equalizer().equals( same  ) //Equalizer에서 정의한 equals로 간다. //그루비는 자동으로 클레스형대로 찾아서간다
assert ! new Equalizer().equals( other ) // Object의 equals 메서드를 호출한다. //그루비는 자동으로 클레스형대로 찾아서간다
/*결과
equals Equalizer : class _17.Equalizer
*/



뭐가 다른지 자바를 보자 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import GG;
import java.util.Date;
 
 
class GG{
     
    public boolean equals(String obj) {
        System.out.println("equals String");
        return true;
    }
}
 
public class TestJava {
    public static void main(String[] args) {
        GG g = new GG();
         
        Object s = new String("---");
        Object d = new Date(0);
         
        System.out.println( g.equals(s) );
        System.out.println( g.equals(d) );
         
         
        System.out.println( g.equals((String)s) );
    }
}
/*결과
false
false
equals String
true
 */





18.그루비빈 사용하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package _18
 
import java.io.Serializable
 
class MyBean implements Serializable {
    def untyped
    String typed
    def item1, item2
    def assigned = 'default value'
    //접근제한자가 붙은건 자동프로퍼티가 생기지 않는다.
}
 
def bean = new MyBean()
assert 'default value' == bean.getAssigned()
println bean.getAssigned();
 
bean.setUntyped('some value')
assert 'some value' == bean.getUntyped()
println bean.getUntyped();
 
bean = new MyBean(typed:'another value',untyped:'untyped---',item1:'item1---')
assert 'another value' == bean.getTyped()
assert 'untyped---' == bean.getUntyped()
assert 'item1---' == bean.getItem1()
println bean.getTyped();
println bean.getUntyped();
println bean.getItem1();
 
/*
결과
default value
some value
another value
untyped---
item1---
*/


여기서 중요하게 볼것이 생성할때 프로퍼티를 통하여 초기값을 줄수 있다는것이다. 


읽기전용 프로퍼티
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package _18
import java.io.Serializable
 
class MyBean implements Serializable {
    final def untyped='untyped'
}
 
def bean = new MyBean()
assert 'untyped' == bean.getUntyped()
println bean['untyped']
bean['untyped']='tttttttttt'; //error
bean.setUntyped('----------untyped');//error
 
//프로퍼티 변수에 final을 붙이면 읽기전용이 된다




프로퍼티 name에 접근하는 방법 비교 

자바그루비
getName()name
setName('a')name='a'

19.getter, setter 메서드 만들기만해도 그루비 프로퍼티 접근형식으로 접근가능


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package _19
 
class MrBean {
    String firstname, lastname              //#1
     
    String getName(){                       //#2 가상 프로퍼티의 생성자 가상의name이생긴거다/
        return "$firstname $lastname"
    }
}
 
def bean = new MrBean(firstname: 'Rowan')   //#3
bean.lastname = 'Atkinson'                  //#4
 
assert 'Rowan Atkinson' == bean.name        //#5 자동으로 생긴name 읽고 쓰고한다.
println bean.name ;
/*결과
Rowan Atkinson
*/


위에서 보는것처럼 프로퍼티접근하는거에대한 일관성이 유지된다. 오우.~ 




20.프로퍼티 함수를 통하여 접근하지 않고! @기호로 필드 변수에 바로 접근하기.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package _20
 
class DoublerBean {
    public value                           //#1
     
    void setValue(value){
        this.value = value                 //#2
    }
     
    def getValue(){                       
        value * 2                          //#3
    }
}
 
def bean = new DoublerBean(value: 100)    
 
assert 200 == bean.value                   //#4
assert 100 == bean.@value                  //#5
 
println 'bean.value;    :  '+bean.value;
println 'bean.@value;   :  '+bean.@value;
/* 결과
bean.value;    :  200
bean.@value;   :  100
*/


필드 변수와 같은 영역에서는 fieldname 이나this.fieldname을 필드 변수에 대한 접근으로 해석하며 프로퍼티에 대한 접근으로 보지 안흔ㄴ다 영역 밖에서는 referenc.@fieldname문법을 이용해야 동일한 효과를 얻는다. 
주의할것이 하나있는데 정적인 영역 (static context)에서 @를 사용하거나 def x = this;x.@fieldname 같은식으로 사용하면 이상한 현상이 발생한다. 권장하지 않는다. 


자바빈 스타일 이벤트처리


그루비는 빈의 내부를 검사(bean introspection) 해서 할당문의 필드 변수가 리스너 등록 메서드 인지 확인한다 등록 메서드로 판단되면 ClosureListener 클래스가 자동으로 붙고 이벤트가 발생하면 클로저를 호출해준다. 

그루비 

1
2
3
4
def button = new JButton('push');
button.actionPerformed ={event->
    println button.text;
}



자바 

1
2
3
4
5
6
final JButton button = new JButton("push");
button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        System.out.println(button.getText());
    }
});





필드, 접근자, 맵, Expando


그루비 코드에는 object.name같은 표현이 자주 눈에 띈다 그루비가 이런 표현식을 만났을 때 하는 일을 나열해 보면 다음과같다. 

1. object가맵이면 object.name은 맵의 키에서 name을 찾아 대응하는 값을 가르킨다. 

1
2
def map = [a:1,b:2];
println map.a;



2. name이 object의 프로퍼티일 때는 프로퍼티를 찾아서 가리킨다 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class gogo{
    def oneProperty='oneProperty_value'
    def towProperty='towProperty_value'
    public def getOneProperty() {
        println 'call getOneProperty'
        return oneProperty;
    }
}
def g = new gogo();
println g.oneProperty;
/*결과
call getOneProperty
oneProperty_value
*/



3. 모든 그루비 객체는 자신만의 getProperty(name) 과 setProperty(name,value)메서드를 정의할수 있다. 맵(map)객체도 이방법으로 키를 프로퍼티처럼 제공한다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
class gogo{
    def oneProperty='oneProperty_value'
    def towProperty='towProperty_value'
    public def getOneProperty() {
        println 'call getOneProperty'
        return oneProperty;
    }
}
def g = new gogo();
println g.getProperty('towProperty');
/*결과
towProperty_value
*/




4. object.get(name)메서드를 만들면 필드 변수 접근을 가로챌수 있다 이부분은 그루비 실행환경의 최전방에 속한다 자바빈에 적당한 프로퍼티도 없고 getProperty메서드도 구현되지 않았을때 사용된다. 
그리고 당연히 name에 특수 문자가 있어서 식별자(identifier)로 사용할수 없을 때도 문자열로 만들면 사용할수 있다 즉 object.’my-name’과같이 만들면된다 또 GString 이용할수도있다. def name = ‘my-name’;Object.”$name” 처럼 써도된다 Object에는 getAt이라는 메서드가 있어서 object[name] 형식으로 프로퍼티에 접근하도록 위임할수 있다. 

21.프러퍼티 접근 및 get,set메서드 활용 예제


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package _21
 
class Go{
    def g;
    public void setG(def g) {
        println 'setG '+g;
        this.g = g;
    }
     
     
    public void set(def name,def value) {
        println 'No such property Name -> Set Name:'+name+', value:'+value;
    }
    public def get(String name) {
        return 'No such property Name -> '+name;
    }
     
}
  
def g = new Go([g:'1']);
println 'g.g;  : ' + g.g; 
println 'g.gg; : ' + g.gg;
 
println '';
g.g='g_value'
g.gg='gg_value';
println '';
 
println 'g.g;  : ' + g.getAt("g"); 
println 'g.gg; : ' + g.getAt("gg");
 
 
 
/*결과
setG 1
g.g;  : 1
g.gg; : No such property Name -> gg
 
setG g_value
No such property Name -> Set Name:gg, value:gg_value
 
g.g;  : g_value
g.gg; : No such property Name -> gg
*/



여기서 주의할것은 get,set 메서드는 프로퍼티가 없을경우에만 탄다는것이다. 
프로퍼티가 있는것은 자신의 접근 메서드로 바로 호출된다.
 



그루비 특장점 활용하자


그루비의 강력한 세가지 기능을 설명한다 
1. GPath 
2. 확산연산자(spread operator 
3. use 


22.GPath는 객체들의 구조를 탐색하는 강력한 도구이다. XPath이름을 따서 왔다나..?


GPath예제 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package _22
 
//현재 객체를 문자열로 표현한것이다
println 'this : '+this;
 
//이객체의 클래스가 무엇인지 알려면 this.getClass라고하면되지만 그루비에서는 다음처럼 가능하다.
println 'this.class   : '+this.class;
 
//클래스 객체는 getMethods메서드를 통해서 메서드들의 리스트를 제공한다
println 'this.class.methods   :  '+this.class.methods;
 
//모든 메서드에는 getName메서드가있으니 이렇게 입력해보자
println 'this.class.methods.name   : '+this.class.methods.name;
 
//get으로 시작하는 메서드이름가져와보자
println 'this.class.methods.name.grep(~/get.*/) : '+this.class.methods.name.grep(~/get.*/);
 
/*결과
this : _22._22@1766806
this.class   : class _22._22
this.class.methods   :  [public void _22._22.super$1$wait(), public void _22._22.super$1$wait(long,int), public void _22._22.super$1$wait(long), public java.lang.String _22._22.super$1$toString(), public void _22._22.super$1$notify(), public void _22._22.super$1$notifyAll(), public java.lang.Class _22._22.super$1$getClass(), public boolean _22._22.super$1$equals(java.lang.Object), public java.lang.Object _22._22.super$1$clone(), public int _22._22.super$1$hashCode(), public void _22._22.super$1$finalize(), public java.lang.Object _22._22.this$dist$invoke$4(java.lang.String,java.lang.Object), public void _22._22.this$dist$set$4(java.lang.String,java.lang.Object), public java.lang.Object _22._22.this$dist$get$4(java.lang.String), public java.lang.Object _22._22.super$3$getProperty(java.lang.String), public void _22._22.super$3$setProperty(java.lang.String,java.lang.Object), public void _22._22.super$3$println(), public void _22._22.super$3$println(java.lang.Object), public void _22._22.super$3$print(java.lang.Object), public void _22._22.super$3$printf(java.lang.String,java.lang.Object), public void _22._22.super$3$printf(java.lang.String,java.lang.Object[]), public java.lang.Object _22._22.super$3$evaluate(java.lang.String), public java.lang.Object _22._22.super$3$evaluate(java.io.File), public groovy.lang.MetaClass _22._22.super$2$getMetaClass(), public void _22._22.super$2$setMetaClass(groovy.lang.MetaClass), public groovy.lang.Binding _22._22.super$3$getBinding(), public void _22._22.super$3$setBinding(groovy.lang.Binding), public void _22._22.super$3$run(java.io.File,java.lang.String[]), public java.lang.Object _22._22.super$3$invokeMethod(java.lang.String,java.lang.Object), public static void _22._22.main(java.lang.String[]), public java.lang.Object _22._22.run(), public void groovy.lang.Script.setBinding(groovy.lang.Binding), public java.lang.Object groovy.lang.Script.invokeMethod(java.lang.String,java.lang.Object), public groovy.lang.Binding groovy.lang.Script.getBinding(), public void groovy.lang.Script.println(), public void groovy.lang.Script.println(java.lang.Object), public void groovy.lang.Script.run(java.io.File,java.lang.String[]) throws org.codehaus.groovy.control.CompilationFailedException,java.io.IOException, public void groovy.lang.Script.setProperty(java.lang.String,java.lang.Object), public java.lang.Object groovy.lang.Script.getProperty(java.lang.String), public void groovy.lang.Script.print(java.lang.Object), public void groovy.lang.Script.printf(java.lang.String,java.lang.Object), public void groovy.lang.Script.printf(java.lang.String,java.lang.Object[]), public java.lang.Object groovy.lang.Script.evaluate(java.io.File) throws org.codehaus.groovy.control.CompilationFailedException,java.io.IOException, public java.lang.Object groovy.lang.Script.evaluate(java.lang.String) throws org.codehaus.groovy.control.CompilationFailedException, public groovy.lang.MetaClass groovy.lang.GroovyObjectSupport.getMetaClass(), public void groovy.lang.GroovyObjectSupport.setMetaClass(groovy.lang.MetaClass), public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException, public final void java.lang.Object.wait() throws java.lang.InterruptedException, public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public boolean java.lang.Object.equals(java.lang.Object), public java.lang.String java.lang.Object.toString(), public native int java.lang.Object.hashCode(), public final native java.lang.Class java.lang.Object.getClass(), public final native void java.lang.Object.notify(), public final native void java.lang.Object.notifyAll()]
this.class.methods.name   : [super$1$wait, super$1$wait, super$1$wait, super$1$toString, super$1$notify, super$1$notifyAll, super$1$getClass, super$1$equals, super$1$clone, super$1$hashCode, super$1$finalize, this$dist$invoke$4, this$dist$set$4, this$dist$get$4, super$3$getProperty, super$3$setProperty, super$3$println, super$3$println, super$3$print, super$3$printf, super$3$printf, super$3$evaluate, super$3$evaluate, super$2$getMetaClass, super$2$setMetaClass, super$3$getBinding, super$3$setBinding, super$3$run, super$3$invokeMethod, main, run, setBinding, invokeMethod, getBinding, println, println, run, setProperty, getProperty, print, printf, printf, evaluate, evaluate, getMetaClass, setMetaClass, wait, wait, wait, equals, toString, hashCode, getClass, notify, notifyAll]
this.class.methods.name.grep(~/get./) : [getBinding, getProperty, getMetaClass, getClass]
*/




23.확산 연산자 사용하기


이연산자는 확산-도트 연산자에서 리스트의 각 요소로 대응해 주는 역활을 한다. 
배열기호 연산자와는 반대라고 할수 있다 배열기호 연산자는 쉼표로 구분된 객체들을 모아서 리스트로 만들어준다. 확산 연산자는 리스트의 요소들을 수신자에게 배포해준다. 수신자는 여러 인자를 받을수 있는 메서드이거나 리스트를 생성할수있다. 
어떤 메서드가 결과 값을 리스트에 담아서 리턴 하는데 호출한 측에서 이결과 값들을 다른 메서드에 전달해야 하는 상황을 생각해보자 확산 연산자를 이용하면 결과 값을 그대로 두번재 메서드에 인자로 전달할수있다. 
아래와 같이 하면 여러개 리턴하는 메서드와 이를 수신하는 메서드들을 섞어 쓰기쉽고 수신하는 메서드에서는 인자로 따로 선언할수있다. 

23.확산연산자 예제.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package _23
 
def getList(){
    return [1,2,3,4]
}
 
def sum(a,b,c,d){
    return a+b+c+d;
}
println 'this.list;           : ' + this.list;
println 'getProperty ('list') : ' + getProperty ('list');
 
println 'sum(*list)  : '+  sum(*list)
println 'sum(list)   : '+  sum(list)
println '-------------'
println '[*list]       : ' + [*list];   //여기서 확산연산자의 기능을 확인해볼수있다. 오리지널 값이 그대로 .값이들어간다.
println '[]+list       : '+([]+list);   //뒤쪽에+연산이 들어가는건  list를 처리한 값을 앞쪽 리스트에 넣겠다는소리다. 즉 확산연산자랑 같은 일을한다.
println '[list]       : ' + [list];     //그자체가들어간다/
assert 10==sum(*list);
assert 10==sum(list);
assert 11==sum([2,2,3,4]);
assert 12==sum(3,2,3,4);
 
 
println '-------------'
def range=(1..3);
assert [0,1,2,3] ==[0,*range];
assert [0,1,2,3] ==[0]+range;
assert [0,1,2,3] ==[0,range].flatten();
println '[0,*range];            :  '+([0,*range]);        //확산연산자의 연산된값 즉 오리지널
println '[0,range]              :  '+[0,range];
println '[0]+range;             :  '+([0]+range);            
println '[0,range].flatten();   :  '+([0,range].flatten());  
 
 
 
println '-------------'
//맵에서도 쓸수있다.
def map =[a:1,b:2];
assert [a:1,b:2,c:3] == [c:3,*:map];
assert [a:1,b:2,c:3] == [c:3,*:map];
assert [a:1,b:2,c:3] == [c:3]+[*:map];
 
println '[c:3,*:map];   : '+([c:3,*:map]);  
println '[c:3,*:map];   : '+([c:3,*:map]);  
println '[c:3]+[*:map]; : '+([c:3]+[*:map]);
 
/*결과
this.list;           : [1, 2, 3, 4]
getProperty ('list') : [1, 2, 3, 4]
sum(*list)  : 10
sum(list)   : 10
-------------
[*list]       : [1, 2, 3, 4]
[]+list       : [1, 2, 3, 4]
[list]       : [[1, 2, 3, 4]]
-------------
[0,*range];            :  [0, 1, 2, 3]
[0,range]              :  [0, 1..3]
[0]+range;             :  [0, 1, 2, 3]
[0,range].flatten();   :  [0, 1, 2, 3]
-------------
[c:3,*:map];   : [c:3, a:1, b:2]
[c:3,*:map];   : [c:3, a:1, b:2]
[c:3]+[*:map]; : [c:3, a:1, b:2]
 
*/


여기를 보면 확산연산자의 진짜능력을 알수있다. 
[*list] : [1, 2, 3, 4] 
[]+list : [1, 2, 3, 4] 
[list] : [[1, 2, 3, 4]] 


24.키워드 use를 이용한 카테고리 섞기



'숫자형 문자열' + '숫자형 문자열' = 산술계산값 
처럼 결과값을 얻고 싶다고 한다면 ‘1’+’1’ 하면 안될것이다. 
이문제를 해결하기위해 그루비는 use키워드를 제공한다 이키워드는 카테고리를 이용해 
클래스에 인스턴스 메서드를 추가할 수 있다. 

1
2
3
use (StringCalculationCategory) {
    write(read()+read())
}


여기서 카테고리는 정적 메서드 (카테고리 메서드) 가 포함된 클래스다 
키워드 use는이 메서드의 첫째 인자로 문자열의 인스턴스를 전달해서, 인스턴스 메서드처럼 동작하게 해준다. 

1
2
3
4
5
class StringCalculationCategory {   
    static def plus(String self, String operand) {
        //구현
    }        
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package _24
 
class StringCalculationCategory {   
    static def plus(String self, String operand) {
        println 'plus String'
        try {   
            return self.toInteger() + operand.toInteger()
        }
        catch (NumberFormatException fallback){
            return (self << operand).toString()
        }
    }
     
    static def plus(def self, def operand) {
        println 'plus Def'
            return (self + operand)
    }
     
    static def plus(ArrayList self, ArrayList operand) {
        println 'plus ArrayList'
        //(self + operand) 이렇게하게되면 재귀호출이된다.  오류남
        //self.plus(operand) 이렇게하게되면 재귀호출이된다.  오류남
            int cnt = self.size;
            for (int i = 0; i < operand.size; i++) {
                self[cnt++] = operand[i]
            }
            return self;
    }
     
              
    static def minus(String self, String operand) {
        println 'minus String'
        try {   
            return self.toInteger() - operand.toInteger()
        }
        catch (NumberFormatException fallback){
            return (self << operand).toString()
        }
    }       
     
 
//여기서부터 StringCalculationCategory 클래스의 정의를 따르겠다.
use (StringCalculationCategory.class) {
    assert 1    == '1' + '0'
    assert 2    == '1' + '1'
    assert 'x1' == 'x' + '1'
    assert 0    == '1' - '1'
    println '------------------';
    println "'1' + '0'   :  ${('1' + '0') } "
    println "'1' + '1'   :  ${('1' + '1') } "
    println "'x' + '1'   :  ${('x' + '1') } "
    println "'1' - '1'   :  ${('1' - '1') } "
     
    println ([1,2,3,4]+[5,6,7,8]);
     
}
 
/*결과
plus String
plus String
plus String
minus String
------------------
plus String
'1' + '0'   :  1
plus String
'1' + '1'   :  2
plus String
'x' + '1'   :  x1
minus String
'1' - '1'   :  0
plus ArrayList
[1, 2, 3, 4, 5, 6, 7, 8]
 
*/



카테고리는 주어진 클로저와 현재 스레드에서만 동작한다 이런식의 변경이 전역적으로 반영되면 부작용이 발생되기때문이다. 
1. 특수목적의 메서드를 제공한다 StringCalculation Category에서 본것처럼 연산 메서드의 대상이 동일한 클래스이고 기존 동작을 재정의해야 할 때 사용한다 예제처럼 연산자를 재정의하는 특수한 경우에도 쓸수있다. 
2. 라이브러리 클래스에 메서드를 추가한다 ‘불안전 라이브러리 클래스’가 의심될때 사용하면 효과적이다. 
3. 함께 동작하는 서로 다른 수신자 클래스를 위한 메서드 모음을 제공한다. 예를들어 java.io.OutputStream을 위한 encryptedWrite 메서드와 java.io.InputStream을 위한 encryptedWrite 메서드와 java.io.InputStream을 위한 decryptedRead메서드를 제공할수 있다. 
3. 자바에서 Decorator 패턴을 써야 하는 겨웅에는 사용한다 하지만 메서드들을 많이 만들어야 하는 불편이 더는 겪지 않아도 된다. 
4. 클래스가 너무 커졌을 때 이를 한개의 핵심 클래스와 상황에 맞는 여러개의 카테고리로 나눌수 있다 그리고 use에는 카테고리 클래스를 여러개줄수 있으므로 이카테고리들을 핵심 클래스와 함께 사용한다. 

25.use 메서드 사용하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package _25
 
class PCategory{
    static void gogoSave(Object self){
        //self 지정하는함수
        println 'gogoSave Call '+self.gogo;;
    }
}
class GG{
 
    def gogo=[1,3]
    GG(){  
        use (PCategory){
            gogoSave();
        }
        gogoSave();
    }
    public void gogoSave(){
        println 'GG Class gogoSave()';
    }
     
     
}
 
def gg = new GG();
 
/*결과
gogoSave Call [1, 3]
GG Class gogoSave()
*/


카테고리 메서드를 Object에 할당하면 그 메서드는 모든 객체에서 사용할수 있다. 
Object보다 더작은 영역에 적용하는 법에도 관심이 있을것이다 모든Collection 클래스나 여러분이 작성한 클래스들 중 특정 인터페이스를 공유하는 비즈니스 객체들 전부에 대해 적용할수도있다. 
use에는 카테고리 클래스 여러개를 줄수 있다 카테고리들은 쉼표로 불리하거나 리스트로줘도된다. 
use(a,b,c,d) use([a,b,c,d]).. 


그루비 메타 프로그래밍




그루비에는 가로챌수 있는 지점이 셀수 없이 많다 
그중 어떤것을 고르는지에 따라 그루비가 내부적으로 제공하는 기능을 이용할수도 있고 
재정의할 코드의 양이 달라지기도 한다. 
특징들이 모여서 그루비의 메타오브젝트 프로토콜(MOP,Meta-Object Protocol)를 이룬다 

메타 프로그래밍 이해



그루비의 모든것은 GroovyObject 인터페이스에서 시작된다. 

1
2
3
4
5
6
7
8
9
package groovy.lang;
 
public interface GroovyObject {
Object invokeMethod(String name, Object args);
Object getProperty(String propertyName);
void setProperty(String propertyName, Object newValue);
MetaClass getMetaClass();
void setMetaClass(MetaClass metaClass);
}


그루비에서 당신이 작성한 모든 클래스는 GroovyClassGenerrator에 의해서 생성된다 덕분에 
그클래스들은 GroovyObject 인터페이스를 구현하여 각메서드의 디폴트 구현을 포함하게된다. 
(재정의하지 않았다면말이다.) 

보통의 자바 클래스를 그루비 클래스로 인식시키려면 GroovyObject인터페이스를 구현하면된다 더간편하게는 디폴트 메서드를 구현되어있는 GroovyObjectSupport추상클래스를 상속해도된다. 




MetaClass
GroovyObject는 메타 오브젝트 프로토콜의 핵심인 MetaClass와 밀접한 관련이 있다. 이클래스가 Groovy 클래스에 대한 모든 메터 정보를 제공한다 여기서 제공되는 메타정보들은 사용가능한 메서드,필드, 변수, 프로퍼티등이다. 그리고 다음 메서들들도 구현되어있다. 
1
2
3
4
Object invokeMethod(Object obj,String methodName,Object args)
Object invokeMethod(Object obj,String methodName,Object[] args)
Object invokeStaticMethod(Object obj,String methodName,Object args)
Object invokeStaticMethod(Object obj,String methodName,Object[] args)

위메서드들이 실제로 메서드 호출을 담당하는 메서드들이디. 이메서드들은 자바의 리플렉션 API나 자동으로 생성된 리플렉터(reflector) 클래스를 통해서 메서드를 호출한다 성능을 생각한다면 기본적으로는 리플렉터 메서드를 이용한다. 




Groovy-Object.invokeMethod의 디폴트 구현은 모든 호출을 자신의 MetaClass에게 전달한다. MetaClass는 MetaClassRegistry라는 중앙 저장소에 저장되고 꺼내진다. 





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package groovy.lang;
//디폴트 메서드를 구현되어있는 GroovyObjectSupport
import org.codehaus.groovy.runtime.InvokerHelper;
public abstract class GroovyObjectSupport implements GroovyObject {
 
    // never persist the MetaClass
    private transient MetaClass metaClass;
 
    public GroovyObjectSupport() {
        this.metaClass = InvokerHelper.getMetaClass(this.getClass());
    }
 
    public Object getProperty(String property) {
        return getMetaClass().getProperty(this, property);
    }
 
    public void setProperty(String property, Object newValue) {
        getMetaClass().setProperty(this, property, newValue);
    }
 
    public Object invokeMethod(String name, Object args) {
        return getMetaClass().invokeMethod(this, name, args);
    }
 
    public MetaClass getMetaClass() {
        if (metaClass == null) {
            metaClass = InvokerHelper.getMetaClass(getClass());
        }
        return metaClass;
    }
    public void setMetaClass(MetaClass metaClass) {
        this.metaClass = metaClass;
    }
}


MetaClassRegistry는 싱글톤 이어야하지만 아직은 아니다. 어쨌든 실질적으로 한개뿐인 이객체의 인스턴스를 얻을떄는 InvokerHelper의 팩토리 메서드를 사용한다. 



위그림 Groovy in Action책 발췌하였습다. 


출처 - http://www.gliderwiki.org/wiki/215

'Language > Groovy' 카테고리의 다른 글

DSL(Domain Specific Language) 이해하기  (0) 2016.09.09
Metaprogramming  (0) 2016.09.09
그루비의 동적 객체지향 - 2  (0) 2016.09.08
What does .delegate mean in groovy?  (0) 2016.09.08
ExpandoMetaClass  (0) 2016.09.08
:

그루비의 동적 객체지향 - 2

Language/Groovy 2016. 9. 8. 16:44

그루비가 메서드를 호출하는 자바 바이트 코드를 생성할 때는 (몇차례 전달 과정을 거친후에) 다음중 한가지 방식으로 작성한다 
1. 클래스 자체적인 invokeMethod 구현 호출(내부적으로 다른 MetaClass로 연결될수있다) 
2. 자신의 MetaClass이용 getMetaClass().invokeMethod()호출 
3. MetaClassRegisty에 저장된 객체의 자료형에 대한 MetaClass를 이용 



위그림은 Groovy in Action 책에서 발췌하였습니다 

1. 모든 메서드 호출을 중간에 가로챌(intercept) 수있다. 즉 모든 로깅(logging)이나 추적(tracing)을 가로채서 보안 관련 사항을 적용하고 트랜잭션 처리를 할수있다. 
2. 다른객체로 호출을 전달할 (relay) 수있다 예를들어 감싸는 객체(wapper)가 자신이 처리하지 못하는 메서드를 감싸진(wrapped) 객체에게 전달하는 일이 가능하다 
* 이게 클로저가 동작하는 방식이다 클로저는 메서드 호출을 자신의 대리인(delegate)에게 전달한다. 
3. 사실 다른 일을 하면서 겉으로 메서드인척 가장할(pretend)수있다 예를들어 Html클래스는 body라는 메서드가 있는것처럼 보이면서 내부적으로는 print(‘body’) 를수행할수도있다. 
* 이것이 바로 빌더(builder)의 동작방식이다 빌더들은 중첩된 구조를 만들기 위해 메서드가 있는 척한다. 


가로채기(intecept) 전달하기(relay) 가장하기(pretend)등 몇가지 구현방법
1. GroovyObject의 invokeMethod를 구현 혹은 재정의해서 메서드를 가장하거나 전달할수있다 (정의해놓은 다른 메서드들은 평사시처럼 동작함) 
2. GroovyObject의 invokeMethod를 구현 혹은 재정의하기 GroovyInterceptable인터페이스를 구현해서 메서드의 호출을 가로챈다. 
3. MetaClass를 구현하고 GroovyObject의 setMetaClss를 호출한다.(넣는다) 
4. MetaClass를 구현하고 MetaClassRegistry에 대상 클래스들을 등록한다(그루비와 자바 클래스가 모두 가능하다) 이방식은 ProxyMetaClass를 통해 지원된다. 
일반적으로 invokeMethod를 재정의 또는 구현하는것은 도트메서드 “.”메서드 이름 연산자를 재정의하는것과같다. 



26.GroovyInterceptable인터페이스를 구현해서 메서드의 호출



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package _26
 
 
import org.codehaus.groovy.runtime.StringBufferWriter
import org.codehaus.groovy.runtime.InvokerHelper
 
class Traceable implements GroovyInterceptable{                //#1
     
    private int indent = 0
     
    Object invokeMethod(String name, Object args){              //#3
        System.out.println ("n" + '  '*indent + "before methodName : '$name'")
        indent++
        def metaClass = InvokerHelper.getMetaClass(this)        //|#4
        def result = metaClass.invokeMethod(this, name, args)   //|#4
        indent--
        System.out.println ("n" + '  '*indent + "after  methodName : '$name',  reuslt : $result ")
        return result
    }     
class Whatever extends Traceable {                              //#5
    int outer(){
        return inner()       
    }
    int inner(){
        return 1
    }
}
 
def traceMe = new Whatever() //#6
 
println 'traceMe.outer() :  '+traceMe.outer();
 
/*결과
 
before methodName : 'outer'
 
  before methodName : 'inner'
 
  after  methodName : 'inner',  reuslt : 1
 
after  methodName : 'outer',  reuslt : 1
traceMe.outer() :  1
*/


아쉬운점이있다면 
1. GroovyObject 대해서 만 동작한다는점 따라서 다른 일반적인 자바클래스에서 쓸수없음 
2. 대상클래스가 이미 상속을 받고있다면 적용할수없음 


27.MetaClassRegistry에서 우리의 MetaClass를 바꿔치자


MetaClassRegistry에서 우리의 MetaClass를 바꿔치는 방법도 있을것이다. 
이를위해 ProxyMetaClass라는 클래스가 있다. 
이클래스는 기존 MetaClass의 기능과 더불어 Interceptor 를 이용한 가로채기 기능을 제공한다
 

interface Interceptor 

1
2
3
4
5
6
package groovy.lang;
public interface Interceptor {
Object beforeInvoke(Object object, String methodName, Object[] arguments);
Object afterInvoke(Object object, String methodName, Object[] arguments, Object result);
boolean doInvoke();
}



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package _27
 
class CustomInterceptor implements Interceptor{
    def beforeInvoke(Object object, String methodName, Object[] arguments) {        //메서드호출전
        println "beforeInvoke   object : ${object}, methodName : ${methodName}, arguments : ${arguments}"
        return null;
    }
    def afterInvoke( Object object, String methodName, Object[] arguments, Object result) {//메서드호출후
        println "afterInvoke   object : ${object}, methodName : ${methodName},  arguments : ${arguments}, result : ${result}"
        return result;
    }
    public boolean doInvoke() {
        return true;
    }
}
 
class Whatever1 {                           
    int outer(){
        return inner()       
    }
    int inner(){
        return 1
    }
}
 
def log = new StringBuffer("n")
def tracer = new CustomInterceptor()                  //# implements Interceptor 구현한다.
 
def proxy = ProxyMetaClass.getInstance(Whatever1.class) //적용할 클래스를 선택한다.
proxy.interceptor = tracer                              //proxy에 인텁셉터를 등록한다. (내가만든거)
proxy.use {                                           
    assert 1 == new Whatever1().outer()                
}  
/*결과
beforeInvoke   object : class _27.Whatever1, methodName : ctor, arguments : []
afterInvoke   object : class _27.Whatever1, methodName : ctor,  arguments : [], result : _27.Whatever1@e79839
beforeInvoke   object : _27.Whatever1@e79839, methodName : outer, arguments : []
beforeInvoke   object : _27.Whatever1@e79839, methodName : inner, arguments : []
afterInvoke   object : _27.Whatever1@e79839, methodName : inner,  arguments : [], result : 1
afterInvoke   object : _27.Whatever1@e79839, methodName : outer,  arguments : [], result : 1
*/


출처 - http://www.gliderwiki.org/wiki/219

'Language > Groovy' 카테고리의 다른 글

Metaprogramming  (0) 2016.09.09
그루비의 동적 객체지향 - 1  (0) 2016.09.09
What does .delegate mean in groovy?  (0) 2016.09.08
ExpandoMetaClass  (0) 2016.09.08
Groovy - 문법 (Java 와 비교해서 다른 점 위주로)  (0) 2016.09.05
:

What does .delegate mean in groovy?

Language/Groovy 2016. 9. 8. 15:55


50down voteaccepted

The delegate of a closure is an object that is used to resolve references that cannot be resolved within the body of the closure itself. If your example was written like this instead:

def say = {
  def m = 'hello'
  println m
}
say.delegate = [m:2]
say()

It prints 'hello', because m can be resolved within the closure. However, when m is not defined within the closure,

def say = {
  println m
}
say.delegate = [m:2]
say()

the delegate is used to resolve the reference, and in this case the delegate is a Map that maps mto 2.

출처 - http://stackoverflow.com/questions/8120949/what-does-delegate-mean-in-groovy


'Language > Groovy' 카테고리의 다른 글

Metaprogramming  (0) 2016.09.09
그루비의 동적 객체지향 - 1  (0) 2016.09.09
그루비의 동적 객체지향 - 2  (0) 2016.09.08
ExpandoMetaClass  (0) 2016.09.08
Groovy - 문법 (Java 와 비교해서 다른 점 위주로)  (0) 2016.09.05
:

ExpandoMetaClass

Language/Groovy 2016. 9. 8. 15:54

ExpandoMetaClass - Adding methods to interfaces

It is possible to add methods onto interfaces with ExpandoMetaClass. To do this however, it MUST be enabled globally using the ExpandoMetaClass.enableGlobally() method before application start-up.

As an example this code adds a new method to all implementors of java.util.List:


List.metaClass.sizeDoubled = {-> delegate.size() * 2 }

def list = []

list << 1
list << 2

assert 4 == list.sizeDoubled()

Another example taken from Grails, this code allows access to session attributes using Groovy's subscript operator to all implementors of the HttpSession interface:


  HttpSession.metaClass.getAt = { String key ->
        delegate.getAttribute(key)
  }
  HttpSession.metaClass.putAt = { String key, Object val ->
        delegate.setAttribute(key, val)
  }  

  def session = new MockHttpSession()

  session.foo = "bar"
출처 - http://groovy.jmiguel.eu/groovy.codehaus.org/ExpandoMetaClass+-+Interfaces.html


:

Groovy - 문법 (Java 와 비교해서 다른 점 위주로)

Language/Groovy 2016. 9. 5. 11:39

생략 가능한 구문들

Import Statements

그루비는 기본적으로 다음 패키지들을 임포트하고 있기 때문에, 다음 패키지들에 대해서는 임포트문을 생략할 수 있다.

● Groovy.lang.*

● Groovy.util.*

● Java.lang.*

● Java.util.*

● Java.net.*

● Java.io.*

● Java.math.BigInteger

● Java.math.BigDecimal

Semicolons

그루비에서 세미콜론은 필수가 아니기 때문에 생략 가능하다. 다음 두 문장 모두 유효하다.

1
2
println 'Hello World';
println 'Hello World'

하지만 두 문장을 한 라인에 코딩할 경우라면 세미콜론으로 각 문장을 구분해 줘야 한다.

1
println 'Hello'; println 'world'

Parentheses

그루비에서 괄호는 필수가 아니기 때문에 생략가능하다. 다음 두 문장은 모두 유효하다

1
2
println ('Hello World')
println 'Hello World'

하지만 간단한 테스트 코드가 아니라면 괄호를 써주는 편이 읽기에 좋다.

Return Type and the return Keyword

그루비에서는 리턴 타입을 명시할 필요도 없고, 메소드 마지막 줄에서 리턴 키워드를 사용할 필요도 없다. 리턴 타입으로 def 키워드를 사용하면 그루비가 알아서 타입을 결정해 준다.

1
2
3
4
5
6
def getPi() {
    3.14
}
 
assert getPi() in BigDecimal
assert getPi() == 3.14

Getters and Setters

그루비는 더 간단한 문법을 지원하기 위해 JavaBeans 와 유사한 GroovyBeans 를 도입했다.

GroovyBeans 내의 프로퍼티들은 public 필드와 유사하므로 getter 와 setter 를 명시적으로 정의할 필요가 없다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person {
    String firstName
    String lastName
 
    def getName() {
        firstName + ' ' + lastName
    }
 
    static void main(args) {
        def person = new Person()
        person.firstName = 'Kwan Young'
        person.lastName = 'Shin'
        assert person.firstName == 'Kwan Young'
        assert person.lastName == 'Shin'
        println person.getName()
    }
}

Access Modifiers

자바에서 접근 제어자를 생략할 경우 해당 패키지내에서만 접근가능도록 설정되지만 그루비에서 생략할 경우에는 기본 접근자가 public 으로 인식되므로 모든 곳에서 접근이 가능하다.  

Checked Exceptions

자바에서는 IOException 과 같은 Checked 예외를 처리하기 위해서 try/catch 문으로 감싸줘야 했지만 그루비에서는 Checked 예외들이 RunTimeException 으로 감싸지므로 try/catch 문을 적용할 필요가 없어졌다.

추가되거나 강화된 문법들

Assertions

그루비는 강력한 assert 키워드를 제공한다.

1
2
3
4
5
6
7
8
9
x=1
assert x
assert (x ==1)
assert ['a'] // 리스트가 비어있지 않으므로 true
assert ['a':1] // 맵이 비어있지 않으므로 true
assert 'a' // 스트링이  비어있지 않으므로 true
assert 1
assert !null
assert true

Closures

재사용 가능한 코드블록인데 파라미터로 사용될 수도 있고 실행시킬 수도 있다. 클로져는 다음과 같이 정의한다.

{arg1, arg2...-> statements}

"->" 는 클로져의 파라미터 목록과 클로저의 기능을 정의하는 블락을 구분해주는 역할을 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
def clos1 = {println "hello world!"}
clos1()
 
def clos2 = {arg1, arg2 -> println arg1+arg2}
clos2(3,4)
 
def method1(book) {
    def prefix = "The title of the book is"
    return {println prefix + book}
}
 
def clos3 = method1("Groovy")
clos3()

클로져는 보통 변수에 할당되고, 이 변수를 사용해서 클로져를 호출한다. 클로져를 파라미터로 받는 메소드도 작성 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Employee {
    def salary
    double calculateRaise(c) {
        return c(salary)
    }
}
 
Employee employee1 = new Employee(salary:1000)
def raise1 = {salary -> (salary * 1.5)}
assert employee1.calculateRaise(raise1) == 1500
 
Employee employee2 = new Employee(salary:500)
def raise2 = {salary -> (salary + 300)}
assert employee2.calculateRaise(raise2) == 800

Collective Data Types

Lists

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def x = [1, 2, 3, 4]
 
def y = ['Hi', 1, true, File]
 
def a = []
a += [1,2,3]
assert a == [1,2,3]
assert a.size == 3
// 리스트 에 4와 5를 다음과 같이 추가할 수도 있다.
a << 4 << 5
assert a == [1,2,3,4,5]
a.add(6)
assert a == [1,2,3,4,5,6]
 
assert a[0] == 1
assert a.get(0) == 1
assert a.getAt(0) == 1
assert a[-1] == 6
 
a.putAt(1,1)
assert a == [1,1,3,4,5,6]
// set 메소드는 이전 값을 리턴한다.
assert a.set(1,2) == 1
assert a == [1,2,3,4,5,6]
 
a.each{println "$it"}
a.eachWithIndex{it, index -> println item : "$it", index : "$index"}
 
a -=1
assert a == [2,3,4,5,6]
a = a.minus([2,3,4])
assert a == [5,6]

Maps

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def map = ['name':'Bashar', 'age':26, skills:['Java', 'Groovy'], 'author':true]
assert map.size() == 4
 
map += ['city':'Tucson']
assert map == ['name':'Bashar', 'age':26, skills:['Java', 'Groovy'], 'author':true, 'city':'Tucson']
 
map['state'] = 'AZ'
assert map == ['name':'Bashar', 'age':26, skills:['Java', 'Groovy'], 'author':true, 'city':'Tucson', 'state':'AZ']
 
assert map.city == 'Tucson'
assert map['city'] == 'Tucson'
assert map.get('city') == 'Tucson'
assert map.getAt('city') == 'Tucson'
assert map.skills[0] =='Java'
 
assert ['name':'Bashar', 'name':'Abdul'] == ['name':'Abdul']
 
map.each{ it -> println it.key + ":" + it.value}
map.eachWithIndex{it, index -> println "item $index - " + it.key + ":" + it.value}

Ranges

1
2
3
4
5
6
7
8
9
10
11
12
def range = 1..10
assert range == [1,2,3,4,5,6,7,8,9,10]
range = 'a'..'c'
assert range == ['a', 'b', 'c']
 
range = 1..<8
assert range == [1,2,3,4,5,6,7]
 
(1..5).each{println it}
 
assert[*1..4] == [1,2,3,4]
assert[1,*2..4] == [1,2,3,4]

새로운 헬퍼, 라이브러리, ,API 들

GDK

GDK 는 JDK 클래스의 기본 기능에 그루비 만의 기능을 추가한 것이다. 예를 들어 그루비는 JDK의 java.lang.Object, java.lang.Number, java.io.File 클래스등에 다음과 같은 함수를 추가했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// java.lang.Object
def a = [1,2,3]
assert a.any{it > 2} // At least one element satisfies the condition
assert a.every(it > 0) // All elements must satisfy the condition
 
assert a.collect{it * 2} == [2,4,6]
assert a.findAll{it > 2} == [3]
a.print(a)
 
//java.lang.Number
def x = 10;
assert a.abs() == 10
assert x.compareTo(3) == 1
assert x.div(2) == 5
def total = 0
x.downto(5) {
    number -> total += number} // Sums the numbers from 10 to 5 inclusive
assert total == 45
total = 0
x.upto(15) {
    number -> total += number} // Sums the numbers from 10 to 15 inclusive
assert total == 75
 
//java.io.File
def f = new File("C:\\temp\\groovy.txt") // Marks a file for creation
f.text = "Groovy rocks!" // File will be created if it doesn't exist
assert f.exists()
assert f.text == "Groovy rocks!"
f.append("Doesn't?")
assert f.text == "Groovy rocks!?Doesn't?"
f.renameTo(new File("C:\\temp\\groovyRenamed.txt"))
assert f.name == "groovy.txt"
[new File("C:\\temp\\groovy.txt"), new File("C:\\temp\\groovyRenamed.txt")]
.each{it.delete()}

Strings and GStrings

그루비는 JDK의 java.lang.String 타입과 GDK의 groovy.lang.GString 이라는 두가지 타입의 스트링을 지원한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 홑따옴표로 선언한 스트링은 GString 를 지원하지 않고
// 자바에서 스트링을 선언한 것과 같다고 보면 된다.
def text1 = 'Welcom to Groovy'
assert text1 as java.lang.String
 
// 쌍타옴표로 선언한 스트링은 GString 타입 스트링이다.
// 따라서 PlaceHolder 등을 지원한다. 여기서는 $language 가 PlaceHolder 임
def language = "Groovy"
def text2 = "Welcome to $language"
assert text2 == "Welcome to Groovy"
assert text2 as groovy.lang.GString
 
// 3개 따옴표는 각 라인의 마지막에 캐리지 리턴이 추가된다.
def text3 = '''
Welcome to Groovy
-----------------
'''
assert text3 == "\nWelcome to Groovy\n-----------------\n"

GDK 는 다음과 같이 string 관련 추가 메소드를 많이 제공한다.

1
2
3
4
5
6
7
8
9
10
11
def text = "Welcome to Groovy"
assert text.size() && text.length() == 17
assert text.substring(0, 7) == "Welcome"
assert text.contains("Welcome")
assert text.count("Welcome") == 1
text += "\nWhat a great language"
assert text.isNumber() == false
 
assert text.reverse() == "egaugnal taerg a tahW\nyvoorG ot emocleW"
assert text.findAll(){it > 'w'} == ['y']
assert text.replace('Groovy', 'Java') == 'Welcome to Java\nWhat a great language'

Regular Expressions

그루비에서 정규표현식은 슬래쉬 구문으로 정의할 수 있다.

1
2
3
4
5
def pattern = /abc/
assert pattern == 'abc'
 
pattern = /\s\d/
assert pattern == '\\s\\d'

그루비는 정규표현식과 함께 사용할 수 있는 3가지 연산자를 제공한다.

● ~ : 정규표현식을 정의할 때 사용함

● =~ : 정규표현식을 기반으로 검색을 수행함

● ==~ : 정규표현식을 기반으로 일치여부를 반환함

1
2
3
4
5
6
7
8
9
text = "Information technology revolution"
pattern = /\b\w*ion\b/
assert text =~ pattern
def matched = []
text.eachMatch(pattern) {match -> mached += match[0]}
println matched
assert matched.size() == 2
assert matched[0] == "Information"
assert matched[1] == "revolution"

기타

Optional Typing

그루비에서는 변수 선언시에 정적 또는 동적 타이핑이 가능하다. 변수 선언시에 java 처럼 변수 타입을 명시하는 방법을 정적 타이핑이라 하고 def 키워드를 사용해 선언하는 방식을 동적 타이핑이라고 한다. 동적 타이핑의 경우 런타임시에 그루비가 해당 변수의 타입을 결정한다.

Operator Overloading

그루비에서 모든 연산자는 메소드 호출로 해석된다. 예를 들어 1+1 은 1.plus(1) 이라는 식으로 해석되는 것이다. 따라서 이런 메소드를 오버로드하면 연산자도 함께 오버할 수 있다.


출처 - http://springsource.tistory.com/85

'Language > Groovy' 카테고리의 다른 글

Metaprogramming  (0) 2016.09.09
그루비의 동적 객체지향 - 1  (0) 2016.09.09
그루비의 동적 객체지향 - 2  (0) 2016.09.08
What does .delegate mean in groovy?  (0) 2016.09.08
ExpandoMetaClass  (0) 2016.09.08
: