是否有可能通过哪个子类看到父在Java中调用的静态方法?

时间:2009-12-07 14:21:29

标签: java ruby-on-rails reflection static

首先是一点背景。我正在研究尽可能干净简洁地在Java中实现Ruby的ActiveRecord的可能性。为此,我需要允许以下类型的方法调用:

Person person = Person.find("name", "Mike");

哪个会解决为:

ActiveRecord.find(Person.class, "name", "Mike");

计划是让Person扩展ActiveRecord,它将具有带有两个参数(列,值)的静态find方法。这个方法需要知道它是通过Person.find调用的,而不是像Car.find那样调用另一个域类,并调用find(Class,String,Object)方法来执行实际的操作。

我遇到的问题是找出通过哪个子类ActiveRecord调用静态查找方法(两个参数)。以下是一个简单的测试用例:

public class A {
  public static void testMethod() {
    // need to know whether A.testMethod(), B.testMethod(), or C.testMethod() was called
  }
}

public class B extends A { }
public class C extends A { }

public class Runner {
  public static void main(String[] args) {
    A.testMethod();
    B.testMethod();
    C.testMethod();
  }
}

到目前为止找到的解决方案是使用aspectJ进行加载时或编译时编织。这将涉及在A中的testMethod()上放置一个调用拦截器,并找出用于调用它的签名。我完全是为了加载时间编织,但设置它(通过VM args)的设置有点复杂。

有更简单的解决方案吗?

这在java中是否完全可能,或者需要在groovy / ruby​​ / python中完成?

使用像ActiveRecord.find这样的静态负载和Person.save实例的方法会更好吗?

7 个答案:

答案 0 :(得分:3)

您不能在Java中覆盖静态方法,因此通过子类对静态方法的任何调用都将在编译时绑定到基类。因此,在运行应用程序之前,对B.testMethod()的调用将绑定到A.testMethod。

由于您在运行时查找信息,因此无法通过普通的Java操作来使用它。

答案 1 :(得分:1)

正如其他人所指出的那样,我认为这个问题在Java中是不可解决的。静态方法实际上并没有像非静态方法那样继承。 (对不起,如果我没有正确使用这个术语。)

然而,在我看来,如果您愿意稍微修改一下界面,有很多方法可以达到预期的效果。

最明显的是使用父类进行调用。写作有什么问题

Person person=(Person)ActiveRecord.find(Person.class, "name", "Mike");

或者,您可以先创建记录类型的实例,然后执行查找以填充它。例如

Person person=new Person();
person.find("name", "Mike");

此时你有一个Person对象,如果你需要从超类型的函数中知道它的类,你只需要做“this.getClass()”。

或者,您可以创建一个虚拟Person对象来进行调用,只是为了让您在必要时执行getClass()。然后你的发现看起来像:

Person dummyPerson=new Person();
Person realPerson=dummyPerson.find("name", "Mike");

顺便说一句,在我看来,任何尝试拥有一个通用的ActiveRecord类都意味着find的返回类型必须是ActiveRecord而不是特定的记录类型,所以你可能需要将它转换为从通话返回时的正确类型。击败它的唯一方法是在每个记录对象中明确覆盖查找。

我已经有很多次写过一些通用的记录处理代码,但我总是避免为每种记录类型创建Java对象,因为这总是会变成编写一大堆代码。我更喜欢保持Record对象完全通用,并且具有字段名称,索引,无论是内部数据和名称。如果我想从“bar”记录中检索“foo”字段,我的界面将如下所示:

Record bar=Record.get(key);
String foo=bar.get("foo");

而不是:

BarRecord bar=BarRecord.get(key);
String foo=bar.getFoo();

不那么漂亮,它限制了编译时错误检查,但实现的代码却少了。

答案 2 :(得分:0)

你不会在Java中这样做。你可能会做更多的事情:

public interface Finder<T, RT, CT>
{
    T find(RT colName, CT value);
}

public class PersonFinder
    implements Finder<Person, String, String>   
{
    public Person find(String nameCol, String name)
    {
        // code to find a person
    }
}

public class CarFinder
    implements Finder<Car, String, int>   
{
    public Person find(String yearCol, int year)
    {
        // code to find a car
    }
}

答案 3 :(得分:0)

这是可能的,但它很昂贵。

如果你能找到一种只调用一次的方法,那么你就可以了。

您可以创建一个新的例外并查看第一帧,然后您就会知道是谁调用它。问题是它不是高效的。

例如使用此answer,可以创建一个这样的记录器:

 class MyClass {
      private static final SomeLogger logger = SomeLogger.getLogger();
      ....
  }

让记录器创建一个不同的实例,具体取决于谁调用它。

所以,以同样的方式,你可以有类似的东西:

 class A  {
      public static void myStatic() {
          // find out who call it
          String calledFrom = new RuntimeException()
                              .getStackTrace()[1].getClassName();
      }
 }

这适用于一次初始化。但不是1000个电话。虽然我不知道一个好的VM是否可以为你内联。

我会选择AspectJ路径。

答案 4 :(得分:0)

我的相关理论是,使用代码生成策略为包含该方法的每个类创建一个委托。你不能在Java中拥有相当多的隐藏代码,只要你生成合理的东西,它可能不值得付出努力。如果你真的想要隐藏它,你可以做类似的事情......

public class Person extends PersonActiveRecord
{

}

//generated class, do not touch
public class PersonActiveRecord extends ActiveRecord
{
   public Person find(Map params)
   {
      ActiveRecord.find(Person.class, params);
   }
}

但它往往会过多地破坏你的继承层次结构。我说只是生成类并完成它。不值得隐藏查找方法。

答案 5 :(得分:0)

你可以通过创建一个hackish构造函数来非常手动地完成它。

A example = new B(B.class);

让超类构造函数存储传递给它的类。

我不认为上面抛出的异常会起作用,但是如果你想在没有创建异常的情况下做这样的事情......

Thread.currentThread().getStackTrace()

您可以通过元编程和 javassist 更顺畅地完成这项工作。

答案 6 :(得分:0)

我想你想在Java中实现ActiveRecord。当我决定这样做时,我遇到了同样的问题。这对Java来说很难,但我能够克服它。 我最近在这里发布了名为ActiveJDBC的整个框架: http://code.google.com/p/activejdbc/

如果有兴趣,您可以查看来源以了解其实施方式。查看Model.getClassName()方法。

这就是我解决从静态方法获取类名的方法。第二个问题是实际上将所有静态方法从超类移动到子类(毕竟这是一种笨重的继承形式!)。我使用Javassist。这两个解决方案允许我完全用Java实现ActiveRecord。 字节码操作最初是在加载类时动态完成的,但我在Glassfish和Weblogic中遇到了一些类加载问题,并决定实现静态字节码操作。这是通过http:activejdbc.googlecode.com/svn/trunk/activejdbc-instrumentation/ Maven插件完成的。

我希望这能为您的问题提供详尽的答案。

享受,

伊戈尔

相关问题