温故知新: Groovy Recipes (上)

Groovy Recipes 是 2008 的老书了(没办法,貌似 Groovy 的基础读物之后就没有新的了),胜在够薄,拿来复习下 Groovy 的知识吧。

2.4 Running the Groovy Shell

Gotcha: Why Does the Groovy Shell Forget Your Variables? //好吧,我承认3年前为这个问题纠结过

groovy:000> String s = "Jane"
groovy:000> println s
===>
ERROR groovy.lang.MissingPropertyException:
No such property: s for class: groovysh_evaluate
groovy:000> s = "Jane"
groovy:000> println s
===> Jane
The Groovy shell has a curious case of amnesia when it comes
to typed variables. A variable declared with either a datatype
or a def is forgotten immediately. An untyped variable is remembered
for the duration of the shell session. This can be a source
of great confusion when copying code into the shell from a
script—in the script the code is fine, whereas in the shell it is
broken.
To make sense of this apparent discrepancy, you need to better
understand how the Groovy shell is implemented. (If you feel
your eyes beginning to glaze over, just leave the type declarations
off your shell variables, and move along....)
The Groovy shell is an interactive instance of a groovy.lang.
GroovyShell. This class is also what enables the evaluate command
discussed in Section 5.10, Evaluating a String, on page 95.
Each GroovyShell stores locally declared variables (such as s =
"Jane") in a groovy.lang.Binding.
This Binding object is essentially the “big hashmap in the sky.”
When you type println s, the shell calls binding.getVariable("s")
behind the scenes. Variables declared with a datatype (String s
= "Jane") don’t get stored in the Binding, so they can’t be found
the next time you ask for them.

2.5 Running the Groovy Console

groovyConsole 不仅仅是 groovysh 的 GUI 替代品,在 Script 菜单下的 Inspect * 功能可以提供及其方便的对象浏览功能。

理论上,groovyConsole 没什么大用,但在但遇上某个不熟悉的语法特性或是类,拿 gC 来迅速实验一下会非常方便。就这一点而言,脚本化的语言胜过其前辈不是一星半点,想想当年学 Java 的时候,你是怎么玩的?(在 IDE 中)保存 -> (编译)运行 -> 撞墙 -> 修改……

2.6 Running Groovy on a Web Server

通常我们会在 Grials 下使用 groovy,但是依然可以用简单的步骤来把 groovy 整合到你的现有系统中:

  1. Copy $GROOVY_HOME/embeddable/groovy-all-.jar to WEB-INF/lib.
    原书为 groovy.jar,但那是很多年以前的时候了(那时候貌似是N多人准备奥运的年代)
  2. Add groovy.servlet.GroovyServlet to WEB-INF/web.xml.
       1: <web-app
       2:     version="2.4"
       3:     xmlns="http://java.sun.com/xml/ns/j2ee"
       4:     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       5:     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd" >
       6:  
       7:     <servlet>
       8:         <servlet-name>Groovyservlet-name>
       9:         <servlet-class>groovy.servlet.GroovyServletservlet-class>
      10:     servlet>
      11:     <servlet-mapping>
      12:         <servlet-name>Groovyservlet-name>
      13:         <url-pattern>*.groovyurl-pattern>
      14:     servlet-mapping>
      15:  
      16:     
            
      17:     <welcome-file-list>
      18:         <welcome-file>index.jspwelcome-file>
      19:     welcome-file-list>
      20: web-app>
  3. Place your Groovy scripts wherever you'd normally place your JSP files.
    hello.groovy
       1: println "Hello ${request.getParameter('name')}"
  4. Create hyperlinks to your Groovy scripts.
       1: <a href="http://localhost:8080/g2/hello.groovy?name=Scott" >Say Helloa>

    or use form submissions
       1: <form method="get" action="hello.groovy" >
       2:     Name: <input type="text" name="name" />
       3:     <input type="submit" value="Say Hi" />
       4: form>

3.2 Optional Semicolons

OK,下面这段话不属于技术范畴,但是写的很有趣……而且很明显他是和我一伙的:

