Is case var(var x, var y) a valid syntax ?

Brian Goetz brian.goetz at
Sun Sep 13 13:42:39 UTC 2020

>> - While instance members, they are not inherited (just like constructors)
> At least you want a deconstructor to be overrideable (which is not fully equivalent to being inherited).
> A deconstructor is for allowing encapsulation so the world projected by a deconstructor may have a little to share with how the the class are implemented
>  class Employee {
>    int baseSalary;
>    deconstructor Employee(int salary) { return (baseSalary); }
>  }
>  class VP extends Employee {
>    int bonus;
>    deconstructor VP(int salary) { return (baseSalary + bonus); }
>  }
>  ...
>  Vp vp = new VP();
>  vp.setBaseSalary(2000);
>  vp.setBonus(500);
>  Employee employee = vp;
>  employee instanceof Employee(salary) {
>    System.out.println(salary);  // 2500
>  }
> and here you can see that Employee(salary) is not a call to the deconstructor but an instanceof Employee + a call to the deconstructor (int salary) !

I think what you are alluding to here is the idea that a deconstructor is like a “multi-accessor”, and accessors are virtual but deconstructors are not. 

But the example is distorted for two reasons; this is already a questionable deconstructor API, and even if so, Employee is conflicted about the distinction between salary and baseSalary.  So I’m not sure how much we can learn from this particular example.  Maybe you have a better one?

>> - They can only be called via a pattern match (just as a constructor can only be
>> called via a `new` operation.)
> so unlike a constructor that can be called either by a new or by this(...) and super(...) a deconstructor can only be called vua pattern matching.

You should re-read the document about deconstructors, as this symmetry is well covered.  The case of one deconstructor delegating to another, just like one constructor delegating to another, is important, because we want each class to be responsible for its own state.  So yes, this is covered.  (Technically, though, this sort of delegated invocation _is_ a pattern match, so the statement “only through pattern matching” still stands.)  

>> In this way, both ctor and dtor mediate access between an external API and the
>> internal representation.
> It's only true for a constructor if the constructor (constructors) are the only way to change the value of an instance.
> It's only true for a deconstructor if the deconstructor has a matching constructor

Both of these “only true” claims are not true :)  

You can have multiple constructors with different views of the state (overloading), and these views could be overlapping or non-overlapping.  And you can have multiple deconstructors with different views of the state too.  And have the choice to align the constructor / deconstructor views, or not.  You can have matching ctor/dtor, or asymmetric ones — this is a matter of API design.  

We anticipate it will be common to provide matched ctor/dtor pairs, because together these form an adjunction between the state space of the object and the external API shared between ctor/dtor, which is a useful and practical building block.  

Even for mutable objects, deconstructors are still sensible. 

class C {
    int x;

    C(int x) { this.x = x; }
    deconstructor C(int x) { x = this.x; }

    void setX(int x) { this.x = x; }

I can do:

    C c = new C(3);
    if (c instanceof C(var x)) { … x is 4 here … }

The deconstructor is free to track the state, mutable or not.   Again, this is a tool for API design, and it can be used in multiple ways.  The record case is notable because records have a highly constrained API and  they are not extensible, so they are the best behaved of the bunch.  But classes can play this game too.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the amber-spec-experts mailing list