开发者

Hiding a constructor behind a static creator method?

I've recently discovered an interesting way to create a new instance of an object in Google Guava and Project Lombok: Hide a constructor behind a static creator method. This means that instead of doing new HashBiMap(), you do H开发者_StackOverflow中文版ashBiMap.create().

My question is why? What advantage do you have of hiding the constructor? To me I see absolutely no advantage of doing this, and it seems to break basic object creation principles. Since the beggining you create an object with new Object(), not some Object.createMe() method. This almost seems like creating a method for the sake of creating a method.

What do you gain from doing this?


There are a number of reasons why you might prefer a static factory method instead of a public constructor. You can read Item 1 in Effective Java, Second Edition for a longer discussion.

  1. It allows the type of the object returned by the method to be different than the type of the class that contains the method. In fact, the type returned can depend on the parameters. For example, EnumSet.of(E) will return a different type if the emum type has very few elements vs if the enum type has many elements (Edit: in this particular case, improving performance for the common case where the enum doesn't have many elements)
  2. It allows caching. For instance, Integer.valueOf(x) will, by default, return the same object instance if called multiple times with the same value x, if x is between -128 and 127.
  3. It allows you to have named constructors (which can be useful if your class needs many constructors). See, for example, the methods in java.util.concurrent.Executors.
  4. It allows you to create an API that is conceptually simple but actually very powerful. For instance, the static methods in Collections hides many types. Instead of having a Collections class with many static methods, they could have created many public classes, but that would have been harder for someone new to the language to understand or remember.
  5. For generic types, it can limit how much typing you need to do. For example, instead of typing List<String> strings = new ArrayList<String>() in Guava you can do List<String> strings = Lists.newArrayList() (the newArrayList method is a generic method, and the type of the generic type is inferred).

For HashBiMap, the last reason is the most likely.


This is usually done because the class actually instantiated by the create() method might be different than the type upon which you are invoking the method. i.e. a factory pattern where the create() method returns a specific subclass that is appropriate given the current context. (For example, returning one instance when the currrent environment is Windows, and another when it is Linux).


Unlike constructors, static methods can have method names. Here's a recent class I wrote where this was useful:

/**
 * A number range that can be min-constrained, max-constrained, 
 * both-constrained or unconstrained.
 */

public class Range {
  private final long min;
  private final long max;
  private final boolean hasMin;
  private final boolean hasMax;

  private Range(long min, long max, boolean hasMin, boolean hasMax) {
    // ... (private constructor that just assigns attributes)
  }

  // Static factory methods

  public static Range atLeast (long min) {
    return new Range(min, 0, true, false);
  }

  public static Range atMost (long max) {
    return new Range(0, max, false, true);
  }

  public static Range between (long min, long max) {
    return new Range(min, max, true, true);
  }

  public static Range unconstrained () {
    return new Range (0, 0, false, false);
  }
}

You couldn't do this using just constructors, as atLeast and atMost would have the exact same signature (they both take one long).


This is called a Factory method pattern. Where the factory lies within the class itself. Wikipedia describes it pretty well but here are a few snippets.

Factory methods are common in toolkits and frameworks where library code needs to create objects of types which may be subclassed by applications using the framework.

Parallel class hierarchies often require objects from one hierarchy to be able to create appropriate objects from another.


Well it would be possible for SomeClass.create() to pull an instance from a cache. new SomeClass() won't do that without some shenanigans.

It would be also be possible for create() to return any number of implementations of SomeClass. Basically, a Factory type of dealio.


Although not applicable to this particular code example, the practice of hiding the constructor behind a static method is Singleton Pattern. This is used when you want to ensure that a single instance of the class is created and used throughout.


There are many reasons to use this factory method pattern, but one major reason Guava uses it is that it lets you avoid using type parameters twice when creating a new instance. Compare:

HashBiMap<Foo, Bar> bimap = new HashBiMap<Foo, Bar>();

HashBiMap<Foo, Bar> bimap = HashBiMap.create();

Guava also makes good use of the fact that factory methods can have useful names, unlike constructors. Consider ImmutableList.of, ImmutableList.copyOf, Lists.newArrayListWithExpectedSize, etc.

It also takes advantage of the fact that factory methods don't necessarily have to create a new object. For instance, ImmutableList.copyOf, when given an argument that is itself an ImmutableList, will just return that argument rather than doing any actual copying.

Finally, ImmutableList's factory methods return (non-public) subclasses of ImmutableList such as EmptyImmutableList, SingletonImmutableList and RegularImmutableList depending on the arguments.

None of these things are possible with constructors.


i got very interesting reason to hide constructor check it and please let me know if there is any other alternative to achieve this

enter code here

Class A
 {
   String val;
    protected A( )
     {
     }
    protected A(String val)
     {
       this.val=val;
     }
   protected void setVal( String val)
    {
       this.val=val;
    } 
   public String getVal()
    {
      return val;
    }    
}
class B extends A
  {
     B()
     {
       super();
     }     
    public val setVal(String val)
    {
      super.val=val;    
    }
} 
class C extends A
{
    C(String val)
    {
      super(val);
    }
}


Some main reasons

  • Primarily it gives you the power to instantiate a different (sub) class
  • Possibility to return null
  • It enables you to return an already existing object
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