I'm tired of arguing about where the opening curly brace should go:
if it's good enough for Kernighan and Ritchie, then it's good enough
for me. The VIctor of the text editor war has been decided as far as I
am concerned. You can have your Emacs—I have a VIable alternative.
(Although some people say behind my back that I am a VIctim of an old
VIce, I won't dignify those VIcious rumors with a response.)

3.4 Optional Return Statements

关于为什么要少用括号、分号和 return,以及为什么要写尽可能短的代码:

Uh, because Al Gore said that
all of that extra unnecessary typing is the 623rd leading cause of global
warning.

3.15 Map Shortcuts

Gotcha: Why Does .class Work on Everything Except Maps?

def family = [dad:"John" , mom:"Jane" ]
println family.class
===> null
println family.getClass()
===> java.util.LinkedHashMap
Since the dot notation is used to get elements out of the
Map, calling map.class returns null instead of the class type.

注:书中没有提到 map 的减法,但是参考以下代码:

   1: def m1 = [a: 1, b: 2, c: 3]
   2: def m2 = [a: 2, b: 2, d: 4]
   3: assert m1 + m2 == [a: 2, b: 2, c: 3, d: 4]
   4: assert m1 - m2 == [c: 3, a: 1]

3.17 Closures and Blocks

在学习 Scala 的时候,Currying 是非常重要的部分。在 Groovy 中也有同样的功能,但是以前我并未注意到这一点(虽然有用到)。

   1: def calculateTax = { taxRate, amount ->
   2:     amount + (taxRate * amount)
   3: }
   4: def tax = calculateTax.curry(0.1)
   5: [10,20,30].each {
   6:     println "Total cost: ${tax(it)}"
   7: }

相比之下,Scala 的 Currying 形式上要简洁得多(毕竟人家是函数风格出身),但 Groovy 的 Currying 要好理解得多(以至于我根本没有意识到其存在)。

4.2 Autogenerated Getters and Setters

由于 @ 前缀的存在,调用者可以忽视任何 access modifier 来存取任何 POGO 变量,因此通过覆写 getter/setter 来保护私有字段并不是完全有效。另外,覆写 setter 方法时并不要求参数的型别一致,只需要参数数量一样就可以(当然,参数数量必须为 1)。

4.6 Optional Parameters/Default Values

这是一个很方便的特性,但我还未决定是否要在我的项目中使用这个特性。考虑如下代码,当写下一个带有默认值的方法时,Groovy 编译器实际上会自动生成多个方法以便于用户调用。

   1: class OptionalParas {
   2:     def p(a = 'yes', b, c, d = 'no') {
   3:         println "$a, $b, $c, $d"
   4:     }
   5: }
   6:  
   7: OptionalParas.methods.each { if(it.name == 'p') println it.parameterTypes }
   8: ===>
   9: [class java.lang.Object, class java.lang.Object, class java.lang.Object, class java.lang.Object]
  10: [class java.lang.Object, class java.lang.Object, class java.lang.Object]
  11: [class java.lang.Object, class java.lang.Object]

看起来没有任何问题对吗?不过我的直觉告诉我,在一个 5 行的类中运作完美的语法糖在 50 行的代码下未必行得通。在上面那个类中,当你想要重载方法 p 时,必须小心考虑参数的类型和个数,否则就会和原来的 p 方法产生冲突。

4.8 Calling Groovy from Java

Gotcha: Groovy Ignores the Private Modifier //看起来 private 修饰符对 Groovy 来说是直接无视了,怎么看待这个问题?

Java is the neighbor that knocks on your front door even though it knows where you hide the key. Groovy is the neighbor that lets itself in to borrow a cup of sugar and leaves you a note on the kitchen table...in practice this really hasn't been much of an issue. Private methods don't show up in the public interface, so usually the only way I know that a private method exists is if I have the source code open in front of me. If I have that level of access to the class, the onus is on me not to hopelessly screw things up...
Bjarne Stroustrup famously said, "C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do, it blows your whole leg off." Some might argue that in the case of private methods, Groovy makes it easier to blow your whole leg off. My personal take on the issue is a bit more pragmatic: I'd rather have a sharper scalpel and a better-trained surgeon than a duller blade. It's the responsibility of the developer to use this feature wisely.

5.5 Using Shell Wildcards in Groovy Scripts

   1: //in Windows:
   2: println "cmd /c dir *.groovy".execute().text
   3: def c = ["cmd" , "/c" , "dir *.groovy" ].execute().text
   4: println c
   5: //in Unix / Linux / Mac OS X:
   6: def output = ["sh" , "-c" , "ls -al *.groovy" ].execute().text
   7: println output
   8: //sadly, these don't work
   9: println "ls -al *.groovy".execute().text
  10: println "sh -c ls -al *.groovy".execute().text

5.7 Waiting for a Shell Command to Finish Before Continuing

   1: def p = "convert -crop 256x256 full.jpg tile.jpg".execute()
   2: p.waitFor()
   3: println "ls".execute().text

5.11 Calling Another Groovy Script

Calling Methods in Another Script

   1: //hello3.groovy
   2: if(args){
   3:     println "Hello ${args[0]}"
   4:     if(args.size() > 1){
   5:         println "...and your little dog, too: ${args[1]}"
   6:     }
   7: }
   8: def sayHola(){
   9:     println "Hola"
  10: }
  11: //goodbye3.groovy
  12: hello3.main()
  13: hello3.main("Glenda" )
  14: hello3.main("Dorothy" , "Toto" )
  15: println "Goodbye"
  16: h = new hello3()
  17: h.sayHola()

Calling Another Script in a Different Directory

   1: evaluate(new File("/some/other/dir/hello.groovy" ))

6.1 Listing All Files in a Directory

   1: new File('.').eachFileMatch(~/.*/.jsp/){ println it }

File.eachFileMatch technically accepts any class with a method boolean isCase(String s). This means you could expand the example to include a JspFilter class:

   1: class JspFilter {
   2:     boolean isCase(String filename) { filename.endsWith('.jsp') }
   3: }
   4:  
   5: new File('.').eachFileMatch(new JspFilter()){ println it }

6.4 Copying Files

Copying Binary Files

   1: def src = new File('src.jpg')
   2: new File('dest.jpg').withOutputStream { it.write src.readBytes() }

6.5 Using AntBuilder to Copy a File

   1: def ant = new AntBuilder()
   2: ant.copy(file: 'src.txt', tofile: 'dest.txt')

Copying a File to a Directory / Overwriting the Destination File

   1: ant.copy(file: 'src.txt', todir: '../backup')
   2: ant.copy(file: 'src.txt', tofile: 'dest.txt', overwrite: true)

6.9 Creating a ZIP File/Tarball

   1: def ant = new AntBuilder()
   2: ant.zip(basedir: "images", destfile: "../backup.zip")
   3: ant.tar(basedir: "images", destfile: "../backup.tar")
   4: ant.gzip(zipfile: "../backup.tar.gz", src: "../backup.tar")
   5: ant.bzip2(zipfile: "../backup.tar.bz2", src: "../backup.tar")
   6:  
   7: ant.zip(destfile: "../backup.zip") {
   8:     fileset(dir: "images") {
   9:         include(name: "**/*.jpg")
  10:         exclude(name: "**/*.txt")
  11:     }
  12: }

6.10 Unzipping/Untarring Files

   1: ant.unzip(src: "../backup.zip", dest: "/dest")
   2: ant.gunzip(src: "../backup.tar.gz")
   3: ant.bunzip2(src: "../backup.tar.bz2")
   4: ant.untar(src: "../backup.tar", dest: "/dest")
   5:  
   6: ant.unzip(src: "../backup.zip", dest: "/dest") {
   7:     patternset {
   8:         include(name: "**/*.jpg")
   9:         //exclude(name: "**/*.txt")
  10:     }
  11: }

 

 

 

#END

你可能感兴趣的:(Java,&,Groovy,读书笔记)