【问题标题】:Java generics, singletons and static methodsJava 泛型、单例和静态方法
【发布时间】:2011-12-20 09:27:46
【问题描述】:

所以我有一些“经理”类,例如 GroupManager。所有这些经理都是单身人士。

使用此方法进行实例化:

private static GroupManager groupManager = null;

private GroupManager()
{

}

public static GroupManager Instance()
{
    if (groupManager == null)
    {
        groupManager = new GroupManager();
    }
    return groupManager;
}

我想我应该开始使用一些继承,因为它们有很多复制的方法。

每个 Manager 的 Instance() 方法都是相同的。

所以对于继承我可以这样做(显然):

GroupManager extends Manager

是否可以使用泛型为所有管理器使用相同的实例方法,例如:

public class Manager<E>
{
    private static E instance = null;

    public static E Instance()
    {
        if (instance == null)
        {
            instance = new E();
        }
        return instance;
    }

}

我认为这是有道理的:)

那么你会像往常一样执行 GroupManager.Instance()。

【问题讨论】:

  • Java 泛型与 C++ 模板完全不同!放手吧!您永远不能使用 Java Generic 分配对象(即调用 new)。尝试在 Google 上搜索“Java 类型擦除”
  • Manager E 是对泛型类型的引用。没有简单的方法可以设置一个类来分配仅在运行时才知道的类型的对象。 Reid Mac 的答案是我会尝试的。
  • 您在手工编码的单例中遇到的限制是人们使用 Spring 或 Guice 等容器来管理单例生命周期的重要原因。
  • 另外,tihs 不会编译:“无法对非静态类型 E 进行静态引用”

标签: java generics inheritance static singleton


【解决方案1】:

你不明白泛型和静态是如何工作的。如果您有一个静态字段或方法(例如“instance”或 instance()),可以在不实例化类管理器的情况下调用它,您如何期望 JVM(甚至编译器)知道 E 应该是什么类型是吗?

这是一个例子,按照 G_H 的建议:

GeneralManager 和 AreaManager 都扩展了 Manager

Manager 类是唯一具有 getInstance() 静态方法的类:

    public class Manager {

        private static Map<Class<? extends Manager>,Manager> INSTANCES_MAP = new java.util.HashMap<Class<? extends Manager>, Manager>();

//Also, you will want to make this method synchronized if your application is multithreaded,
//otherwise you mihgt have a race condition in which multiple threads will trick it into
//creating multiple instances
        public static <E extends Manager> E getInstance(Class<E> instanceClass) throws InstantiationException, IllegalAccessException {
            if(INSTANCES_MAP.containsKey(instanceClass)) {
                return (E) INSTANCES_MAP.get(instanceClass);
            } else {
                E instance = instanceClass.newInstance();
                INSTANCES_MAP.put(instanceClass, instance);
                return instance;
            }
        }
    }

【讨论】:

  • 是的,但这完全不适合实现单例的想法。您需要一些极其扭曲的结构,例如从 Classes 到实例的 Map。
  • +1,这是非常正确的,我在想我是否也应该添加那个观察(我决定不这样做,因为我相信首先需要理解泛型和静态的基本概念,然后再像这样使用)。但是,您提出了一个很好的观点。此外,您可以将泛型 E 类型限制为基类,例如 E extends RootOfIheritanceChainClass。
  • 我有点迷路了,大声笑,我应该继续:基本上我需要一个新的 Instance() 方法为每个新的经理。
  • 如果你有多个继承自 Manager 的类,并且你希望每个类只有一个实例,那么你应该在每个类中声明静态 getInstance() 方法,只返回一个实例事情。静态方法没有重载,因此它们都将独立工作。这是最简单的方法,你会得到一些重复的代码,但不多。或者你可以只在基类中创建一个静态 getInstance() 方法,并保留所有单例的 Class/ClassInstance 映射。
  • “或者你可以只在基类中创建一个静态 getInstance() 方法,并保留所有单例的 Class/ClassInstance 映射。”请您进一步解释一下,如果可能的话,举个例子。
【解决方案2】:

不,这行不通。 Java 在编译时使用泛型进行类型检查,但不会在运行时生成额外的类或保留有关类型参数的信息。

当您使用该类型参数E 声明Manager&lt;E&gt; 时,这只会在实际实例中发挥作用。你可以有一个像GroupManager extends Manager&lt;String&gt; 这样的子类,但这不会神奇地生成各种静态方法。

静态方法和成员属于一个类,而不是一个实例。所以尝试在那里使用泛型,用于键入实例,是行不通的。

【讨论】:

    【解决方案3】:

    如果您按如下方式创建组管理器类,那么您可以调用您的实例方法。

    public class GroupManager extends Manager<GroupManager>{}
    

    在你的 Manager 课上试试这个...

    public class Manager<E>
    {
    private static E instance = null;
    
    public static E Instance()
    {
                      try {
                return instance.newInstance();
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            return null;  
    }
    

    或者,如果您知道要为其创建实例的对象,只需将方法设为通用

    public static <T> T getInstance(Class<T> t){
                 try {
                return t.newInstance();
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            return null;
        }
    

    我还没有尝试过,所以不确定它是否会起作用。

    【讨论】:

      【解决方案4】:

      在通用上下文中注入构造函数。现金不是线程安全的,但只在静态上下文中使用,所以如果你不错过使用它就可以了

      public class Example {
      
          public static class MySingletonClass {
          }
      
          public interface Provider<T> {
              T get();
          }
      
          static final Provider<MySingletonClass> myClassInstanceProvider = new Cash<MySingletonClass>(new Provider<MySingletonClass>() {
                  @Override
                  public MySingletonClass get() {
                      return new MySingletonClass();
                  }
              }); 
      
      
          public static class Cash<T> implements Provider<T> {
              private Provider<T> provider;
      
              public Cash(Provider<T> provider) {
                  this.provider = provider;
              }
      
              @Override
              public T get() {
      
                  final T t = provider.get();
                  provider = new Provider<T>() {
      
                      @Override
                      public T get() {
                          return t;
                      }
                  };
                  return t;
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        public class Manager<E>{
        
        private static Object instance = null;
        
            public static E Instance() {
               if (instance == null)
               {
                instance = new E();
               }
               return (E)instance;
           }
        } 
        

        【讨论】:

        • 上面的答案就可以了,不是吗?
        猜你喜欢
        • 2012-12-04
        • 2014-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-17
        • 2011-01-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多