>> - 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);
    c.setX(4);
    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.



Reply via email to