Fun with generics

O

Oliver Wong

Dimitri Maziuk said:
Oliver Wong sez:

Yes, precisely. I don't know the type of E in "class Foo<ArrayList<E>>"
and that's what I want to tell the compiler.

That's like saying "I don't know the type of E in 'fsdljfskdjfsdf'". What
you wrote is not legal Java, and so doesn't make sense. I'm guessing what
you MEANT to say is something like this:

<paraphrasing>
I don't know the type of E in:

interface Foo [don't know what to write here] {
public ArrayList<E> getArray();
}
</paraphrasing>

And I'd answer:

<answer>
Okay, so you need to write code like this:

interface Foo<E> {
public ArrayList<E> getArray();
}
</answer>

If you protest "But Foo is not a collection of E!", I would reply "Foo
isn't a collection at all; but the unknown type is E, and that's what why
you write Foo<E>. 'Cause it's E that's unknown, not ArrayList which is
unknown."

And I can get it to compile with
class Foo<T, C extends ArrayList<T>>

Except it won't actually do what generics are supposed to: a
Foo<String, ArrayList<String>> will happily take an Integer due
to RTTE.

I'm not sure what YOU think generics is supposed to do, but probably
"Class Foo<T, C extends ArrayList<T>>" does exactly what it says it does
(unless there's a bug in your compiler).

When you say "Foo<String, ArrayList<String>> will happily take an
Integer", maybe you could post an SSCCE showing where exactly this Integer
will show up, and I can try to show you how to rewrite the code to disallow
this Integer from showing up there.
First of all, if that were true I'd get a "Cannot resolve symbol
Iterable".

No, if it were true, you'd get a program which compiled fine, but in
which name shadowing occured. It shouldn't say "Cannot resolve symbol
Iterable", because you DID declare the Iterable symbol.
Secondly, the compiler will let me use that field in foreach loop (sans
the cast to Object thanks to RTTE). Finally, if I replace ArrayList with
Iterable in my Foo, it'll actually run just as nicely (FVO "nicely" =
"casting Strings to Objects) as before.

So it's pretty safe to suggest that you think again (dunno about Chris).

From reading Chris' reply to your post elsewhere in the thread, I'm
fairly confident I had guessed correctly at what Chris meant.
As for your other question, both. As in
Table< ROWS extends List< ROW extends List< CELLSTORAGE > >,
COLS extends List< CELLTYPE > >

What's "CELLSTORAGE" in this case? Would this be the same as CELLTYPE?

- Oliver
 
D

Dimitri Maziuk

Chris Smith sez:
I think you've run into a problem with Sun's not particularly general
description of the purpose of generics.

Sort of. They didn't exactly enumerate things you can and cannot do
with generics, so I'm poking at it to see what breaks.

In your case, you already know the basic type of the collection (it's
ColHdr), so what you need to communicate is the type parameter to
ColHdr. The way you do that is with a type parameter to Table

and my comment was that it's ugly because conceptually C is the type
parameter to ColHdr, Table itself should not care. What would make
sense for the Table is to limit the column header types. So:
Table<H extends ColHdr>

Since ColHdr is generic itself, this becomes
Table<H extends ColHdr<C>>
-- where I get to "undeclared C".

Next,
Your code above, though, with some additions, does make sense:

class Foo<T extends ArrayList<Comparable<T>>>

Yep, except "T extends ArrayList<T>" makes my brane hurt because
it looks like it says "type T is a kind of ArrayList of type T
(see also recursion)". So I'd rather go with
That is, in fact, not just faking it, but rather doing what you might
have meant to begin with.

Well, it's faking it in the sense that it shuts up the compiler.
Running that is a different story.
(I also have an esthetic objection to declaring C that isn't used,
but that pales in comparison with RTTE.)

....
You mean that you could do:

interface Table<C, T extends ColHdr<C>> { ... }

Yes, you can, but I don't think you want to. Do you expect people to
subclass ColHdr?

It was an interface, I expect them to implement it.

....If they do, is it important when they use a table that
they remember exactly which subclass of ColHdr they chose?

No, I want exactly what generics is supposed to do: tell the
compiler that my Table collection is a collection of elements
of type ColHdr.

....
I don't recall what Bruce Eckel said. He may have said that the problem
generics solve is not serious, and I may agree with him in some
contexts. But I'd bet a kidney that he didn't say what you're saying in
this thread.

He said more or less this:
If you want your class to contain only Iterable elements, then you don't
need generics.

And it's pretty much what I ended on. I can't help noticing the oxymoron,
however:

"the goal of generics is to communicate the type of a collection element
to the compiler so it can be checked. If you want your collection to
contain only Iterable elements, then you don't need generics".

....
No, in the code above, it's the name of a formal type parameter.

Right, my bad, I guess. My original comment was that <T extends Iterable>
is not the best choice of syntax and it'd be nice if they made it
consistent with type declarations: allow "implements X" as well as
"extends Y" in there. Or -- much cleaner -- to just write <Iterable>
and disambiguate formal type parameters with <typename T>.

Dima
 
C

Chris Smith

Just a few more comments.

Dimitri Maziuk said:
and my comment was that it's ugly because conceptually C is the type
parameter to ColHdr, Table itself should not care.

Both conceptually and actually, C itself is a type parameter to Table.
Whatever type C represents will also become a type parameter to ColHdr.
You seem to be drawing some kind of distinction here that doesn't make
much sense to me, and you've apparently decided that C shouldn't be a
type parameter to Table, even though Table needs to use the type (even
if only to use specific instances of ColHdr types) and doesn't already
know what it is.

Perhaps a C++ example would be more to your liking. std::map<K,V> takes
two type parameters K and V, even though it's really (at least
conceptually) a collection of std::pair<K,V>. Despite that, it is still
not declared with "template<class std::pair<K,V>>".

