Constructor Interfaces

Red IO redio.development at gmail.com
Wed Jan 25 13:42:01 UTC 2023


Yes, as I mentioned in the alternative section this idea intersects with
the concept of static abstract methods.
The problem with the Box constructor not knowing the constructor of T would
be solved by static analysis in my concept.

public interface DefaultConstructable {
new();
new(char[] chars);
}


class Box<T: DefaultConstructable> {
public Box() {
T t = new T();
T t2 = new T({ 'H', 'I' });
}
}

Box<String> box = new Box<>();

Would be desugared to:

public interface DefaultConstructable<T> {
T $ctor();
T $ctor(char[] chars);
}

class Box<T> {
private final DefaultConstructable<T> $ctorT;

public Box(DefaultConstructable<T> $ctorT) {
this.$ctorT = $ctorT;
T t = $ctorT.$ctor();
T t2 = $ctorT.$ctor({ 'H', 'I' });
}
}

//this part would be implemented like a lambda with multiple
methods/multiple lambdas but I have no idea what code lamdas actually
generate.
//So I put an anonymous class dummy here to make my point.
Box<String> box = new Box<>(new DefaultConstructable<>() {
public String $ctor() {
return new String();
}

public String $ctor(char[] chars) {
return new String(chars);
}
});

And I think the solution is pretty reasonable with no actual change in the
runtime or compiler. Just some static analysis and desugaring before
compilation. The only tricky part comes when type variables are passed to
type variables and the static analysis can't find and verify the concrete
type passed to in this case the Box constructor. But an algorithm unwinding
all type variables to its concrete type isn't difficult I actually did it
already and created an runtime type variable analysis tool. (since it needs
to verify the bound of DefaultConstructable and generate the delegates to
the actual constructors.

On Wed, Jan 25, 2023, 14:03 Ron Pressler <ron.pressler at oracle.com> wrote:

> Hi.
>
> By the time the Box constructor runs it doesn’t know what E’s concrete
> type is. This has both advantages and disadvantages, but it’s the way
> things work now.
>
> In general, there are lots of annoyances that changes to the language may
> solve, but a change to the language also invariably introduces its own
> annoyances (for one, it makes the language more complex and requires people
> to learn how this works). So the trick is not so much finding a solution to
> a problem, but finding a problem that’s large and serious enough — it can
> perhaps be very common or it could be one that leads to hard-to-find bugs —
> to justify a particular language change. For example, in this case you may
> want to consider a constructor as a special case of a static method; maybe
> the problem you’re addressing isn’t large enough to justify this kind of
> “constructor trait", but *maybe* a more general one that has to do with
> static methods in general could be.
>
> — Ron
>
> > On 25 Jan 2023, at 07:03, Red IO <redio.development at gmail.com> wrote:
> >
> > 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/2b47c238/attachment-0001.htm>


More information about the amber-dev mailing list