Constructor Interfaces

Red IO redio.development at gmail.com
Wed Jan 25 07:03:14 UTC 2023


Summary
-------

Enable a parameterized class to constrain the parameterized type to be
constructible with a given list of parameters.



Motivation
----------

It is possible since JDK 8 to get a constructor (method) reference of an
object. This allowed for the creation of an unknown class with a known
constructor reference. But currently the only way to obtain such reference
is at call site like this:
Box<String> stringBox = new Box<>(String::new);

It is inconvenient for the user to supply the the reference themselves and
can confuse them as the type of the parameter is something like
Supplier<String> which doesn't require the pased reference to be a
constructor.
It also clutters api's like "toArray" which requires an IntFunction to be
type safe.

Description
-----------

ConstructorInterface
A ConstructorInterface is a special kind of interface similar to a
FunctionalInterface. It also has similar constraints. It only allows
abstract constructors and no other abstract methods. It can declare
multiple constructors though. The definition of such interface would look
similar to this:

@ConstructorInterface //optional validation like FunctionalInterfaces
public interface DefaultConstructible {
new();
new(char[] chars);
}

A parameterized type could declare this interface as a type bound for its
parameter and therefore enabling it to be constructed safely. Like this:
public class Box<E extends DefaultConstructible> {
public Box() {
E newElement = new E();
}
}
The containing type is not forced to implement the ContructorInterface
explicitly. It is implicitly implemented if the required constructor(s)
is(are) present.
public static void main(String[] args) {
Box<String> stringBox = new Box<>(); //compiles because String has the
required constructors.
Box<java.sql.Date> dateBox new Box<>(); error: java.sql.Data does not
satisfy the type bound DefaultConstructible
}
The interface might not be implemented by any class, since it doesn't
follow the inheritance rule that extending classes of those who implement
it also implement it. This requirement comes from the fact that extending
classes do not necessarily need to have the same constructor signature and
therefore don't qualify the requirements for the interface. Another option
would be that extending classes of classes that implement a constructor
interface explicitly are also required to supply the necessary constructors.

class Foo implements DefaultConstructable {
//both required by the interface
public Foo() {}
public Foo(char[] chars) {}
}

class Bar extends Foo {
//the requirement for the required constructors is passed down.
public Bar() {}
public Bar(char[] chars) {}
}



public static <T extends Foo> T createT() {
return new T();
}

public <T extends Foo> T wrapper() {
return createT();
}
This would technically work but would require a lot of static analysis to
find the real type of T to call its constructor.
Restricting the use of "new T()" to type parameters that specify a
constructor interface directly and only allow those to be resolved with a
concrete type rather than another type parameter.

Alternatives
------------
An alternative would be to introduce new syntax to restrict the ability of
certain constructors on a parameter type. Like c# does (but only for the
default constructor) :
public static T foo<T>() where T: new() {
return new T();
}
In java:
public static <T extends new()> T foo() {
return new T();
}
The downside of this approach is obviously the introduction of new syntax
rather than reusing the interface/inheritance syntax.

Another alternative to this approach could be to implement static abstract
methods. This would allow an interface to mandate a static Factory Method.
The downside of this approach is that it requires the parameter class to
actually implement the interface and the concept of type erasure would need
to be addressed for static abstract methods to work. In contrast the
ConstructorInterface enables every class that matches its contract to pass
the type bound.



Risks and Assumptions
---------------------

As mentioned before the restriction the interface is giving on a type bound
is different to normal interfaces, it restricts by its containing abstract
constructors not by the type itself. It also makes use of the new operator
on type variables.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20230125/e9907960/attachment.htm>


More information about the amber-dev mailing list