Java Pattern class doesn't have a public constructor, why?

别说谁变了你拦得住时间么 提交于 2019-12-22 03:44:23

问题


I've been reviewing Java Regex Library, surprised by the fact the Pattern class does not have a public constructor which I've taken for granted for years.

One reason I suspect the static compile method is being used in favor of constructor could be that constructor would always return a new object while a static method might return a previously created (and cached) object provided that the pattern string is the same.

However, it is not the case as demonstrated by the following.

public class PatternCompiler {
    public static void main(String[] args) {
        Pattern first = Pattern.compile(".");
        Pattern second = Pattern.compile(".");
        if (first == second) {
            System.out.println("The same object has been reused!");
        } else {
            System.out.println("Why not just use constructor?");
        }
    }
}

Any other strong rationales behind using static method over constructor?

Edit: I found a related question here. None of the answers there convinced me either. Reading through all answers, I get a feeling that a static method has quite a few advantages over a public constructor regarding creating an object but not the other way around. Is that true? If so, I'm gonna create such static methods for each one of my classes and safely assume that it's both more readable and flexible.


回答1:


Generally, a class won't have a public constructor for one of three reasons:

  • The class is a utility class and there is no reason to instantiate it (for example, java.lang.Math).
  • Instantiation can fail, and a constructor can't return null.
  • A static method clarifies the meaning behind what happens during instantiation.

In the class of Pattern, the third case is applicable--the static compile method is used solely for clarity. Constructing a pattern via new Pattern(..) doesn't make sense from an explanatory point of view, because there's a sophisticated process which goes on to create a new Pattern. To explain this process, the static method is named compile, because the regex is essentially compiled to create the pattern.

In short, there is no programmatic purpose for making Pattern only constructable via a static method.




回答2:


One possible reason is that this way, caching can later be added into the method.

Another possible reason is readability. Consider this (often cited) object:

class Point2d{
  static Point2d fromCartesian(double x, double y);
  static Point2d fromPolar(double abs, double arg);
}

Point2d.fromCartesian(1, 2) and Point2d.fromPolar(1, 2) are both perfectly readable and unambiguous (well... apart from the argument order).

Now, consider new Point2d(1, 2). Are the arguments cartesian coordinates, or polar coordinates? It's even worse if constructors with similar / compatible signatures have entirely different semantics (say, int, int is cartesian, double, double is polar).

This rationale applies to any object that can be constructed in multiple different ways that don't differ in just the argument type. While Pattern, currently, can only be compiled from a regex, different representations of a Pattern may come in the future (admittably, then, compile is a bad method name).

Another possible reason, mentioned by @Vulcan, is that a constructor should not fail.

If Pattern.compile encounters an invalid pattern it throws a PatternSyntaxException. Some people may consider it a bad practice to throw an exception from a constructor. Admittably, FileInputStream does exactly that. Similarly, if the design decision was to return null from the compile method, this would not be possible with a constructor.


In short, a constructor is not a good design choice if:

  • caching may take place, or
  • the constructor is semantically ambiguous, or
  • the creation may fail.



回答3:


This is just a design decision. In this case there is no "real" advantage. However, this design allows optimisation (caching for instance) without changing the API. See http://gbracha.blogspot.nl/2007/06/constructors-considered-harmful.html




回答4:


Factory methods have several advantages, some of which are already specified in other answers. The advice to consider factory methods instead of constructors is even the very first chapter in the great book "Effective Java" from Joshua Bloch (a must-read for every Java programmer).


One advantage is that you can have several factory methods which have the same parameter signatures but different names. This you can't achieve with constructors.

For example, one might want to create a Pattern from several input formats, all of which are just Strings:

class Pattern {
  compile(String regexp) { ... }
  compileFromJson(String json) { ... }
  compileFromXML(String xml) { ... }
}

Even if you are not doing this when you create the class, factory methods give you the ability to add such methods latter without causing weirdness.

For example, I have seen classes where the need for a new constructor came later and a special meaning-less second parameter had to be added to the second constructor in order to allow overloading. Obviously, this is very ugly:

class Ugly {
  Ugly(String str) { ... }

  /* This constructor interpretes str in some other way.
   * The second parameter is ignored completely. */
  Ugly(String str, boolean ignored) { ... }
}

Unfortunately, I can't remember the name of such a class, but I think it even was in the Java API.


Another advantage which has not been mentioned before is that with factory methods in combination with package-private constructors you can prohibit sub-classing for others, but still use sub-classes yourself. In the case of Pattern, you might want to have private sub-classes like CompiledPattern, LazilyCompiledPattern, and InterpretedPattern, but still prohibit sub-classing to ensure immutability.

With a public constructor, you can either prohibit sub-classing for everybody, or not at all.




回答5:


If you really want to take the deep dive, plunge into the archives of JSR 51.

Regular expressions have been introduced as part of JSR 51, that’s where you might still find the design decisions in their archives, http://jcp.org/en/jsr/detail?id=51




回答6:


It has a private constructor.

 /**
     * This private constructor is used to create all Patterns. The pattern
     * string and match flags are all that is needed to completely describe
     * a Pattern. An empty pattern string results in an object tree with
     * only a Start node and a LastNode node.
     */
    private Pattern(String p, int f) {

and compile method calls into that.

public static Pattern compile(String regex) {
        return new Pattern(regex, 0);
    }

Since you are using == comparison which is for references it will not work

The only reason I can think of this behaviour is that the match flag will be defaulted to zero in the compile method which acts a factory method.



来源:https://stackoverflow.com/questions/13758740/java-pattern-class-doesnt-have-a-public-constructor-why

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!