Suspected problem with erasures
Hello,
I have a question regarding java generics.
To explain with an example,
There are three java projects UntypedLib, TypedLib and TypedApp with one class in each project as shown below.
Project TypedApp has a reference to project TypedLib and project TypedLib has a reference to project UntypedLib. More specifically TypedApp must not reference classes from UntypedLib directly.
So, TypedApp -> TypedLib -> UntypedLib (-> indicates project reference).
The idea here is that the class in TypedApp is not directly aware of the (untyped) class UntypedContainer in project UntypedLib. It should only be aware of the (typed) class TypedContainer in project TypedLib, which wraps an instance of UntypedContainer in UntypedLib.
The code snippet below (simplified to focus on problem) doesn't compile at the location (A).
-- Project UntypedLib
package com.test.untyped;
publicclass UntypedContainer{
publicvoid addElement(Object element){
}
}
-- Project TypedLib
package com.test.typed;
import com.test.untyped.UntypedContainer;
publicabstractclass TypedContainer<T>{
private UntypedContainer _delegate =null;
protected TypedContainer(){
}
publicvoid addElement(T typedElement){
_delegate.addElement(typedElement);
}
}
-- Project TypedApp
package com.test.app;
import com.test.typed.TypedContainer;
publicclass StringContainer<String>extends TypedContainer<String>{
// (A) compile error - The type com.test.untyped.UntypedContainer cannot be resolved. It is indirectly referenced from required .class files
publicvoid addStringElement(String s){
}
}
This is the case only if I use java generics in TypedContainer definition. Normally (i.e., in the non-generic case) StringContainer doesn't need UntypedContainer to compile as UntypedContainer is not part of the public signature of class TypedContainer. But it seems even classes in the private parts of TypedContainer are required to compile StringContainer when using generics!!
I did find that within TypedContainer instead of having a private member of type UntypedContainer, if the member is of type UntypedContainer_Proxy - a proxy to UntypedContainer defined within the same project (TypedLib), then the compilation error in StringContainer goes away. UntypedContainer_Proxy defined in project TypedLib looks like,
package com.test.typed;
import com.test.untyped.UntypedContainer;
publicclass UntypedContainer_Proxy{
private UntypedContainer _delegate =null;
publicvoid addElement(Object element){
_delegate.addElement(element);
}
}
But I am not sure why. I suspect this has something to do with erasures in java generics. Somehow looks like UntypedContainer is required during compilation when classes are being erased. Can someone having knowledge of generics/erasures internals suggest what's going on?
Any help appreciated.
regards, alex
Message was edited by:
PeterAhe: added code tags
# 1
I just created the classes and packages as you described and everything compiled fine. Maybe your original scenario has a different twist on it or the classpath for StringContainer is incorrect.
Btw., in StringContainer you define a Type Parameter named String. I am quite sure, this is not what you intended to do. Quite surely, StringContainer should not be parameterized, should it?
Note: please use the code-tag to make the source code parts of your posting more readable (see formatting guidelines of Java Forums).
# 2
I cannot reproduce the problem using this sequence of commands:javac UntypedContainer.java -d UntypedLibjavac -cp UntypedLib TypedContainer.java -d TypedLibjavac -cp TypedLib StringContainer.java
# 3
> public class StringContainer<String> extends TypedContainer<String> {
This won't be helping. You probably don't want a type variable with the same name as a class in the type system. You probably want:
public class StringContainer extends TypedContainer<String> {
(i.e. StringContainer doesn't need to be parameterised)
# 4
> This won't be helping. You probably don't want a type
> variable with the same name as a class in the type
> system. You probably want:
This is a big gotcha in generics. I don't know what the solution is but I wish there was some way to prevent this type of thing.
Maybe a PMD or similar tool could have a check for this.
# 5
> This is a big gotcha in generics. I don't know what
> the solution is but I wish there was some way to
> prevent this type of thing.
>
> Maybe a PMD or similar tool could have a check for
> this.
What should that tool check for other than what java provides, i.e., a warning that the name is hiding a class? It's the same with variable names, so it's not new with Generics.
# 6
> > This is a big gotcha in generics. I don't know
> what
> > the solution is but I wish there was some way to
> > prevent this type of thing.
> >
> > Maybe a PMD or similar tool could have a check for
> > this.
>
> What should that tool check for other than what java
> provides, i.e., a warning that the name is hiding a
> class? It's the same with variable names, so it's not
> new with Generics.
Does the compiler issue a warning for this? I wasn't aware of that.
In any event when someone sees String s, they
are usually not going to check that String isn't a
type parameter name. I've seen a lot of people
get very confused about this. Often, it's more of an
issue with types declared on the method. I've seen
(and probably done it my self) quite a few people think
they've bent generics to do something really slick
with generics when they've just created a type with
the name String.
# 7
> Does the compiler issue a warning for this? I wasn't
> aware of that.
Well, here it does: "The type parameter String is hiding the type String".
May depend on the compiler settings, of course.
> In any event when someone sees String s, they
> are usually not going to check that String isn't a
> type parameter name. I've seen a lot of people
> get very confused about this. Often, it's more of
> an
> issue with types declared on the method. I've seen
> (and probably done it my self) quite a few people
> think
> they've bent generics to do something really slick
> with generics when they've just created a type with
> the name String.
Of course, it's confusing. And surely bad code style. But that is up to the programmer, as the problem for using String as Type Parameter name holds for any other class name. It is a bit worse than declaring a variable, e.g. String Number;
, as they also stand for types. But I doubt you could do more than a warning (or only for a limited set of specific names), as class names can be chosen freely, too.
# 8
> Of course, it's confusing. And surely bad code style.
> But that is up to the programmer, as the problem for
> using String as Type Parameter name holds for any
> other class name. It is a bit worse than declaring a
> variable, e.g. String Number;, as they
> also stand for types. But I doubt you could do more
> than a warning (or only for a limited set of specific
> names), as class names can be chosen freely, too.
That's I guess where I was thinking a PMD or similar tool might come in.
You could create a rule that says types must be in all caps. Generally
class names are CamelCase. I've have seen people use CAPS
for acronymns but I disagree with that practice because it doesn't work
well with CamelCase. Instead I treat acronmyns as normal words for
in CamelCase. If you followed that rule, then a tool like PMD could just
cough on any CamelCase type names.
# 9
Well, in that case, one easily confuses type variables with constants, don't you think? I actually like the way of only using one-uppercase-lettered variable names, which bears few conflict. One should not need to have the use for more than 26 type variables per class definition, I
# 10
Hello all,
Firstly thank you all for your replies.
There is a typo in my code listing earlier - it should indeed be,
public class StringContainer extends TypedContainer<String>
Sorry about this and for leading some of you guys in the wrong direction. The code compiles successfully from the command line as noted by PeterAhe. The classpath references are also correct.
But the error remains in latest eclipse IDE (this was not mentioned clearly by me). Try adding the jar/folder/project containing TypedContainer.class to the classpath references of project containing StringContainer and check if you see the error.
In my case, the StringContainer code is actually generated using a code-generator plugin written for eclipse and hence compiling the generated code separately from the command line may not be an option.
I found later that this is not a problem in NetBeans IDE. May be this is an eclipse bug and needs to be investigated separately, but would nevertheless appreciate if any of the eclipse users can confirm this behavior and give your opinions on this.
thanks again,
Alex
# 11
Phew, if the class-file is generated it might be a refresh problem within eclipse not recognizing the new file in the path. Did you try to do a project refresh after the generation process? eclipse caches the file structures.
# 12
> Well, in that case, one easily confuses type
> variables with constants, don't you think? I actually
> like the way of only using one-uppercase-lettered
> variable names, which bears few conflict. One should
> not need to have the use for more than 26 type
> variables per class definition, I think.
I agree. I'm not going to say that someone would never have a good reason
to use more, though. But even if you never used more than one letter, a PMD
rule that checked for lowercase in type declarations would catch the error.
# 13
> I found later that this is not a problem in NetBeans
> IDE. May be this is an eclipse bug and needs to be
> investigated separately, but would nevertheless
> appreciate if any of the eclipse users can confirm
> this behavior and give your opinions on this.
Eclipse's compiler and the JDK compiler have both had bugs with regard to generics. Sometimes Eclipse gets it right, sometimes it's wrong. The issues have been clearing up over time. I know I've had an issue where Eclipse won't let me use switch with enums which should be allowed IIRC.
Also, along with what Stephan suggested, try a clean on your project in Eclipse. Eclipse sometimes gets 'stuck' in an inconsistent state or something. After you do your clean check the 'problems' view too. If there's something preventing compilation, Eclipse won't tell you other than in that view.