I'd really like to be able to define a constructor on sealed interfaces:
sealed interface Path permits WindowsPath, UnixPath {
public Path(String p) {
if (isWindows()) {
return new WindowsPath(p);
}
return new UnixPath(p);
}
}
That way user code to construct an object is always the same: var p = new Path(s);
Instead of: var p = Paths.get(s);1. They don't have a name. That's bad because names are important. It's OK if constructor is primitive and does not do anything but field assignment. But often constructors do something and naming might help.
2. There can't be two constructors with identical types (as a consequence that constructors don't have a name). This might be restricting in some cases.
Factory methods solve both issues.
If anything, I'd prefer to remove constructors completely and use factory methods for anything. Not realistic with Java, of course. But in my code I'm trying to follow this pattern. Constructors are absolutely minimal (usually just initializers for final fields) and any non-trivial code goes into factory method. Of course it causes issues with subclassing, so it's not a silver bullet.
Where perhaps the first wraps, the second copies and the third may wrap and avoid sorting the input. This would all of course depend on some conventions on meaning within the codebase.
Another benefit is being able to name different constructors differently.
If I were to design a new OO language, I would make it so that constructors always work like that in the first place, from the caller perspective.
Is this not what I'm describing? Using one way to construct objects would allow you to change from a concrete class to an interface without breaking client code. I just want the syntax to be the same across interface/abstract/concrete so class construction is uniform and discoverable.
It also doesn’t provide the flexibility in naming.
// you can start with a final concrete class
final class A {}
var a = new A();
// and later move to interfaces or abstract types
sealed interface A permits B, C {
public A() {
...
}
}
var a = new A();
Client code would remain the same with the flexibility to refactor to whatever you want later.Personally I still prefer having the ability to name the constructors. I'd rather have "new" be the default constructor name, with the option of having it be something else (i.e. Path.new(...) vs. Path.somethingElse(...)).
Constructor(A a, B b) { … }
Constructor(C c, D d)
{
// A and B can be derived from C and D, and
// we want to forward to the (A, B) constructor,
// so we use an intermediate object to do the
// conversion and provide A and B:
this(new Intermediate(c, d));
}
private Constructor(Intermediate x)
{
this(x.a, x.b);
}
Of course, with the JEP this will become much simpler.