Programming Cljr – working with Java

Working with Java

In this chapter, you will see how Clojure access to Java is convenient, elegant, and fast: 
• Calling Java is simple and direct
  Clojure provides syntax extensions for accessing anything you could reach from Java code: classes, instances, constructors, methods, and fields. 
  Although you will typically call Java code directly, you can also wrap Java APIs and use them in a more functional style. 

• Clojure is fast, unlike many other dynamic languages on the JVM. 
  You can use custom support for primitives and arrays, plus type hints, to cause Clojure’s compiler to generate the same code that a Java compiler would generate. 

• Java code can call Clojure code, too. Clojure can generate Java classes on the fly. On a one-off basis, you can use proxy, or you can generate and save classes with gen-and-save-class.

 

• Clojure’s exception handling is easy to use. Better yet, explicit exception handling is rarely necessary. 
  Clojure’s exception primitives are the same as Java’s. However, Clojure does not require you to deal with checked exceptions and makes it easy to clean up resources using the with-open idiom.

 

Calling Java

Accessing Constructors, Methods, and Fields

creating a Java object

(new java.util.Random)
java.util.Random@4f1ada
 
(def rnd (new java.util.Random))
 
(. rnd nextInt)
-791474443

import package

(import '(java.util Random Locale)
        '(java.text MessageFormat))

 

.. macro

The .. macro is great if the result of each operation is an input to the next.

(.getLocation (.getCodeSource (.getProtectionDomain (.getClass '(1 2)))))
 
(.. '(1 2) getClass getProtectionDomain getCodeSource getLocation) ;使用..宏的版本, 更清晰? 少写些括号?

Doto

Sometimes you don’t care about the results of method calls and simply want to make several calls on the same object
The doto macro makes it easy to make several calls on the same object:

(doto class-or-inst & member-access-forms)

As the “do” in doto suggests, you can use doto to cause side effects in the mutable Java world. For example, use doto to set multiple system properties:

(doto (System/getProperties)
  (.setProperty "name" "Stuart")
  (.setProperty "favoriteColor" "blue"))

 

下面的表列出基本语法和响应的语法糖,

 

Using Java Collections

Clojure’s collections supplant the Java collections for most purposes. 
Clojure’s collections are concurrency-safe, have good performance characteristics, and implement the appropriate Java collection interfaces. 
So, you should generally prefer Clojure’s own collections when you are working in Clojure and even pass them back into Java when convenient.

暂时不清楚为什么要在clojure里面使用java collection, 用到再说...

 

Convenience Functions

memfn macro

Try passing .toUpperCase to map in order to upcase a vector of strings:

(map .toUpperCase ["a" "short" "message"])
java.lang.Exception:\
Unable to resolve symbol: .toUpperCase in this context

只有clojure function可以作为参数, 对于Java method需要用memfn封装一下 
The problem is that toUpperCase( ) is a Java method, not a Clojure function. 
This member-as-function idiom is a common one, so Clojure provides the “member function” memfn macro to wrap methods for you:

(map (memfn toUpperCase) ["a" "short" "message"])
("A" "SHORT" "MESSAGE")

As a preferred alternative to memfn, you can use an anonymous function to wrap a method call:

(map #(.toUpperCase %) ["a" "short" "message"])
("A" "SHORT" "MESSAGE")

instance?

Checking whether an object is an instance of a certain class. Clojure provides the instance? function for this purpose:

(instance? Integer 10)
true
(instance? Comparable 10)
true
(instance? String 10)
false

string format

Java provides a string format method. Because the message signature for Java’s format is slightly inconvenient to call in Clojure and because string formatting is so common, Clojure provides a wrapper: (format fmt-string & args) 
You use format like this:

(format "%s ran %d miles today" "Stu" 8)
"Stu ran 8 miles today"

 

Optimizing for Performance

Using Primitives for Performance

In the preceding sections, function parameters carry no type information
Clojure simply does the right thing. Depending on your perspective, this is either a strength or a weakness. 
It’s a strength, because your code is clean and simple and can take advantage of duck typing. 
But it’s also a weakness, because a reader of the code cannot be certain of data types and because doing the right thing carries some performance overhead.

Clojure的参数是没有type信息的, 当然比较简单, 清晰, 当然自然编译器需要做更多的事情, 所以如果对效率要求很高, 可以指定类型.

 

例子, 对于下面的例子, 求和

(defn sum-to [n]
  (loop [i 1 sum 0]
    (if (<= i n)
      (recur (inc i) (+ i sum))
      sum)))

对于没有指定参数类型版本的时间测试,

(dotimes [_ 5] (time (sum-to 10000))) ;dotimes will execute its body repeatedly, here 5 times
"Elapsed time: 0.778 msecs"
"Elapsed time: 0.559 msecs"
"Elapsed time: 0.633 msecs"
"Elapsed time: 0.548 msecs"
"Elapsed time: 0.647 msecs"

To speed things up, you can ask Clojure to treat n, i, and sum as ints:

(defn integer-sum-to [n] 
  (let [n (int n)] 
    (loop [i (int 1) sum (int 0)] 
      (if (<= i n) 
    (recur (inc i) (+ i sum)) 
    sum))))

可以看出, 确实指定类型后, 效率提高很多,

(dotimes [_ 5] (time (integer-sum-to 10000)))
"Elapsed time: 0.207 msecs"
"Elapsed time: 0.073 msecs"
"Elapsed time: 0.072 msecs"
"Elapsed time: 0.071 msecs"
"Elapsed time: 0.071 msecs"

Clojure’s convenient math operators (+, -, and so on) make sure their results do not overflow. 
Maybe you can get an even faster function by using the unchecked version of +, unchecked-add:

对于操作不check是否overflow也可以提高效率, 不过这种优化不到万不得已最好别用, 因为难于维护, 一旦overflow导致数据出错, 难以排查.

(defn unchecked-sum-to [n]
  (let [n (int n)]
    (loop [i (int 1) sum (int 0)]
      (if (<= i n)
	(recur (inc i) (unchecked-add i sum))
	sum))))

 

Adding Type Hints

Clojure supports adding type hints to function parameters, let bindings, variable names, and expressions. These type hints serve three purposes: 
• Optimizing critical performance paths 
• Documenting the required type 
• Enforcing the required type at runtime

(defn wants-a-string [#^String s] (println s))
#^String s 等于 #^{:tag String} s, 因为tag过于常用, 所以不写默认就是tag

 

Creating and Compiling Java Classes in Clojure

Often, Clojure's Java API will not be sufficient for integrating Java code with Clojure code. 
Many Java libraries require you to implement a particular interface or extend a particular base class. 
Fortunately, Clojure can create real Java classes, with methods that can be called like any other Java method, without requiring you to write any “wrapper” code in Java.

对于调用Java库, 有时候需要传入interface或class, 简单的例子是parse XML的sax库, 必须要传入实现了handles的类或接口, 所以需要在clojure里面直接生成java classes

先跳过, 后面再补充 

To use a SAX parser, you need to implement a callback mechanism. 
The easiest way is often to extend the DefaultHandler class. In Clojure, you can extend a class with the proxy function:

(proxy class-and-interfaces super-cons-args & fns)
(def print-element-handler
     (proxy [DefaultHandler] []
       (startElement          ;对于startElement的handle function
	[uri local qname atts]
	(println (format "Saw element: %s" qname)))))

 

Exception Handling

In Java code, exception handling crops up for three reasons: 
• Wrapping checked exceptions (checked exceptions, http://blog.sina.com.cn/s/blog_492c74050100031s.html)

Checked Exceptions 
Java’s checked exceptions must be explicitly caught or rethrown from every method where they can occur. 
This seemed like a good idea at first: checked exceptions could use the type system to rigorously document error handling, with compiler enforcement. Most Java programmers now consider checked exceptions a failed experiment, because their costs in code bloat and maintainability outweigh their advantages. For more on the history of checked exceptions, see http://tinyurl.com/checked-exceptions-mistake

• Using a finally block to clean up nonmemory resources such as file and network handles 
• Responding to the problem: ignoring the exception, retrying the  operation, converting the exception to a nonexceptional result, and so on

在java中为什么要做exception handling,

首先, 对于checked exceptions, 你必须handle, 要不catch, 要不rethrown 
其次, 需要在finally block里面释放资源, 如文件和网络等资源 
最后, 不想程序因为exception简单的停止, 需要做恢复和ignore操作. 比如爬网页, 一个网页发生异常, 需要retry几次, 或继续爬取

In Clojure, things are similar but simpler. The try and throw special forms give you all the capabilities of Java’s try, catch, finally, and throw.

But you should not have to use them very often, because of the following reasons: 
• You do not have to deal with checked exceptions in Clojure. 貌似也只有Java有checked exceptions 
• You can use macros such as with-open to encapsulate resource cleanup.

说白了, 前面两种在clojure里面都不需要或简单的处理, 只有第三种无法避免...

 

Keeping Exception Handling Simple

The absence of exception wrappers makes idiomatic Clojure code easier to read, write, and maintain than idiomatic Java. That said, nothing prevents you from explicitly catching, wrapping, and rethrowing exceptions in Clojure. It simply is not required. You should catch exceptions when you plan to respond to them in a meaningful way.

 

Cleaning Up Resources, 对于outside of garbage-collected memory 
Garbage collection will clean up resources in memory. If you use resources that live outside of garbage-collected memory, such as file handles, you need to make sure that you clean them up, even in the event of an exception. In Java, this is normally handled in a finally block. 
If the resource you need to free follows the convention of having a close method, you can use Clojure’s with-open macro:

(with-open [name init-form] & body)

Clojure使用with-open做了很好的封装, 你不用再自己去写finally block...

下面的例子, 在split里面open file, 使用with-open保证这个file一定会被close, 很简单.

(use '[clojure.contrib.duck-streams :only (spit)])
(spit "hello.out" "hello, world")

; from clojure-contrib
(defn spit [f content]
  (with-open [#^PrintWriter w (writer f)]
  (.print w content)))

 

当然你不满意默认的finally逻辑, 想自己实现也是可以的

If you need to do something other than close in a finally block, the Clojure try form looks like this:

(try expr* catch-clause* finally-clause?)
; catch-clause -> (catch classname name expr*)
; finally-clause -> (finally expr*)
 
(try
 (throw (Exception. "something failed"))
 (finally
  (println "we get to clean up")))

 

Responding to an Exception 
The most interesting case is when an exception handler attempts to respond to the problem in a catch block. 
As a simple example, consider writing a function to test whether a particular class is available at runtime:

例子, 测试类在当前环境中是否ready?

; not caller-friendly
(defn class-available? [class-name]
  (Class/forName class-name))

This approach is not very caller-friendly. The caller simply wants a yes/no answer but instead gets an exception:

(class-available? "borg.util.Assimilate")
java.lang.ClassNotFoundException: borg.util.Assimilate

问题就是不是很友好, 直接抛异常, 让人用的很别扭, 所以可以封装一下, 提供yes/no

; better-class-available
(defn class-available? [class-name]
  (try
   (Class/forName class-name) true
   (catch ClassNotFoundException _ false))) 

本文章摘自博客园,原文发布日期:2013-01-31
时间: 2024-12-27 23:05:53

Programming Cljr – working with Java的相关文章

Java I/O 模型的演进

什么是同步?什么是异步?阻塞和非阻塞又有什么区别?本文先从 Unix 的 I/O 模型讲起,介绍了5种常见的 I/O 模型.而后再引出 Java 的 I/O 模型的演进过程,并用实例说明如何选择合适的 Java I/O 模型来提高系统的并发量和可用性. 由于,Java 的 I/O 依赖于操作系统的实现,所以先了解 Unix 的 I/O 模型有助于理解 Java 的 I/O. 相关概念 同步和异步 描述的是用户线程与内核的交互方式: 同步是指用户线程发起 I/O 请求后需要等待或者轮询内核 I/O

在linux中执行java.jar时报错

问题描述 [devafs:/home/devafs/batch/sbin] sh start.shUsage: java [-options] class [args...] (to execute a class) or java [-options] -jar jarfile [args...] (to execute a jar file)where options include: -d32 use a 32-bit data model if available -d64 use a

OpenRules v5.4.1发布 java开放源代码商业规则管理框架

OpenRules基于java完全开放源代码的商业规则管理框架.它有效的利用了MS Excel, Eclipse IDE 和其它java开源类库去构造,维护,部署,执行不同的复杂商业逻辑的规则引擎. OpenRules® Release 5.4.1 introduces a new version of Rule Solver for defining and solving constraint satisfaction and optimization problems. Rule Solv

JAVA 入坑教程 | 章节一 基础概念

JAVA 章节一 基础概念 学JAVA,首先得知道JAVA是什么,JAVA能做什么事,JAVA有哪些概念和特性,首先我们从JAVA是什么开始入手带大家入坑: 前两篇内容我们提到了,JAVA的一些概念和特性,这篇文章我们来补足剩余的基本概念,当然,每篇文章的概念性东西会根据节奏走,每篇文章不会一次性带入太多的概念. OOP:Object Oriented Programming , 面向对象的程序设计 JAVA  三大特性 JAVA  其它的基础概念 一.OOP思想 首先我们来回顾下历史,OOP的

我的Java开发学习之旅------&amp;gt;在Dos环境下Java内部类的编译和运行

习惯了在IDE工具上进行代码编写,连最基本的Javac命令和Java命令都忘记的差不多了,今天对一个Java内部类进行编译和运行的时候,就出糗了.IDE是把双刃剑,它可以什么都帮你做了,你只要敲几行代码,点几下鼠标,程序就跑起来了,用起来相当方便.你不用去关心它后面做了些什么,执行了哪些命令,基于什么原理.然而也是这种过分的依赖往往让人散失了最基本的技能,当到了一个没有IDE的地方,你便觉得无从下手,给你个代码都不知道怎么去跑. 首先我在C盘上编写了一个InnerClassTest.java代码

8个简单部分开启Java语言学习之路 附java学习书单_java

之前为大家推荐了java语言阅读书籍,下面为大家介绍从哪几个方面开始学习java语言,具体内容如下 1. Java语言基础      谈到Java语言基础学习的书籍,大家肯定会推荐Bruce Eckel的<Thinking in Java>.它是一本写的相当深刻的技术书籍,Java语言基础部分基本没有其它任何一本书可以超越它.该书的作者Bruce Eckel在网络上被称为天才的投机者,作者的<Thinking in C++>在1995年曾获SoftwareDevelopment J

什么时候不应该使用 XML(3)

xml   当问题非常简单时 当它可能产生其大无比的文件时 当应用程序是"一次性的"时 当需要使用 Unix 面向行的文本处理工具时 涉及使用 XML 来进行程序间通信时,有探讨的余地.但当涉及人机通信,如编程语言或配置文件时,XML 可能提供了最不自然的人机界面. 我的论点归结人与计算机硬件之间的一个问题.人类擅长处理隐含的结构,而计算机希望处理明确的结构,它设计成擅长于我们所不擅长的.计算机语言越接近自然语言,它对人类越自然,但实现越困难.在这场拔河竞赛中,稳妥的折衷方案可能是使用

Jsp分页实例代码

js|分页 //Author tar(bigangell@yesky.com) //您可以任意拷贝复制该代码,但请加上以上作者信息 //有任何问题请与我联系 //效果请看 http://210.77.144.64/kk/java/java/article.jsp <%@ page import="java.sql.*" %> <%@ page import="java.io.*" %> <%@ page import="jav

Microsoft .NET与J2EE的比较[E]

j2ee|比较 Microsoft .NET vs. J2EE: How Do They Stack Up? What exactly is the .NET platform [and] how does the .NET architecture measure up against J2EE? Java runs on any platform with a Java VM. C# only runs in Windows for the foreseeable future. .NET