Path: csiph.com!x330-a1.tempe.blueboxinc.net!newsfeed.hal-mli.net!feeder1.hal-mli.net!feeder.news-service.com!feeder.news-service.com!de-l.enfer-du-nord.net!feeder2.enfer-du-nord.net!cs.uu.nl!news.stack.nl!.POSTED!ipv6.urchin.earth.li!twic From: Tom Anderson Newsgroups: comp.lang.java.programmer Subject: Re: Why only public methods on interfaces? Date: Sun, 10 Apr 2011 16:02:21 +0100 Organization: Stack Usenet News Service Lines: 193 Message-ID: References: <25875c94-9af2-4d28-976d-2050a738ae2e@n10g2000yqf.googlegroups.com> <4sOdneh7k40lDgPQnZ2dnUVZ_vSdnZ2d@earthlink.com> <2011040801014026003-angrybaldguy@gmailcom> <5YSdnThYKPSwAgPQnZ2dnUVZ_gydnZ2d@earthlink.com> <58-dnYYCMK1urQLQnZ2dnUVZ_gGdnZ2d@earthlink.com> NNTP-Posting-Host: ipv6.urchin.earth.li Mime-Version: 1.0 Content-Type: TEXT/PLAIN; format=flowed; charset=US-ASCII X-Trace: mud.stack.nl 1302447741 26073 2001:ba8:0:1b4::6 (10 Apr 2011 15:02:21 GMT) X-Complaints-To: abuse@stack.nl NNTP-Posting-Date: Sun, 10 Apr 2011 15:02:21 +0000 (UTC) User-Agent: Alpine 2.00 (DEB 1167 2008-08-23) In-Reply-To: Xref: x330-a1.tempe.blueboxinc.net comp.lang.java.programmer:3020 On Sat, 9 Apr 2011, Mike Schilling wrote: > "Patricia Shanahan" wrote in message > news:58-dnYYCMK1urQLQnZ2dnUVZ_gGdnZ2d@earthlink.com... > >> The really unfortunate decision is not the public methods only, but the >> decision to make non-specification of access mean public interface >> method declarations but package access in class method declarations. >> That cuts off any possibility of changing interfaces now to make them >> more flexible. > > Actually, you could make an argument that "protected" on an interface > method would mean "only visible within the package", because it can't > mean "and also visible to derived types". It could mean 'visible to types derived from implementors of the interface'. It would mostly act to constrain the access modifier on the implementing method. If you interpreted interface-protected your way, you would have to permit implementors to declare their implementations package-access, otherwise it would be pointless, and that would mean that you would be writing implementations that appeared to have a narrower access modifier than their specification. In code: package x; public interface I { protected void m(); } public class A implements I { protected void m() {} // legal, looks normal, but widens access! } public class B implements I { /*package*/ void m() {} // is this legal? } package y; class C extends x.A { void foo() { m(); // i probably shouldn't be able to do this I i = this; i.m(); // i won't be able to do this } } Which is why ... On Sat, 9 Apr 2011, Mike Schilling wrote: > Unless new syntax like "package-private" is introduced. That seems like a very good idea. Indeed, a future revision of the language could do that, and at the same time deprecate the omission of an access modifier altogether. What i'd really like to see is a slightly more complicated but much more expressive system of access control. I worked this out with a friend a while ago, and i think the model should be: 1. The universe consists of a tree of things: the leaves are features (fields and methods), and the branches are containers (packages and classes - classes are also features in some ways, and i don't think that causes any difficulty), with the root being the default package. Packages can contain packages and classes; classes can contain classes and features. 2. Every feature explicitly exposes itself at one scope; it is visible to any code defined within that scope, including nested scopes. 'public' means it is exposed to the default package. 'private' means that it is exposed to the outermost enclosing class. This is as now. A new modifier 'package' means that it is exposed to the innermost enclosing package. This differs from the current default in that it is visible to subpackages (the current default is not visible in subpackages, although it is visible in nested classes in the same package). The lack of an explicit modifier would elicit a compiler warning, and be treated the same as 'package' (this widens access to these features, but in a backwards-compatible way). 3. In addition to these simple modifiers, it is possible to explicitly specify an exposure scope - this can be any scope enclosing the feature. The syntax i would use would be 'private(name.of.scope)'. 'public' is thus shorthand for 'private()', 'package' is shorthand for 'private(innermost.enclosing.package)', and 'private' is shorthand for 'private(outermost.enclosing.class)'. The scope name is resolved in the usual way, so scopes which are classes would not need to be fully qualified. 4. In addition to the exposure scope, a feature can be made visible to subclasses of its defining class, whatever package they may be in. This is done by replacing 'private' with 'protected'; this accepts an explicit scope in the same way as 'private'. The natural exposure scope for an unqualified 'protected' would be the same as for an unqualified 'private', ie the outermost enclosing class, but this would break backwards compatibility, so sadly, it must mean 'protected(innermost.enclosing.package)'. Perhaps we should allow a shorthand for the outermost enclosing class, perhaps 'this', so you could write 'protected(this)' to allow access only to subclasses and inner classes. The main thing that this gives you is a way to make features visible across packages in a hierarchy. This is a frequent problem in large projects which are intended to be modules in a larger world (eg on a vast project, or libraries for public consumption); a class in org.foo.io might need to be visible to one in org.foo or org.foo.core, but should not be visible to unrelated code in com.bar. Similarly, methods in a class in org.foo often need to be accessible to classes in org.foo.io, but not com.bar. At present, you end up with public methods marked 'PRIVATE API, DO NOT USE', which is total rubbish. The proposed access system would let you scope all these methods to org.foo, and have them visible to everything in the project. What it no longer lets you do is have things in org.foo that are *not* visible throughout the project. However, i would suggest that such things really belong in a subpackage. The parent package should be minimal, and only contain things for general consumption. It also lets you have features which are visible to subclasses but not the rest of the package, which is something i have always wanted. If we extend the explicit scope notation to accept multiple exposure scopes, then we have essentially copied the C++ friend mechanism. That would let me solve the classic JPA problem of how i stop unrelated classes calling the internal-use-only innerWhatever methods in this scenario (this is actually a slight variation in the classic pattern, but i hope it's clear): @Entity public class Department { @OneToMany(mappedBy="dept") private List employees; public void addEmployee(Employee emp) { emp.setDepartment(this); } public void removeEmployee(Employee emp) { emp.setDepartment(null); } void innerAddEmployee(Employee emp) { employees.add(emp); } void innerRemoveEmployee(Employee emp) { employees.remove(emp); } } @Entity public class Employee { @ManyToOne private Department dept; public void setDepartment(Department dept) { if (this.dept != null) this.dept.innerRemoveEmployee(this); if (dept != null) dept.innerAddEmployee(this); this.dept = dept; } } At the moment, the methods have to be at least package-access, because Employee needs to be able to call them. But with the proposed mechanism, i could declare them: private(Employee) void innerAddEmployee(Employee emp) private(Employee) void innerRemoveEmployee(Employee emp) And access would be restricted to just where they were needed. Compared to C++'s friendship system (which i don't understand well, so correct me if i'm wrong), we have finer-grained control over what is exposed (individual features, rather than all the features in the class), but coarser-grained control over who we expose it to (classes, rather than individual methods). The former is clearly better, because it means we can expose the things we need to, whilst keeping most details of the class encapsulated. I would argue that the latter is also better, because the exposing class has no business knowing which particular methods of the friend class are going to make use of the exposure; in the above example, i want to be free to refactor the calls from Employee to Department.inner* into a separate method (perhaps updateDepartmentEmployeeLists), without having to modify Department (particularly because it might be a private method - in which case in C++, Department would have to know about a private method of Employee!). tom -- A military-industrial illusion of democracy