【问题标题】:How to Return an Object Via Static Factory Methods?如何通过静态工厂方法返回对象?
【发布时间】:2012-05-13 17:59:04
【问题描述】:

我了解 Java 基础知识,现在我正在阅读 Effective Java。这本书建议使用静态工厂方法而不是构造函数。所以我有这样的 Groovy 代码:

public class Anto {
    public static void main(String[] args) {
            println Java.javaInstance()
        }
}

class Java {
    public static Java javaInstance() {
        return this
    }
}

当我编译这个时,我得到一个这样的错误:

Caught: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'class Java' with class 'java.lang.Class' to class 'Java'
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'class Java' with class 'java.lang.Class' to class 'Java'
    at Java.javaInstance(Anto.groovy:9)
    at Java$javaInstance.call(Unknown Source)
    at Anto.main(Anto.groovy:3)

我在哪里做错了?

【问题讨论】:

  • 我真的不明白为什么每个人都在回答单例实现,因为它是最丑陋的反模式之一:S。我现在赞成 sp00m 的答案,因为我认为“编辑”之前的部分是最合适的。但我建议阅读this other SO question,了解静态工厂方法有效使用的其他示例,以及this 文章,该文章概述了该模式相对于使用重载构造函数的优缺点。

标签: java groovy constructor


【解决方案1】:

您可以使用return new Java(); 来完成。静态方法无权访问this

编辑:

这些静态工厂通常是单例的,这意味着应该只使用该类的一个实例(通常,例如与数据库的连接)。如果您想将此维度添加到您的 Java 类中,请使用如下私有静态属性:

class Java {

    private static Java instance;

    public static Java javaInstance() {
        if(instance == null) {
            instance = new Java();
        }
        return instance;
    }

}

【讨论】:

  • 好吧,这不会在每次通话时创建一个新的Java 实例吗?
  • @Ant's 是的,如果你需要这个约束,我刚刚编辑了我的答案。
  • @sp00m 我认为这本书不是在谈论单身人士(我可能错了,我还没读过)。它可能建议使用静态工厂方法来实例化对象,而不是直接使用构造函数,因为当您需要有不止一种方法来实例化具有重载构造函数的东西时,这不是很清晰/直观,而具有以直观方式命名的不同(静态)方法更可取。我知道这个成语是“named constructors”,但我不知道它在 C++ 世界之外是否还有别的名字。
  • 有趣,我不知道这些 C++ 命名构造函数。关于this post,你一定是对的。
  • @sp00m 该模式在 Java 世界中似乎被称为“静态工厂方法”。 Here 的一篇文章描述了为什么它们比使用重载构造函数更方便。还有hereare 几个关于它们在 Java 语言中使用的问题。
【解决方案2】:

正确创建 Singleton 很容易出错(尤其是在多线程环境中),因此您最好使用 Groovy 附带的 Singleton annotation,而不是自己滚动:

public class Anto {
  public static void main(String[] args) {
    println Java.instance
  }
}

@Singleton
class Java {
}

这会将Java 类转换为:

class Java {
  private static volatile Java instance
  private Java() {}
  static Java getInstance () {
    if( instance ) {
      instance
    } else {
      synchronized( Java ) {
        if( instance ) {
          instance
        } else {
          instance = new Java()
        }
      }
    }
  }
}

【讨论】:

  • 所以我在这里有一个疑问,SingletonStatic factory methods 的概念不同,或者我在某个地方失踪了?
  • 是的,我想我的回答更多是为了纠正你得到的所有不正确/坏的单例实现
  • 只有在使用 @Singleton(lazy = true) 时才会真正得到像这样的惰性初始化(你可能不应该这样做,因为在某些情况下double-checked locking is unreliable)。
  • @JustinPiper 如果您使用volatile 编写上述代码,则双重检查锁定现在仅在旧 JVM(1.5 之前)上存在问题
  • 那些(加上有缺陷的 JVM 和其他语言)将是我提到的情况。是的,您可能没有使用旧的 JVM(如果您使用的是 Groovy,肯定不会),但建议使用它似乎仍然是个坏主意。
【解决方案3】:

Google Guava 是使用静态工厂方法的库的一个很好的示例(尽管不是特定于 Groovy)。 Guava 在很多地方都使用了这个成语。例如,他们的Range 类支持九种类型的范围,如果他们使用普通的构造函数,它们的签名在某些情况下会发生冲突,因为唯一可以用来区分它们的就是它们的参数。

另一方面,静态方法也可以通过它们的名称来区分,因此 Guava 为每种类型的 Range 定义了不同的方法。在内部,这些方法仍然调用普通的构造函数,但它不是可公开访问的。

import com.google.common.collect.Ranges
import com.google.common.collect.DiscreteDomains

final dom = DiscreteDomains.integers()

assert [1,2,3,4,5] as Set == Ranges.closed(1, 5).asSet(dom)
assert [2,3,4] as Set     == Ranges.open(1, 5).asSet(dom)

这是一个有用的习惯用法,但不应该只是自动优先于普通构造函数。在普通构造函数就足够的情况下,您最多编写的代码比您需要的多,最坏的情况是无法扩展类,因为任何子类仍然需要它们可以调用的公共或受保护的构造函数。

【讨论】:

    【解决方案4】:

    您不能使用this,因为static 方法不是实例方法。

    每次创建特定类的新实例时,该新对象/实例都将作为它自己的状态。 this 指向一个特定的实例。

    你想制作一个单例吗?意味着您只想要一个类的单个实例?

    class Singleton {
         //static reference to a particular instance
         private static Singleton instance;
    
         //private constructor so that it cant be called outside this class scope
         private Singleton();
    
         //synchronized in case your working in threaded enviroment
         public synchronized static Singleton getInstance()
         {
           if(NULL == instance)
           {
             instance = new Singleton();
           }
           return instance;
         }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-28
      • 1970-01-01
      • 2012-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多