【问题标题】:Is it possible to have a Java singleton using an enum that uses constructor arguments是否可以使用使用构造函数参数的枚举来创建 Java 单例
【发布时间】:2013-09-23 19:48:49
【问题描述】:
 public enum Singleton  {
            INSTANCE;
            String fromLocation;
            String toLocation;

            private void initialise(String fromLocation, String toLocation) {
                this.fromLocation = fromLocation;
                this.toLocation = toLocation;
            }

            public static void main(String[] args) {
                Singleton s = INSTANCE;
                s.initialise(args[0],args[1]);
            }
    }

我似乎无法理解具有普通 Java 类构造函数的语法,我也可以从主例程传递 args。初始化例程对我来说似乎是一种难闻的气味 - 但这是我能想到的最好的。有什么建议吗?

【问题讨论】:

  • 您是否真的在尝试使用命令行参数初始化枚举的属性,或者这是您的解决方法?
  • 看起来你不想在这里单例...
  • 嗯,一个带有变量的enum。听起来太棒了!不要这样做。永远。
  • 同意 rgettman,我同意 arshajii - Boris - 忽略垃圾邮件

标签: java constructor enums singleton arguments


【解决方案1】:

你的意思大概是这样的:

public class Singleton {
  private static Singleton instance;
  public static void getInstance() {
    if (instance == null)
      throw new IllegalStateException("Singleton not initialized!");
    return instance;
  }
  public static void init(String from, String to) {
    if (instance != null)
      throw new IllegalStateException("Singleton already initialized!");
    instance = new Singleton(from, to);
  }
  private final String from;
  private final String to;
  private Singleton(String from, String to) {
    this.from = from;
    this.to = to;
  }
}

显然,这不是线程安全的等等。但老实说,你最好使用像 Spring 或 Guice 这样的依赖注入框架。从长远来看,你的代码中的这个 Singleton.getInstance() 调用并不是你想要的......

为什么不使用/定义枚举?它说“枚举类型是一种特殊的数据类型,它使变量能够成为一组预定义的常量。”Java tutorial 中。而且你显然没有处理一些常量。

【讨论】:

  • 嗨skirsch 感谢您的回复。 Johan Sjoberg 发表的关于进入反射领域的评论让我意识到——使用像 Spring 这样的 DI 框架将是在单例上实现构造函数注入的一种方法——这涉及到背后的反射。我认为您最终也会通过此实现获得大量额外的样板文件。我对原始代码的唯一实际更改是将字符串 args 数组直接传递到初始化并以这种方式分配参数 - 简化方法签名。
【解决方案2】:

您的方法的问题源于enums 与单身人士一样出色的主要原因。

让我建议与Schrödinger's cat 思想实验进行类比。 enum 就像猫的状态,当盒子打开时它会立即完全知道并永远保持这种状态。这是由enum 的性质所保证的,即无论许多人在框中查看它只是猫的状态的第一个查看器实际上导致猫具有状态。其他人都看到了那一刻的精确副本。

因此,您有两种方法可以将外部数据安装到enum 的最终状态。你可以保证自己是第一个打开盒子的人,或者——在这里我稍微扩展一下这个类比——你教猫在盒子第一次打开时快速伸出手来抓住它的状态。

选项 1 与实现单例一样复杂。绝对确定自己是第一几乎是不可能的。

所以选项 2 可能类似于:

public enum Singleton {

  INSTANCE;
  final String fromLocation;
  final String toLocation;

  private Singleton () {
    // Reach out.
    this.fromLocation = App.args[0];
    this.toLocation = App.args[1];
  }
}

您的 App 入口点如下所示:

class App {
  static String[] args;

  public static void main(String args[]) {
    App.args = args;
  }
}

这里有可怕的警告。首先,如果您不在App.main 启动它,每个使用您的单例的应用程序都会中断。其次,如果您在 App 类中的任何位置访问您的单例,它可能会中断。

【讨论】:

  • 是的 - 双向引用 - 另一种给“那只”猫皮肤的方法!又不是那么好。我认为最接近我真正想要的设计模式是构建器 - 但这对于它来说是多余的,因为它只是一个单例和一个简单的初始化例程,没有复杂性。
【解决方案3】:

不,enum 构造函数的工作方式是由

public enum Singleton {
     A("from", "too"),
     B("from2", "too2");

     private String from, to;

     private Singleton(String from, String too) {
         this.from = from;
         this.too = too;
     }
}

你真正想要达到什么目的?也许enum 不是您想要的,特别是如果您希望在运行时为其分配值。

【讨论】:

  • 我意识到这一点 - 但枚举通常基于静态内容 - 常量 - 我需要它与命令行参数动态相关 - 根据我发布的代码
  • 我想知道是否必须恢复到旧式私有构造函数和静态最终单例属性,将 args 传递给分配其值的构造函数。在这种情况下不要使用枚举...
  • 您的用例与枚举不兼容,涉及reflection。在大多数情况下是一个很好的警钟。
  • 实际上我认为单例不应该有带参数的构造函数,所以我的原始代码可能和我得到的一样好。
  • @JGFMK 单例很可能有构造函数参数。如果您查看 Spring DI 的基础知识,bean 是默认的单例。而且(尽管这似乎是某种宗教辩论)有些人更喜欢构造函数注入而不是属性注入。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-25
  • 2014-07-30
  • 2018-02-06
相关资源
最近更新 更多