Ant's
Ant's

Reputation: 13811

How to Return an Object Via Static Factory Methods?

I know Java basics, and now I'm in the journey of reading Effective Java. The book suggest using static factory methods instead of constructors. So I have Groovy code like this:

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

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

When I compile this, I get an error like 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)

Where am I making a mistake?

Upvotes: 2

Views: 1126

Answers (4)

Justin Piper
Justin Piper

Reputation: 3274

A good (albeit not specific to Groovy) example of a library that uses static factory methods that you could look at would be Google Guava. Guava uses this idiom in a number of places. For example, their Range class supports nine types of ranges, and if they used normal constructors, their signatures would conflict in several cases since the only thing you can use to distinguish them is their arguments.

Static methods on the other hand can also be distinguished by their name, so Guava defines different ones for each type of Range. Internally these methods still call a normal constructor, but it's not one that's publicly accessible.

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)

This is a useful idiom, but not one that should just be automatically preferred over a normal constructor. In situations where a normal constructor would have sufficed, you've at best written more code than you needed and at worst have made extending the class impossible, since any subclasses will still need a public or protected constructor they can call.

Upvotes: 2

tim_yates
tim_yates

Reputation: 171154

Creating a Singleton correctly can be easy to get wrong (especially in a multi-threaded environment), so you're probably better using the Singleton annotation that comes with Groovy rather than rolling your own:

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

@Singleton
class Java {
}

This transforms the Java class to:

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()
        }
      }
    }
  }
}

Upvotes: 2

ehanoc
ehanoc

Reputation: 2217

You can't use this because static methods are not instance methods.

Each time you create a new instance of a particular class, that new object/instance as it's own state. this points to a particular instance.

Are you trying to make a singleton ? Meaning you just want a single instance of a class ?

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;
     }
}

Upvotes: 0

sp00m
sp00m

Reputation: 48837

You can do it using return new Java();. Static methods don't have access to this.

EDIT:

These static factories are usually singletons, which means that only one instance of the class should be used (typically, a connection to a db for example). If you want do add this dimension to your Java class, use a private static attribute as follow:

class Java {

    private static Java instance;

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

}

Upvotes: 3

Related Questions