繁体   English   中英

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

[英]Is it possible to see via which child class was a parent's static method called in Java?

先是一点背景。 我正在研究尽可能干净简洁地在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用于实例的方法总体上会更好吗?

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

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

正如其他人所指出的那样,我认为这个问题在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();

不是很漂亮,它限制了编译时错误检查,但实现的代码更少。

你不会在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
    }
}

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

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

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

例如,通过这个答案 ,可以创建一个这样的记录器:

 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路径。

我的相关理论是,使用代码生成策略为包含该方法的每个类创建一个委托。 你不能在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);
   }
}

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

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

A example = new B(B.class);

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

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

Thread.currentThread().getStackTrace()

使用元编程和javassist,您可以更顺利地完成它。

我想你想在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插件完成的。

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

请享用,

伊戈尔

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM