Java Interop - Generics - Hmmm...

1,194 views
Skip to first unread message

Jules

unread,
Mar 24, 2011, 7:58:16 AM3/24/11
to clo...@googlegroups.com
Guys,

I have a hybrid Java/Clojure project.

The Java side contains a number of interfaces/classes that make use of generics.

Implementing the interfaces Clojure-side is no problem - I just forget about type and get on with it :-)

I am slowly migrating more and more code into Clojure and there will come a point when I'd like to take some of these interfaces over...

So now to my confusion:

My understanding is that generics are a compile-time, NOT a run-time abstraction.

This implies that metadata about generics would not be present at runtime and therefore [probably] not in bytecode ?

So, I would expect to be able to replace my interfaces with defprotocols - however, I would expect this to confuse Java implementations of these interfaces, since they somehow have access to their interfaces generics-oriented metadata...

So maybe I have answered my question - I guess that there must actually be some generics metadata present in the bytecode to allow inter-jar generics compilation to work, but then at runtime this would be ignored [probably].

So this leads me to conclude that I would

1. have to port my classes that implement generic interfaces to Clojure before the interfaces themselves.
2. give up the ability to write further generic implementations of these interfaces in Java

:-(

can anyone who really knows what the situation is confirm my suspicions ?

thanks for your time,

Jules

Armando Blancas

unread,
Mar 24, 2011, 10:17:43 AM3/24/11
to Clojure
For interop I write interfaces in Java. This way I can provide type-
specific signatures, constants, javadocs and generics; then implement
them all in Clojure. I also write exception classes in Java for use in
Clojure since I find it simpler and cleaner than gen-class. You don't
have to give up anyting for the sake of interop, but perhaps you won't
write 100% Clojure.

Jules

unread,
Mar 24, 2011, 11:04:18 AM3/24/11
to clo...@googlegroups.com, Armando Blancas
Thanks for the reply Armando,

This is pretty much where i was until I was asked to write a dot.net client library for my server.

I then looked at all my Java code and figured I could write a second copy in C# and then keep the two in sync for ever after, or port it all to Clojure and let ClojureCLR do the work...

I guess I can't have my cake and eat it.

In a perfect world, I would be able to sprinkle some extra metadata around my defprotocol to give Java classes implementing it whatever generics goodness they require - but I can understand this being low on Clojure's TODO list :-)

Jules

Andy Fingerhut

unread,
Mar 24, 2011, 11:19:07 AM3/24/11
to clo...@googlegroups.com
I'm not an expert on this, but I believe that whenever you have generics in Java, they have no effect on the bytecode, e.g. a HashMap<String,String> has the same type in the bytecode as any other HashMap.  The <String,String> part is only used in some checks made by the Java compiler when compiling Java source code, and in helping avoid some casting.  As evidence I have used reflection to examine the fields and method signatures of some Java classes that have generics, and there was nothing in the types except that which was specified outside the < ... >.

Andy

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Alan

unread,
Mar 24, 2011, 12:24:09 PM3/24/11
to Clojure
This is only half-true. The data exists somewhere, as Jules says, so
that javac can enforce proper use of generics when calling compiled
library code. Eg, rt.jar contains only classes, yet j.u.List still
manages to have generics, which are treated correctly by the compiler.
The *use* of generic code is compiled into just a bunch of Objects and
casting, but the .class file does contain some annotations to declare
the generics. It's just rather hard to find.

Jules

unread,
Mar 24, 2011, 12:29:56 PM3/24/11
to clo...@googlegroups.com, Andy Fingerhut
Thanks Andy,

I agree that there is no practical remnant of generics in the runtime as I have poked around with reflection as well, but I think there might be in the bytecode, otherwise if I compiled a generic interface, stuck it into a jar and gave it to you to link against, the compiler would know nothing about the genericness of my interface ? Surely this can't be right as generics DO exist at compile time, so I think that there is compile-time generics info stashed somewhere in the bytecode that is not loaded at runtime.

What do you reckon ? I don't see how else it could work - but this is all supposition - I'd be interested in hearing from someone in the know.

Jules

Alessio Stalla

unread,
Mar 24, 2011, 12:47:58 PM3/24/11
to clo...@googlegroups.com, Andy Fingerhut
On Thursday, March 24, 2011 5:29:56 PM UTC+1, Jules wrote:
Thanks Andy,

I agree that there is no practical remnant of generics in the runtime as I have poked around with reflection as well, but I think there might be in the bytecode, otherwise if I compiled a generic interface, stuck it into a jar and gave it to you to link against, the compiler would know nothing about the genericness of my interface ? Surely this can't be right as generics DO exist at compile time, so I think that there is compile-time generics info stashed somewhere in the bytecode that is not loaded at runtime.

Reflection is aware of generic type variables: http://download.oracle.com/javase/6/docs/api/java/lang/reflect/GenericDeclaration.html

What is lost at runtime is information about *instantiation* of those variables: e.g. you can't distinguish a method returning List<String> from one returning List<Integer>. (Actually, not even all instantiations of generic type variables are lost at runtime, see http://gafter.blogspot.com/2006/12/super-type-tokens.html for an example).

hth,
Alessio

Alan

unread,
Mar 24, 2011, 12:54:58 PM3/24/11
to Clojure
On Mar 24, 9:47 am, Alessio Stalla <alessiosta...@gmail.com> wrote:
> Reflection is aware of generic type variables:http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Generi...
>
> What is lost at runtime is information about *instantiation* of those
> variables: e.g. you can't distinguish a method returning List<String> from
> one returning List<Integer>.

I think you mean, you can't tell a List<String> from a List<Integer>
once it's been returned. As you demonstrated, telling which kind of
list a particular method will return can be done via reflection.

> (Actually, not even all instantiations of
> generic type variables are lost at runtime, seehttp://gafter.blogspot.com/2006/12/super-type-tokens.htmlfor an example).

That's really nifty. Thanks for the link.

Jules

unread,
Mar 24, 2011, 1:16:46 PM3/24/11
to clo...@googlegroups.com, Alan
cool :-)

I had a feeling that there was some vestige of generics left at runtime - now I know exactly what it is.

thanks guys,

Jules

Alessio Stalla

unread,
Mar 24, 2011, 3:22:11 PM3/24/11
to Clojure
On 24 Mar, 17:54, Alan <a...@malloys.org> wrote:
> On Mar 24, 9:47 am, Alessio Stalla <alessiosta...@gmail.com> wrote:
>
> > Reflection is aware of generic type variables:http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Generi...
>
> > What is lost at runtime is information about *instantiation* of those
> > variables: e.g. you can't distinguish a method returning List<String> from
> > one returning List<Integer>.
>
> I think you mean, you can't tell a List<String> from a List<Integer>
> once it's been returned. As you demonstrated, telling which kind of
> list a particular method will return can be done via reflection.

Only if the method is defined in a class that instantiates a type
variable, e.g. String get(int) in class MyList implements
List<String>, where the superclass is List<T> and defines T get(int).
But if you have class Foo<T> with a method T bar(), you can't tell via
reflection the distinction between new Foo<String>().bar() and new
Foo<Integer>().bar() - they're the same method of the same class at
runtime. Similarly if you have a method <T> List<T> foo(...) you can't
know about the different instantiations of T for calls to foo().

> > (Actually, not even all instantiations of
> > generic type variables are lost at runtime, seehttp://gafter.blogspot.com/2006/12/super-type-tokens.htmlforan example).
Reply all
Reply to author
Forward
0 new messages