In any case, you simply can't write the code you apparently want to
write. Or rather, you can, but it won't compile.
...If they do, is it important when they use a table that

No, I want exactly what generics is supposed to do: tell the
compiler that my Table collection is a collection of elements
of type ColHdr.

Then Table<C> it is.

Since you're writing Table, you don't do that with generics. You do
that by having your methods receive and return variables of type ColHdr.
Generics solve a different problem; if, for example, you wanted to write
Table without deciding what it is a collection of, and then wanted
someone else later on to inform the compiler that it's a collection of
ColHdr, *then* you would want to use generics to declare that type.

As it is, then, you don't want to use generics to declare the thing that
a Table holds -- because it's always ColHdr, and the client code doesn't
need the Table to remember which implementing class of ColHdr is being
used. The only thing you need generics for, then, is to tell the
compiler what exact kind of ColHdr to use. That's C in our discussion
until now, so C is your type parameter.
[Bruce Eckel] said more or less this:
If you want your class to contain only Iterable elements, then you don't
need generics.

And it's pretty much what I ended on. I can't help noticing the oxymoron,
however:

"the goal of generics is to communicate the type of a collection element
to the compiler so it can be checked. If you want your collection to
contain only Iterable elements, then you don't need generics".

Bruce Eckel's statement, then, while correct, seems to be about
something different. Bruce is talking from the client standpoint... and
he's right; with a few simple casts, generics are not needed. When I
said generics aren't needed, though, I meant something different... you
really get NO benefit from using a type parameter in that case. Bruce
Eckel is talking about whether the language feature of type parameters
is helpful, but I'm saying that your specific type parameter right there
is not helpful.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
H

Hendrik Maryns

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
NotDashEscaped: You need GnuPG to verify this message

Dimitri Maziuk schreef:
Hendrik Maryns sez:

My theory is that thanks to RTTE Foo<Bar<T>> becomes Foo<Bar>,

No, it becomes Foo.
at which point <T> causes compiler error.

If it causes a compiler error, then it can have nothing to do with RTTE
(took me a while to guess this is run-time type erasure), as the first
two letters imply.

Or something.

Indeed. Do read up on generics.
Anyway, looks like it can't be done and I'm redesigning the
interface instead.

That?s the other way. But you take you arse with you, as this nice
Russian proverb says.

H.
--
Hendrik Maryns

==================
www.lieverleven.be
http://aouw.org
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2 (GNU/Linux)

iD8DBQFEIpooe+7xMGD3itQRAtEVAJ9WhfD6McDdW/1ReIeiPP+VlKOnggCeMW0J
TbeDJ3uBeqENIOuVZCpDn6c=
=CNDe
-----END PGP SIGNATURE-----
 
D

Dimitri Maziuk

Chris Smith sez:
....
Perhaps a C++ example would be more to your liking. std::map<K,V> takes
two type parameters K and V, even though it's really (at least
conceptually) a collection of std::pair<K,V>. Despite that, it is still
not declared with "template<class std::pair<K,V>>".

Because it isn't. Map's job in life is to associate keys with values,
its function is "V get( K )". There's no pair in there.

A C++ example more to my liking is
template<typename K, template <typename U, typename V> class pair>
class PairMap

Dima
 
C

Chris Smith

Dimitri Maziuk said:
A C++ example more to my liking is
template<typename K, template <typename U, typename V> class pair>
class PairMap

It may be to your liking, but it does something different than what
you've been asking about on this newsgroup. Just like the Iterable
example earlier, you have managed to hide std::pair, in the code above,
with a template parameter named pair. That template parameter is
expected to be a type that requires two template parameters of its own,
but a specific instantiation of the PairMap template does not contain
information about the values of U and V.

In other words, if you want to have a map to a specific kind of pair,
you'd have to do this instead:

template<typename K, typename A, typename B,
template <typename U, typename V> class pair>
class PairMap
{
// use pair<A,B> as the pair type
}

or, if you really didn't intend to provide for your client code to
provide an alternative implementation instead of std::pair, then you'd
do this instead:

template<typename K, typename A, typename B>
class PairMap
{
// use std::pair<A,B> as the pair type
}

And that looks surprisingly like the Java code everyone has been telling
you to write.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top