> De: "Brian Goetz" <brian.go...@oracle.com> > À: "Remi Forax" <fo...@univ-mlv.fr> > Cc: "amber-spec-experts" <amber-spec-experts@openjdk.java.net> > Envoyé: Dimanche 13 Septembre 2020 15:42:39 > Objet: Re: Is case var(var x, var y) a valid syntax ?
>>> - 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; >> if (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. if deconstructors are not virtual, Is the salary printed is 2000 or the compiler raises an error somewhere ? > So I’m not sure how much we can learn from this particular example. Maybe you > have a better one? No a better one, just another one, you want to be able to declare a deconstructor abstract by example on an interface interface Map { interface Entry<K,V> { public abstract deconstructor Entry(K key, V value); } } ... for(Map.Entry<String, String>(var key, var value) : entries) { ... } BTW, it's also an example where 'var' can be useful instead of having to specify the full type for(var(var key, var value) : entries) { ... } >>> - 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 via 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.) I get that you can write the code like this if baseSalary is declared private in Employee class VP extends Employee { int bonus; deconstructor VP(int salary) { super(var baseSalary) = this; return (baseSalary + bonus); } } still, a deconstructor is unlike a constructor because you can call a constructor directly something you can not do with a deconstructor. >>> 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. so why my first example is questionable usage of the deconstructor API. > 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. and now introduce inheritance to the mix and you will see that a deconstructor has to be virtual. Basically, we are at the point where it's not useful anymore to call it a deconstructor because while it's like the reverse of a constructor, at the same time it's also close to an API like clone(). Rémi