Compiler problem with generics and private inner classes
I ran into the following discrepancy between javac and the Eclipse compiler. For the code:
publicclass Foo <T>{
private T myT;
public T getT(){
return myT;
}
publicvoid setT(T aT){
myT = aT;
}
}
publicclass Barextends Foo<Bar.Baz>{
publicstaticvoid main(String[] args){
Bar myBar =new Bar();
myBar.setT(new Baz());
System.out.println(myBar.getT().toString());
}
privatestaticclass Baz{
@Override
public String toString(){
return"Baz";
}
}
}
Eclipse compiles this fine, while javac complains that "Bar.java:1: Bar.Baz has private access in Bar".
I originally filed this as an Eclipse bug (https://bugs.eclipse.org/bugs/show_bug.cgi?id=185422) , but they seem to think (and I think I now agree) that it's a problem with javac.
Anyone have any thoughts one way or another?
[2004 byte] By [
bbark1eya] at [2007-11-27 3:21:53]

# 1
Very interesting problem, indeed!
My gut feeling is that javac has a valid point here:
By supplying the private class as a type parameter to another class, you somehow "release it to the public". Since Baz is declared private with respect to its enclosing class, this looks at least suspicious.
(As a sidenote, I do not understand Philippe Mulet's comment in the eclipse Bugzilla entry. The example he gives there bears no resemblance with the originial question, since it never uses a private type outside the enclosing class.)
# 2
> My gut feeling is that javac has a valid point here:
>
> By supplying the private class as a type parameter to
> another class, you somehow "release it to the
> public". Since Baz is declared private with
> respect to its enclosing class, this looks at least
> suspicious.
I originally thought the same as well, but in my case Baz extends a base class (and the generic T is 'T extends Base') and so has a public type. Also consider the case of the type implementing an interface such as Iterator. You'd still want the iterator to be 'released to the public' even though it's a private class.
A frustrating corner case of generics I guess.
# 3
> I originally thought the same as well, but in my case
> Baz extends a base class (and the generic T is 'T
> extends Base') and so has a public type.
"Baz" extends just Object, doesn't it?
Also, the type parameter used in "Foo" has no other bound except the implicit java.lang.Object.
So I'm afraid I don't get you here...
Also
> consider the case of the type implementing an
> interface such as Iterator. You'd still want the
> iterator to be 'released to the public' even though
> it's a private class.
In such a case you usually dont' publish the type itself, but rather supply an instance (which can be of a private type) that implements a public interface.
What do you think of the following line of reasoning?
Consider this code
public class Bar {
private static class Baz {
}
}
public class Foo {
public void set(Bar.Baz baz) {
}
}
I think we all agree that this cannot compile because "Bar.Baz" cannot be accessed outside of "Bar".
Now, why should these rules be changed only because "Foo" becomes a generic type?
In other words, why should this compile when used with the type parameter "Bar.Baz"?
public class Foo<T> {
public void set(T baz) {
}
}
# 4
Okay, these abstract examples aren't doing a good job of illustrating any real world use cases. Here's some (slightly) more realistic code:
public interface Service {
public void doSomething();
}
public class Base<T extends Service> {
private Map<String, T> svcMap = new HashMap<String, T>();
protected void registerSvc(String key, T svc) {
svcMap.put(key, svc);
}
protected T getSvc(String key) {
return svcMap.get(key);
}
}
public class MyClass extends Base<MyClass.MyService> {
private static class MyService implements Service {
public void doSomething() {
System.out.println("doing something...");
}
private void doSomethingPrivate() {
System.out.println("private...");
}
}
... creates, registers, and uses MyService instances
}
In this case the base class has common code for maintaining a service registry while remaining strongly typed for subclasses to use. MyClass can register multiple MyService classes and still be able to call doSomethingPrivate on them without a cast. Let's say that the Service is only ever really used within the class instance itself - so MyService is only used within MyClass. Ideally the visibility of MyService should be private b/c nobody else needs to be aware of it. The service registry methods on Base need to be protected so that subclasses can access them. Eclipse allows this case, but javac doesn't. In my mind it looks like a valid use case. The base class is responsible for managing common generic code which is available to subclasses but still strongly typed.
# 5
>Eclipse allows this case, but javac doesn't. In
> my mind it looks like a valid use case.
The original question was: Whose bevhaiour is right according to the current language spec - javac's or Eclipse's?
You're now trying to answer an entirely different question:
Can we find valid use cases that would make one or the other behaviour desirable?
That might be interesting, too, bu I still would like to know the answer to the original question...