使用超类静态方法获取子类实例

3
我有一个超类,我想将名为getInstance()的静态方法转发到所有子类。
在创建子类实例时,我会在超类中注册该实例(可能使用哈希表,其中键基于getClass())。然后,我希望使用前面提到的静态方法(getInstance),其中超类方法将返回正确类型的实例。
例如,我有一个超类A和一个子类B扩展A。我想编写一个静态方法A.getInstance();当从B调用它(B.getInstance())时,我希望它返回之前存储的B实例。
这有点难以解释,但我将经常使用此超类,而我不想在每个子类中编写getInstance方法。
我应该如何做到这样的事情?
编辑:我刚意识到我的问题可能被误解为创建对象的新实例。我已经创建了实例,并希望获取类的现有实例。

2
静态方法不知道“THIS”。 - farmer1992
2
静态方法不会被继承。 - Jesper
令人困惑的。请举个例子。 - Subhrajyoti Majumder
3
不要使用那个静态方法,改用一个工厂。 - SpaceTrucker
@Jesper 这与你的观点直接相矛盾:https://dev59.com/hWkv5IYBdhLWcg3w3Uk0 - Eloff
显示剩余8条评论
6个回答

3
许多其他人在评论中指出,您试图做的事情在静态方法中是不可能的。同时,尽可能避免使用静态方法,因为它们可能导致测试和维护上的噩梦(*)。
您将方法命名为“getInstance”,所以我猜您想要做的是工厂模式和单例模式的混合。以下是一些关于这些模式的信息,可供您参考:
单例模式:http://en.wikipedia.org/wiki/Singleton_pattern
工厂方法:http://en.wikipedia.org/wiki/Factory_method_pattern
抽象工厂:http://en.wikipedia.org/wiki/Abstract_factory 现今不应手动编写这两个模式(*) - 可以查看好的“依赖注入”(DI)容器,如Google Guice或Spring。我不确定您确切想要实现什么,但看起来一个DI容器可以为您完成它。
编辑:这是对问题编辑的回应。您想收到子类的缓存实例。在这种情况下,我仍然建议避免使用静态方法。您可以创建一个“BCache”类的单例实例(使用DI容器或手动编程),然后使用此缓存对象查找已注册的对象。使用Guice作为DI容器,它可能是这样的(警告,未经测试):
@Singleton
public class BCache {
    private Map<Class<? extends B>, B> cache = ...;

    public <T> T getInstance(Class<? extends T> type) {
        return (T) cache.get(type);
    }
}

我仍然认为可以通过使用DI容器完全摆脱缓存类。虽然这是未经测试的代码,使用Guice,但它可能看起来像这样:

@Singleton
public class A extends B {
    public A() {
        //I am not sure if you need to register in this case, because your
        //DI container keeps track of the singleton instances.
        super.register(this);
    }
}

public class SomeClassUsingA {
    @Inject private A a;
}

(*) 注意,“所有概括都是错误的”,也就是说,在某些项目中可能会有意义,但在大多数项目中不会。

2
使用 DI 框架并不能改善 OP 不了解“工厂”模式的事实。他应该学习一般的设计模式,然后决定是否需要 DI 框架。这只是我的个人意见。 - Fildor
谢谢。那么我假设解决这个问题的最佳/唯一方法是为每个类编写一个getInstance方法,对吗? - CorrieKay
@DavidTanzer 为什么静态方法是维护的噩梦? - Eloff
1
@Eloff 大多数时候,它们真的很难测试。使用静态方法会创建一个依赖项,您无法轻松替换(用于测试或其他目的)。所以您将调用方和被调用方紧密耦合在一起。当您想要重新使用调用方时,这通常会对您产生影响。一个例外是纯实用方法:像 "Strings.notEmpty(str)" 或 "Arguments.notNull(argumentValue, argumentName)" 这样的东西,静态方法可能是可以接受的。 - David Tanzer
@Eloff 如果你想要一个更详细的答案,可以在 Stack Overflow 上提问并告诉我(例如在 Twitter 上 @dtanzer),那么我会尽力提供更详细的答案,并展示一些替代方案。 - David Tanzer
显示剩余3条评论

1

您可以随时将子类实例分配给超类引用。因此,您的超类的静态方法可以设置或获取子类实例。但是在使用之前,请务必对返回的实例进行强制转换。

public class A {
    private static A a;

    /**
     * @param a the a to set
     */
    public static void setA(A a) {
        A.a = a;
    }

    /**
     * @return the a
     */
    public static A getA() {
        return a;
    }


public class B extends A {
    private int index = 0;

    /**
     * @param index the index to set
     */
    public void setIndex(int index) {
        this.index = index;
    }

    /**
     * @return the index
     */
    public int getIndex() {
        return index;
    }

使用方法:

public static void main(String[] args) throws Exception {
        B b = new B();
        A.setA(b);

    B c = (B) A.getA();
    System.out.println(c.getIndex());
}

1

使用静态方法无法以良好的面向对象编程方式实现您想要的功能。您可以通过反射或一堆if .. else if来实现某些功能。

然而,您应该使用一些定义良好的设计模式,例如抽象工厂工厂方法。这是您应该采取的方式,就像有人说的“不要发明温水”。


0

在超类中将 getInstance() 的形式更改为 getInstance(String class)

public static A getInstance(String class){
if(class.equals("A")){
RegisterObject(...);//User defined method
return new A(...);
}
else if(class.equals("B")){
RegisterObject(...);//User defined method
return  new B(...);
}
else if(class.equals("C")){
RegisterObject(...);//User defined method
return  new C(...);
}
//... and so on

}

0

静态方法无法知道用于调用它们的类。例如,如果有类A和B扩展了A,并在A中实现了static getInstance(),则无论您是使用A还是B调用getInstance()都没有区别。此外,尝试调用B.getIntance()将产生编译警告(至少在Eclipse中)。

但是,您可以将类作为参数传递给getInstance()

public class A {
    public static <T extends A> T getInstance(Class<T> clazz) {
        return clazz.newInstance(); // I do not catch exceptions here: do it yourself
    }
}

public class B extends A {
}

...............
B b = A.getInstance(B.class);

0

你可以这样做,

 class A{
     public static A getInstance(){
     return new A();
   }

  @Override
  public String toString() {
   return "inside A";
   }
 }

 class B extends A{
public static A getInstance(){
  return new B();
 }

   @Override
   public String toString() {
      return "inside B";
   }
 }

主函数内:

   public static void main(String[] args) {
  A a1 = A.getInstance();
  A a2 = B.getInstance();
      System.out.println(a1.toString());
  System.out.println(a2.toString());
   }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接