posts - 5, comments - 0, trackbacks - 0, articles - 0
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

匿名内部类-回调-闭包

Posted on 2011-11-24 16:44 火炎炎 阅读(873) 评论(0)  编辑  收藏

首先明确闭包的概念:一个代码段被用来做为方法的参数.
java中没有直接使用某个方法做为另一个方法的参数的,java使用匿名内部类来模拟这种情况。

匿名内部类往往是做为一个内部类(接口)的具体实现。在一些平台类(platform class)中有一些模板方法。模板方法的包含了固定流程。其中某些步骤是调用了内部类(接口)中的某些方法。但是平台类将这些方法的具体实现延迟到了业务类中。业务类调用平台类的模板方法,但是传入匿名内部类的实现做为模板方法的参数。

示例:

 

package callback;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class AnonymousBusinessTemplateExample2 {

// 内部接口也是回调接口,只定义抽象方法。
private interface Callback {
Object doIt(Connection conn)
throws SQLException;
}

// 模板方法(抽象)
private Object execute(Callback callback) throws SQLException {
Connection conn
= openConnection();
try {
return callback.doIt(conn);
}
finally {
closeConnection(conn);
}
}

// 业务方法(具体)
public Object sqlQuery(final String sql) throws SQLException {
//匿名内部类做为模板方法的参数来模拟闭包
return execute(new Callback() {
public Object doIt(Connection conn) throws SQLException {
return conn.createStatement().executeQuery(sql);
}
});
}

public Connection openConnection() throws SQLException {
return DriverManager.getConnection("", null);
}

public void closeConnection(Connection conn) throws SQLException {
if (conn != null && !conn.isClosed()) {
conn.close();
}
}
}

 

 

 

一般内部接口比内部类用的更多。内部类中的方法可以有默认的实现。匿名内部类做为业务方法的参数传入时,会override默认的方法。内部接口的话,没有默认的实现。完全将具体的实现交给了匿名内部类。

匿名内部类的思想是回调,即好茉坞原则。回调的一个好处是decouple。 客户端只需要关心业务(比如匿名内部类的具体实现)而不用再关心一些资源的连接释放什么的,这些交给平台类中的模板方法。ruby的闭包还支持对数组中的每个元素,文件中的每行,结果集中的每个记录的操作。而用java实现这样的迭代并操作其中元素非常麻烦。感觉java中用的多的偏模板方法,即逻辑中固定一些流程,初始化及释放某些资源。






动态回调函数、匿名内部类和spring中的excute方法

    公司目前采用了spring框架来构建和管理整个web项目。对于持久层的处理,使用了由spring框架提供的对hibernate3的封装。这样做无非是为了使用spring提供的对事务的统一管理。当我们用到由spring所封装的hibernate的时候一定会用到一个类:HibernateTemplate.这是一个对持久层处理封装的非常完整的类,包括对session的管理(事实上session的获取于释放是一个令人头疼的问题)等等,我们通常会使用HibernateTemplate的excute方法来进行数据库操作(即使我们调用的也许是别的类似于find、get之类的方法,但是实质上最终还是转变为了调用excute方法)。对于第一次使用这个方法一定会存在困扰。因为excute方法所需要的参数一个HibernateCallback类型的参数。而在excute方法体内部回调了HibernateCallback类型的doInHibernate方法。这是一个典型的对象回调。就到目前为止也许一切都很清晰。但是实际上如果阅读了HibernateTemplate的内部代码就会发现,对于像get、find这样的方法最终都回调用excute来完成数据库操作但是调用形式看起来却很奇怪:

public Object get(final Class entityClass, final Serializable id, final LockMode lockMode)

        throws DataAccessException

    {

        return execute(new HibernateCallback() {

            public Object doInHibernate(Session session)

                throws HibernateException

            {

                if(lockMode != null)

                    return session.get(entityClass, id, lockMode);

                else

                    return session.get(entityClass, id);

            }

        }, true);

    }

    Excute方法的参数是一种匿名类的方式。为什么要采用匿名类呢(不管怎么说匿名类看起来总是让人觉得不舒服)?这个地方是否必须采用匿名类呢?

    首先我们来想一想这段代码涉及到几个关键点:1、回调:excute方法会回调HibernateCallback类型的doInHibernate方法;2、匿名类参数:我们为excute方法提供的参数并不是一个真正生命出来的HibernateCallback实例。3、动态创建回调方法:如果我们打开HibernateCallback类就会发现,其实这是一个abstract类型的类,而他声明了唯一的一个ie抽象方法就是doInHibernate。问题似乎已经明朗了,如果不采用匿名类,我们需要做的是为HibernateCallback创建一个实现类,并且实现doInHibernate方法。但是最要命的问题是doInHibernate方法的实现对于我们的实际需求来说每一次调用可能都是不一样的(在doInHibernate方法中我们使用session进行数据库操作,对于不同的业务逻辑,方法实现必定是不一样的),采用了匿名类我们不用在代码重创建新的类型,而且可以动态的创建我们所需要的回调函数。

    总结一下,我们上面所讲的并非是如何使用HibernateTemplate这个类。我们得到的结论是:当我们需要动态的创建回调函数的时候,匿名内部类是一个好的方式。注:这种需要动态创建的回调方法通常是一个interface中的接口或者abstract class中的抽象方法。



只有注册用户登录后才能发表评论。


网站导航: