Grails コ ン #13
Java 開発者 ため 実践的 Groovy 入門
2008/10/8(資料更新:2008/11/27) 日本 ビ (株) 須江 信洋
めに - Groovy 特徴
Java 段階的 移行 可能
Java そ 使え
Java 文法 そ 使え
Java (Ant,JUnit, ) そ 使え
硬軟 対応
ョ ン 必要 こ け型 指定
Java 統合
動的 ン MOP
動的 生成 ン ン 選択
⇒日々 作業 開 幅広く使え
⇒ Java API 対話的 確認 有用
Agenda
Groovy
Java ⇒ Groovy 注意 こ
Inside Groovy
Groovy ン 入門編
ョン操作
制御構造
演算子 Category
XML 操作
Web/Web
※ 特 断 い限 Groovy-1.5.6 動作確認 い
Groovy サバイバル イド
ン (Windows)
groovy.codehaus.org 得 (groovy-
binary-1.5.6.zip) 展開
zip 内 groovy-1.5.6 適当 以
(c:¥groovy ) 展開
環境変数 GROOVY_HOME 設定
- ン -
詳細設定 選択 環境設定
環境変数 新規 以 設定
変数 : GROOVY_HOME
変数値: c:¥groovy¥groovy-1.5.6 ※各自 環境 合わ
設定
環境変数 変数 PATH 選択 編集
変数値 先頭 %GROOVY_HOME%¥bin; 追加
ン ン groovy – v 実行 正常 起動
こ 確認
Groovy を実行
ン
groovy Hoge[.groovy]
単 Groovy 実行
Groovy
groovysh
ン ン版対話型
Groovy ン
groovyConsole
GUI 版
SwingBuilder 構築さ い
groovysh 使用上 注意
中 変数 定義
× def x= hogehoge
◎ x= hogehoge
※x binding 格納さ 期待通 動作
GINA 11.2.2
そ 他 手段
IDE 使う
Eclipse Groovy plug-in
Groovy +α
NetBeans 6.5(beta)
Grails け こう手厚く支援
Groovy そ
IntelliJ IDEA
Groovy/Grails 老舗
有償 使い勝手 良好? い 使 こ い
Windows 人 Installer 版 使う 楽
.groovy け 実行 う
Gant gant ン 関連付け く
Object Browser
最後 評価 / 参照
groovysh: inspect ン
groovyConsole: Ctrl+I or Script - Inspect Last
Tips
ン ン
obj.class.name
/ / 一覧
obj.properties
obj.class.methods.name
obj.class.fields.name
文 列表現
obj.dump()
Inside Groovy
Groovy 舞台裏を探
Groovy 実行時 Java ン さ
通常 動的 生成さ
groovyc ン 明示的 生成 こ
Java 逆生成
Java 屋必携: Jad
JadClipse 組 合わ さ 手軽
記 組 合わ
Groovy 何 い 観察
⇒Groovy 追う根性 い 向け 自 こ
GroovyBean
class Book {
def title
private author
}
public class Book
implements GroovyObject { public Object invokeMethod() public Object getProperty() public void setProperty() public Object getTitle() public void setTitle() }
groovyc /jad
[Book.groovy] [Book.jad]
Script
def message = 'Hello' println "Say ${message}"
public class Hello extends Script{ public Hello()
public Hello(Binding context) public static transient void
main(String args[]) public Object run() }
groovyc /jad
[Hello.groovy] [Hello.jad]
※ 一部 い
扱わ 記述 順 実行さ
Java ⇒ Groovy の注意点
Groovy 基礎
ン 表記
基曓的 Java
一行 ン // 複数行 ン /* .. */
行曒 ; ( ン) 不要 あ 良い
戻 値指定(return) ョン 省略時 最後 式 戻 値
構文
以 Java 等 使え
package機構 文
定義 ン さ い
制御構造 for 張さ い )
演算子 式 代入 例外処理
ン ン 生成 参照 呼び出
以 Groovy 張 い
簡素化 添 演算子 GPath
張 前付
新 い 型 範囲
扱う
Java と Groovy 違い
return ワ ョン
省略時 最後 評価 式 戻 値
/ / public
時 private 無視さ
ン さ い
代替 場合 多い
例外 い
非 例外 処理
a==b 値性 a.eqauls(b) 意味
一性 定 い場合 a.is(b)
宣言
Groovy 宣言 Java 等
Java 可視性(修飾子 ) さ い
以 自動的 importさ
groovy.lang.*, groovy.util.*, java.lang.*, java.util.*, java.net.*, java.io.*, java.math.BigInteger,
java.math.BigDecimal
class Book {
private String title
void setTitle(String theTitle) {
title = theTitle
}
String getTitle(){
return title
} }
定型的
(boilerplate code)
GroovyBean
GroovyBean 特徴
可視性 設定さ い い さ
setter/getter 自動生成さ
bean.prop びbean[prop] setter/getter 変換さ class Book {
String title }
def aBook = new Book()
aBook.setTitle('Groovy Recipes')
assert aBook.getTitle() == 'Groovy Recipes' aBook.title = 'Groovy in Action'
assert aBook.title == 'Groovy in Action' aBook['title'] = 'Groovy in Action'
assert aBook['title'] == 'Groovy in Action'
前頁 定義
張さ 文 列
文 列 表記
def name = 'Bob'
def greeting = "Hello, ${name}"
def pattern = /¥d.¥d/
def multiLineString = '''
Hello, world
My name is Groovy.'''
def multiLineGString = """
Hello, ${name}
My name is Groovy."""
String
GString
GString(¥ 不要)
複数行String
複数行GString
Java 型 非
Java 以 型 い
boolean, byte, short, int, long, float, double, char
型 特徴
Java 演算子(+,-,*,/ ) 演算
ョン ワ (List, Map ) 格納 い
利用
型 特徴
Java 演算子(+,-,*,/ ) 演算 い
演算 実装 必要 あ (cf. BigInteger)
例外:String(Java 特 扱い)
ョン ワ (List, Map ) 格納
Autoboxing (Java 5~)
必要 ン 暗黙 変換 行わ う
Ease of Development 実現 .NET 影響
Groovy 型
数値
Groovy 数値 扱わ
演算 呼び出 変換さ
こ 独自 演算 定義可能
def x = 1
def y = 2
assert x + y == 3
assert x.plus(y) == 3
assert x instanceof Integer
Groovy 数値
型 接尾辞
java.lang.Integer (int 範囲内 無印整数 )
例 15, 0x1234ffff
java.lang.Long L,l
java.lang.Float F,f
java.lang.Double D,d
java.math.BigInteger G,g,(int 範囲外 無印整数 )
java.math.BigDecimal G,g,( 無印 数 ),( 指数表記 )
例 1.23, 1.4E4, 2.8e4, 1.23g
Groovy Autoboxing
def a = 1
println a.class.name // class java.lang.Integer
a= a*99999999999999999999999999
println a.class.name // class java.math.BigInteger
println a+1 // 100000000000000000000000000
a.add(1) 直観的
記述 可能
精度 大 い方 引数
型 そ え
※ 生 Ruby う 自動的 型変換 行わ い
階乗(**演算子) 例外 型変換 行わ
Groovy ッ ン 入門編
• ョン操作
• 制御構造
• 演算子 Category
•XML 操作
•Web/Web
オ とコ ョン操作
ョン
GPath
Safe dereference
張さ
List
Map
範囲 (Range 型 新規 入 )
⇒利用頻度 高い割 Java 弱い
Expando Metaclass
GPath
GPath
XPath う 簡易 表記 階層 辿
obj.a.b.c => obj.getA().getB().getC()
途中 ョン 含 そ 扱え
例 ン 持 全 getter
出力
this.class.methods.name.grep(~/get.*/).sort()
類似 技術
XPath
/document/order/lineItem[1]
<document>要素 <order>要素 1番目 <lineItem>要素
JavaScript 参照
document.form.text1.value
現在 ン text1要素 内容
GPath safe dereferencing operator
safe dereferencing operator( 安全 参照演算子 ) : ?.
GPath 辿 途中 null 含 可能性 あ 場合 利用
Java 比 ン 表記 NPE 回避 こ
直前 null 評価 中断 結果 null 返
obj?.a?.b?.c => if(obj!=null) obj.getA() else null ....
List
List型 言語
表記 可能
添 演算子([ ]) 可能
動的 張可能
def roman = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII'] assert roman[4] == 'IV'
roman[8] = 'VIII„ roman << „IX‟
assert roman.size() == 10
def list1 = []
def list2 = [] as LinkedList
空 List(java.util.ArrayList)
空 List(実装 指定)
List 便利 ソッ
assert [1,[2,3]].flatten() == [1,2,3]
assert [1,2,3].intersect([4,3,1]) == [3,1] assert [3,1,2].sort() == [1,2,3]
def list = [ [1,0], [0,1,2], [2,1,0] ]
list = list.sort { a,b -> a[0] <=> b[0] } assert list = [ [0,1,2], [1,0], [2,1,0] ] def doubled = [1,2,3].collect { it*2 } assert doubled == [2,4,6]
assert [1,2,3].join(„-‟) == „1-2-3‟
Map
Map 型 言語
表記 可能
添 演算子([ ]) 可能
def http = [
100 : 'CONTINUE', 200 : 'OK',
400 : 'BAD REQUEST' ] assert http[200] == 'OK'
http[500] = 'INTERNAL SERVER ERROR' assert http.size() == 4
def map1 = [:]
def list2 = [:] as TreeMap ← new TreeMap() 等価 空 Map(java.util.HashMap)
範囲
範囲 (Range 型 ) 言語
対応 概念 Java 在 い
表記 可能
組合 く使わ
def x = 1..10
assert x.contains(5)
assert x.contains(15) == false
assert x.from == 1
assert x.to == 10
assert x.reverse() == 10..1
def y = 1..<10
assert y.contains(10) == false
境界値 含
境界値 含 い
:
ン First Class Object 扱え
Ruby JavaScript 関数 相当
再利用や動的 有効
宣言時 環境 Lexical Scope) 保持 い
log = ''
(1..10).each{ log += it }
assert log == '12345678910'
map = ['a':1, 'b':2]
def doubler = {key, value -> map[key] = value * 2}
map.each(doubler)
assert map == ['a':2, 'b':4]
引数 1
it 省略可能
変数 代入可能
例
p [1,2,3,4,5].select {|i| i%2==0 }
Ruby
println ([1,2,3,4,5].findAll {it%2==0 } )
Groovy
1,2,3,4,5 いう数 配列 偶数 抜 出
“動詞(find)+目的語(All)”
Java 表記 準
it : 内
定義
意義 : Java ン 比較
Java 定型的 処理 中 可変 処理 埋 込 際 ン 多用さ
Java 再利用 最 単位 あ
特 Swing ActionListener 実装 頻出
// Java
final JButton button = new JButton("Push me!"); button.addActionListener(new IActionListener() {
public void actionPerformed(ActionEvent event){ System.out.println(button.getText());
} } );
// Groovy
button = new JButton('Push me!') button.actionPerformed = { event ->
println button.text }
曓来必要 処理
実装
新 く作
け い
曓来必要 処理
録 い
宣言時 35
Expando – 動的 張可能
def obj = new Expando()
obj.hello = {println "Hello"}
obj.hello() // Hello
obj.param = "world"
println obj.param // world
ン ン 生成後 や 追加 可能
⇒ Ruby ン や
JavaScript 指向 近い機能
制御構造
Groovy 真偽 定
正規表現
張さ switch case
Groovy 真偽 定
張さ 真偽 定
Boolean式 定(Java )
Matcher け false true
ョン/ 空 false そ 以外 true
文 列 空文 列 false そ 以外 true
数値 false そ 以外 true
null false そ 以外 true
利用可能 制御構文
if (式) then ... else
while (式) { ... }
for (elem in 範囲) { ... }, for (elem in ) { ... }
ョン.each() { ... }
switch(式) { case c1: ... ; break; case c2: ... ; break; deafult: ... }
制御構造 例
if (null) {
assert false } else {
assert true }
def i = 0
while (i < 10) { i++
}
assert i == 10
def clinks = 0
for (remainingGuests in 0..9) { clinks += remainingGuests }
assert clinks == (10*9)/2
def list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] for (j in list) {
assert j == list[j] }
list.each() { item ->
assert item == list[item] }
正規表現
正規表現 手軽 使う 専用 演算子 入
=~ : 正規表現検索演算子
正規表現 対 部 一致 定
式 評価 java.util.regex.Matcher 返
==~ : 正規表現 演算子
正規表現 対 全体一致 定
式 評価 Boolean 返
~ 文 列 : 正規表現 ン
文 列 ン 扱う ~/pattern/ 書式 推奨)
式 評価 java.util.regex.Pattern 返
正規表現 例
def twister = 'she sells sea shells at the sea shore of Seychelles' assert twister =~ /s.a/
assert (twister ==~ /s.a/) == false assert twister ==~ /¥w+( ¥w+)*/ def words = twister.split(/ /)
assert words.size() == 10 switch('bear'){
case ~/..../ : assert true
case ~/b.*/ : assert true; break default : assert false
}
def animals = ['cat', 'dog', 'bear', 'wolf', 'tiger', 'elephant'] assert animals.grep(~/.../) == ['cat', 'dog']
実用的に拡張さ た switch ~ case
case 多様 条件式 表記
def x = 1.23 def result = '' switch (x) {
case "foo" : result = "found foo" case "bar" : result += "bar"; break
case [4,5,6,'inList'] : result = "list"; break case 12..30 : result = "range"; break case Integer : result = "integer"; break case Number : result = "number"; break default: result = "default"
}
println result
範囲によ 分類 ( 例 : 運賃 ) を実装
range switch case 組 合わ 直感的 表記
def distance = 199 def fare=0
switch (distance) { case 0..<2 :
fare = 660; break case 2..<100 :
fare = 660 + (distance - 2)*80; break case 100..<200 :
fare = 660 + (100-2)*80 + (distance - 102)*60; break default:
throw new IllegalArgumentException() }
println fare
演算子オ バ と Category
Groovy 演算子
こ 挙動 変え
http://groovy.codehaus.org/Operator+Overloading
通常 挙動 特定 箇所 演算子
挙動 変え い場合 Category 使う
Category ( ≒ mixin)
任意個 static 持 =
use 指定 範囲内 有効 こ
ン 区 複数個 Category 差 込 こ
既 可能
Category 例:日付操作
org.codehaus.groovy.runtime.TimeCategory
Date plus()/minus()
Integer getHours()/getMinutes() 等 追加
例:現在時刻 9 時間 5 後 求
def date = new Date()use(org.codehaus.groovy.runtime.TimeCategory){ date = date + 9.hours + 5.minutes
}
⇒ Java ???
XML 操作
XML 込 以 方法 使え
Groovy XmlParser
XML全体 化
内部表現 groovy.util.Node
前空間対応
Groovy XmlSlurper
XML ン ン 化 SAX 消費 多い
内部表現 GPathResult
前空間 無視
Java DOM
Groovy独自 提供
そ 他 Java SAX/StAX や JDOM そ 使え
def plan = new XmlParser().parse(new File('plan.xml'))
println plan.name() // plan
println plan.week[0].name() // week println plan.week[0].task[0].name() // task
println plan.week[0].task[0].„@title„ // ProjectZero
簡潔 XML Parser
<?xml version="1.0" encoding="Shift_JIS" ?>
<plan>
<week capacity="8">
<task done="1" cost="2" title="ProjectZero ン " />
<task done="1" cost="3" title="Groovy 作成 " />
</week>
<week capacity="8">
<task done=“0” cost=“1” title=“ 作成" />
<task done=“0” cost=“3” title=“ 実施 " />
</week>
</plan>
plan.xml
Groovy (GPath Xpath 様 式 )
<tasks project=„2'>
<title>Install ProjectZero</title>
<title>Make Groovy project</title>
<title>Code it</title>
簡潔 XML Builder
def builder = new groovy.xml.MarkupBuilder();
builder.tasks([project:2]) {
title("Install ProjectZero")
title("Make Groovy project")
title("Code it")
}
println builder
Groovy
実行結果
XML 処理
XML 込 加工 再び XML 出力
例 以 3 ン 比較
⇒こ ン ン 意外 い
XmlParser
XmlSlurper
Dom(Groovy Dom )
詳細 こ 参照
http://groovy.codehaus.org/Processing+XML
加工内容
<?xml version="1.0" encoding="UTF-8"?>
<shopping>
<category type="groceries">
<item> ョ </item>
<item> </item>
</category>
<category type="supplies">
<item>紙</item>
<item quantity="4"> ン</item>
</category>
<category type="present">
<item when="Aug 10">
ン 誕生日
</item>
</category>
</shopping>
<?xml version="1.0" encoding="UTF-8"?>
<shopping>
<category type="groceries">
<item>贅沢 ョ </item>
<item>贅沢 </item>
</category>
<category type="supplies">
<item>紙</item>
<item when="urgent" quantity="6"> ン
</item>
</category>
<category type="present">
<item> 誕生日</item>
<item when="Oct 15"> 誕生日
</item>
</category>
</shopping>
[shopping.xml] [shopping_modified.xml]
XmlParser
def root = new XmlParser().parse("shopping.xml")
def groceries = root.category.findAll{ it.@type == 'groceries' }.item[0] groceries.each { g -> g.value = '贅沢 ' + g.text() }
def supplies = root.category.findAll{ it.@type == 'supplies' }.item[0] supplies.findAll{ it.text() == ' ン' }.each { s ->
s.@quantity = [email protected]() + 2 s.@when = 'urgent'
}
def presentCategory = root.category.find{ it.@type == 'present' } presentCategory.children().clear()
presentCategory.appendNode('item', " 誕生日")
presentCategory.appendNode('item', [when:'Oct 15'], " 誕生日") new XmlNodePrinter(new PrintWriter(System.out)).print(root)
[UpdateUsingXmlParser.groovy]
XmlSlurper
import groovy.xml.StreamingMarkupBuilder
def root = new XmlSlurper().parse('shopping.xml')
def groceries = root.category.find{ it.@type == 'groceries' }
(0..<groceries.item.size()).each { groceries.item[it] = '贅沢 ' + groceries.item[it] } def pens = root.category.find{ it.@type == 'supplies' }.item.findAll{ it.text() == ' ン' } pens.each { p ->
p.@quantity = ([email protected]() + 2).toString() p.@when = 'Urgent' }
def presents = root.category.find{ it.@type == 'present' } presents.replaceNode{ node ->
category(type:'present'){ item(" 誕生日")
item(" 誕生日", when:'Oct 15') } }
def outputBuilder = new StreamingMarkupBuilder() def result = outputBuilder.bind{ mkp.yield(root) } System.out << result
[UpdateUsingXmlSlurper.groovy]
DOM + DOMCategory
import groovy.xml.dom.DOMUtil import groovy.xml.dom.DOMCategory import groovy.xml.DOMBuilder
def doc = DOMBuilder.parse(
new InputStreamReader(new FileInputStream('shopping.xml'),'UTF-8') ) def root = doc.documentElement
use(DOMCategory) {
def groceries = root.category.findAll{ it.'@type' == 'groceries' }[0].item groceries.each { g -> g.value = '豪華 ' + g.text() }
def supplies = root.category.findAll{ it.'@type' == 'supplies' }[0].item supplies.findAll{ it.text() == ' ン' }.each { s ->
s['@quantity'] = s.'@quantity'.toInteger() + 2 s['@when'] = 'Urgent' }
def presents = root.category.find{ it.'@type' == 'present' } presents.item.each { presents.removeChild(it) }
presents.appendNode('item', "Mum's Birthday")
presents.appendNode('item', [when:'Oct 15'], "Monica's Birthday") }
println DOMUtil.serialize(root)
[UpdateUsingDomCategory.groovy]
Groovy DOM サポ 利用時 注意
DOMUtil.serialize()
内部 javax.xml.transform.* 利用 い
DOM 空 適用 常套手段
NoClassDefFoundError や ClassCastException XSLT
わ 問題 生 場合 Groovy Xalan
(xalan.jar,serializer.jar) 入 や 解
決
DOMBuilder.parse()
Reader け付け い
UTF-8 い ン ン
XML InputStream 経由 必要あ
英語圏 ン 直接FileReader 多い 涙
Web/Web サ ビ
Web 利用
REST
特 便利 機能 い 普通 HTTP
XML-RPC
groovy.net.xmlrpc.*
SOAP
GroovyWS (groovyx.net.ws.*)
Web 操作 自動化
HtmlUnit ョンや ン
GINA 13.5.2 参照
Basic 認証
Apache Commons利用
Web 操作自動化 例 : Google Search
import com.gargoylesoftware.htmlunit.WebClient def client = new WebClient()
def page = client.getPage('http://www.google.com') def input = page.forms[0].getInputByName('q')
input.valueAttribute = 'Groovy' page = page.forms[0].submit()
def hits = page.anchors.grep { it.classAttribute == 'l' } [0..2]
hits.each { println it.hrefAttribute.padRight(30) + ' : ' + it.asText() } [GoogleSearch.groovy]
※HtmlUnit 必要
http://htmlunit.sourceforge.net/
Google検索 結果 位3件 得
Basic 認証
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod; HttpClient client = new HttpClient()
client.getState().setCredentials(
new AuthScope('192.168.1.1', 80, 'RealmName'), new UsernamePasswordCredentials('user','pass') )
GetMethod get = new GetMethod('http://192.168.1.1') get.setDoAuthentication(true)
def status = client.executeMethod(get) println(status)
println(get.getResponseBodyAsString())
[BasicAuth.groovy]
※ Java 標準API け 実現 面倒 Commons HttpClient 利用
書籍
Groovy ン ョン
日 ュ ョン 絶賛 売中
待望 日曓語版Groovy曓
ン Groovy-1.5 全面
Groovy 基礎 活用 幅広い話題 網羅
Grails 徹底入門
翔泳社 絶賛 売中
Groovy 解 章あ 得 !
Groovy曓既刊 英語版
"Groovy in Action" (Manning)
初 Groovy曓 決定版 Groovy-1.0
"Programming Groovy" (Pragmatic Bookshelf) 初心者向け 入門書 Groovy-1.5
"Groovy Recipe" (Pragmatic Bookshelf) Groovy-1.5