一个 java 的难题

比如说你有一个对象是 foo 类的,它提供了几个方法都能够接受 Integer,也能返回 Integer,那么你怎么动态的通过条件选择调用谁?这个看起来很容易的事情,却因为 generics 的存在而让人大费周折。

对 C++ 来说这一切都是编译时的事情,以下面的类为例:

#ifndef FUNCBIND_HPP
#define FUNCBIND_HPP
#include <iostream>

struct foo {
  int plus (int a) const {
    std::cout << "plus " << a << "\n" ;
    return a + 1 ;
  }

  template <class T> int
  size (T t) const {
    std::cout << "sizeof " << t << "\n" ;
    return sizeof (T) ;
  }
} ;
#endif

下面是 C++11 的做法,

#include "funcbind.hpp"
#include <functional>

int
main(int, char**) {
  foo bar ;
  bar.plus (1) ;
  bar.size (2) ;

  std::function<int (const foo&, int)> f
    = [] (const foo& b, int x) {return b.plus (x) ;} ;
  f (bar, 1) ;
  std::function<int (const foo&, int)> g
    = [] (const foo& b, int x) {return b.size (x) ;} ;
  g (bar, 2) ;

  return 0 ;
}

即使是 C++03 下面没有 anonymous function,我们也能

#include "funcbind.hpp"
#include <boost/mem_fn.hpp>
#include <boost/function.hpp>

int
main(int, char**) {
  foo bar ;
  bar.plus (1) ;
  bar.size (2) ;

  boost::function<int (const foo&, int)> f =
    boost::mem_fn (&foo::plus) ;
  f (bar, 1) ;

  boost::function<int (const foo&, int)> g =
    boost::mem_fn (&foo::size<int>) ;
  g (bar, 2) ;

  return 0 ;
}

很自然的做法就是把这些东西扔到一个 map 里面,随用随取。但是对 Java 这个事情就变得奇怪起来了。一种办法自然就是搞个 interface 出来,为不同的方法实现不同的实现

public interface Silly {
  public Integer doSilly (Foo o, Integer i) ;
}

这样能实现和 C++ 类似的功能,但是累赘很多,在没有闭包也没有成员函数指针等类似的概念的情况下,有更好的解决方案么?hmm,何不把事情弄到运行时来做?OK,我们知道 java.lang.reflect.Method 提供了对方法的封装,那剩下的就是需要获得这个方法,这通常就可以用 java.lang.Class.getDeclaredMethod 获得,hmm 看起来多么好的解决方案呀:

public class Foo {
  // ...
}
Foo bar ;
String funcName = "plus" ;
Method f = Foo.class.getDeclaredFunction (funcName, Integer.class) ;
f.invoke (bar, 1) ;

可是当你对拥有 generic 的函数使用的时候就悲催了,由于 type erasure,getDeclaredFunction 第二个参数变成了 Object.class 才行,否则会抛出异常。如果走运行时的路线,我们却不能直接仅仅让 funcName 替换生成对应的 Method 对象,我们还得运行时多传递一个 Class 对象保证在做 reflection 的时候没错。否则如果做编译时传递的话不比 C++ 干净多少,而且本身通过 reflection 看起来是要搞运行时的却因为这个类型不得不做编译时代码。C++11 一致的风格看起来完全不比关心这个函数是不是 generic 的,C++03 显得稍微有点不一致,而 Java 这时感觉有点不伦不类… 也许这就是 java 奇怪的地方,有点动态特性却不能比较完成的应用一些类似的动态语言的事情。兴许这就是为啥咱要抛弃 java 的原因了…

——————
And Abimelech said, What is this thou hast done unto us? one of the people might lightly have lien with thy wife, and thou shouldest have brought guiltiness upon us.

Advertisements
一个 java 的难题

一个有关“一个 java 的难题”的想法

  1. guojc 说:

    type erase和empty object size根本是jvm老大难问题。我拿scala搞得时候这两个问题也巨头疼。可其它platform不成气候啊。haskell太research了, c++没有动态反射和centeral repository. python根本没有速度。

      1. guojc 说:

        .net frame 在generic上倒是do it right, 其gc据说不错。F#看上去很nice。可有多少人用windows写后台程序。。。 mono感觉还不是特靠谱。

  2. zt 说:

    .NET… 汗一记,很久都没考虑过 MS 家独有的东西了,mono 之类的话传说 GNOME 里面不少用这个写的,比如 gnome-do 之类的,效率什么的不清楚了,不过都没碰过。

    to JC:你终于出来冒泡了啊,我准备 scala 和 groovy 一起上了,组里面某位同志要上 jruby + scala,后者用来取代 java 的代码。不过觉得似乎 groovy 语法上面友好很多,可能性能的确要差不少,不过似乎自 2.0 之后 improve 了。c# -> f# 了啊,中间有 DE 么?

    to FreePeter:是啊,第一次被这个 generic 搞了一次之后就格外小心了,不想这次又被阴了…

    1. guojc 说:

      groovy看下jvm 7的性能吧。不过好像compile时要target jvm7。有新的dynamicInovke instruction. F#是functional的意思。是ocaml的近亲,也是functional 和oo的合体。

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s