Constructor Interfaces
Brian Goetz
brian.goetz at oracle.com
Wed Jan 25 16:24:06 UTC 2023
I think you may have misunderstood. We don't use annotations to extend
the language in this way at all; if you want a new type system feature
(such as new kinds of bounds on type variables), you have to extend the
language honestly. My comments below were related not to the
annotation-mockup of the feature, but to the actual feature itself.
What you're doing is inventing an entirely new kind of
implements-interface relation, one which the usual inheritance rule:
T implements I S extends T
----------------------------
S implements I
does not apply.
That's not to say that it can't be done, but I think you're dramatically
underestimating what you're suggesting.
I get it; what you really want to be saying is "gee, it would be nice if
we could express constraints on type variables that the type being
substituted provides static methods and/or constructors". And it would
be nice, but it's not a new observation, and the answers are complicated.
On 1/25/2023 10:54 AM, Red IO wrote:
> I'm not sure how big of a closing factor this implementation would
> have. Since the whole implementation is opaque to everyone but the
> compiler. If implemented that way it would be vital to guard the
> information carrying field from the user (against reflection for
> example). If done correctly this would be so opaque that a full
> implementation change would be possible. The sole requirement would be
> that you could use the type variable the same way in the new
> implementation.
> As I was exploring a possible implementation in my last mail I
> realized that it (by design) is 90% preprocessor. I think I will
> continue exploring it by trying to implement it using an annotation
> processor and explore potential problems while using it.
> Of cause the annotation processor will be ugly to use and for
> development only.
>
> Great regards
> RedIODev
>
> On Wed, Jan 25, 2023, 16:11 Brian Goetz <brian.goetz at oracle.com> wrote:
>
> This is a good example of a "you can, but you probably shouldn't"
> language feature. The power-to-weight ratio isn't favorable; it
> is a lot of new machinery and concept to move the ball forward a
> small amount. And as soon as the ball is moved forward by that
> amount, we will immediately be confronted by the next thing we
> can't do, and the solutions are likely to be an increasingly
> complex sequence of worse power-to-weight ratio ideas. (Careful
> observers of Java history might note that this phenomenon is
> especially evident in any proposal surrounding annotations.)
>
> As Michael K pointed out, other languages have explored more
> general, but more suitable, answers here; what you're looking for
> is a witness to conformance to a type class. (Our friends in C#
> have pursued something similar through abstract statics; for
> various reasons, that's less of a good match for Java than for
> C#.) This is not a small ask; its significant new complexity, but
> the power gained is much greater. If we were to choose to invest
> in solving problems like this one, that would likely be the path,
> but this is a big lift and we have other big things on our plate
> right now.
>
> As a general note, while it is fun to imagine new language
> features, language design needs to be a holistic process. If we
> did a hundred "point" features like this, what are the chances
> that the whole would hold together? If we did this feature, what
> other potential feature directions are we implicitly foreclosing
> on? These are the questions we address ourselves to when choosing
> what features to consider and not.
>
>
> On 1/25/2023 2:03 AM, Red IO 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/c62db9e9/attachment-0001.htm>
More information about the amber-dev
mailing list