Fun with generics

D

Dimitri Maziuk

Let's say I want to write a table that may be backed by an SQL table
or by in-memory store. The table is a list of column headers and a
list of rows.
public interface ColHdr<C> {
String getName();
int getWidth();
C getType();
}
-- where type will be implementation-specific, say, an enum.

Rows may be a simple list of String [] or something else iterable.
So,
public interface Table<R implements Iterable,ColHdr<C>> {
R getRows();
List<ColHdr<C>> getCols();
}
That doesn't compile. What compiles is
public interface Table<R extends Iterable,C> {
R getRows();
List<ColHdr<C>> getCols();
}
which is not really what the table is about: its second parameter
should not be a "column type" (C), it should be a "column header
that uses C for column type".
public interface Table<R extends Iterable,ColHdr>
compiles but loses the <C> -- not quite what I wanted either.

Anyone knows how to declare an interface that takes one formal
type parameter and one type that is itself generic?

Dima
(While we're at it, I wonder who came up with
public class bar extends Iterable <-- error, of course
public class bar implements Iterable <-- that's how you do it,
but
public interface foo<T implements Iterable> <-- error
public interface foo<T extends Iterable> <-- ruh-iight)
 
O

Oliver Wong

Dimitri Maziuk said:
Let's say I want to write a table that may be backed by an SQL table
or by in-memory store. The table is a list of column headers and a
list of rows.
public interface ColHdr<C> {
String getName();
int getWidth();
C getType();
}
-- where type will be implementation-specific, say, an enum.

Rows may be a simple list of String [] or something else iterable.
So,
public interface Table<R implements Iterable,ColHdr<C>> {
R getRows();
List<ColHdr<C>> getCols();
}
That doesn't compile. What compiles is
public interface Table<R extends Iterable,C> {
R getRows();
List<ColHdr<C>> getCols();
}
which is not really what the table is about: its second parameter
should not be a "column type" (C), it should be a "column header
that uses C for column type".
public interface Table<R extends Iterable,ColHdr>
compiles but loses the <C> -- not quite what I wanted either.

How about:
<code>
public interface ColHdr<C> {
String getName();
int getWidth();
C getType();
}


public interface Table<R extends Iterable,ColHeads extends ColHdr<?>> {
R getRows();
List<ColHeads> getCols();
}
</code>

You're not using the C in your code for Table, so it doesn't need to be
specified.
Anyone knows how to declare an interface that takes one formal
type parameter and one type that is itself generic?

Dima
(While we're at it, I wonder who came up with
public class bar extends Iterable <-- error, of course
public class bar implements Iterable <-- that's how you do it,
but
public interface foo<T implements Iterable> <-- error
public interface foo<T extends Iterable> <-- ruh-iight)

A class can't extend an interface. An interface can extend an interface.
A type can extend an interface. T is a type, not a class. But I'm not even
sure why you would need two different keywords, "extends" and "implements".
If we had only a single keyword for both situations, would there be any
ambiguity?

I don't know who came up with it though.

- Oliver
 
O

Oliver Wong

Oliver Wong said:
How about:
<code>
public interface ColHdr<C> {
String getName();
int getWidth();
C getType();
}


public interface Table<R extends Iterable,ColHeads extends ColHdr<?>> {
R getRows();
List<ColHeads> getCols();
}
</code>

After thinking about it a bit, maybe you were trying something like this?

<code>
public interface ColHdr<C> {
String getName();
int getWidth();
C getType();
}


public interface Table<C, R extends Iterable<C>,ColHeads extends
ColHdr<C>> {
R getRows();
List<ColHeads> getCols();
}
</code>

That is, the getType method returns the type that you're iterating over? It
wasn't clear to me if this was your intent or not.

- Oliver
 
T

Thomas Hawtin

Oliver said:
public interface Table<C, R extends Iterable<C>,ColHeads extends
ColHdr<C>> {
R getRows();
List<ColHeads> getCols();
}

Or more generally:

public interface Table<
C,
R extends Iterable said:
R getRows();
List<ColHeads> getCols();
}

If you don't mind it being used in a contrary way:

public interface Table<
R extends Iterable said:
R getRows();
List<ColHeads> getCols();
}

Or even:

public interface Table<
R,
ColHeads
R getRows();
List<ColHeads> getCols();
}

Tom Hawtin
 
D

Dimitri Maziuk

Oliver Wong sez:
<code>
public interface ColHdr<C> {
String getName();
int getWidth();
C getType();
}

public interface Table<R,ColHeads extends ColHdr<C>> {
List<ColHeads> getCols();
}
is not it: ColHeads would be a List<ColHdr<C>> and it wouldn't
fix the problem.

<R,List<ColHdr<C>>> doesn't work any better than <R,ColHdr<C>>.

Plus,
public interface Table<R,T extends ColHdr<C>> {
public List<ColHdr<C>> getColumns();
}
complains that it "cannot find symbol class C" (besides the unused "T".
This looks the problem that C++ solves with "typename" keyword, BTW).
Method declaration can be fixed:
public <C> List<ColHdr<C>> getColumns();
but that does not seem to work for interface declaration.

It get better:
public abstract class Table<C> {
public abstract List<ColHdr<C>> getColumns();
}
was OK as interface declaration, but is not OK as a class.
public interface Table<C, R extends Iterable<C>,ColHeads extends
ColHdr<C>> {
R getRows();
List<ColHeads> getCols();
}
</code>

That is, the getType method returns the type that you're iterating over? It
wasn't clear to me if this was your intent or not.

No, it's a table. I'm iterating over _rows_, and I should have left
that bit out for simplicity's sake when I posted it (my code above has
just "R", without "extends Iterable").

getType() returns _column_ type. Each column has a "header" that
remembers its name, size, and type. That type is the "C". It can be
e.g. "rcsb([:upper:]|[:digit:]){6}" -- just to give an example.

Dima
 
D

Dimitri Maziuk

Thomas Hawtin sez:
Or more generally:

public interface Table<
C,

R getRows();
List<ColHeads> getCols();
}

Nope. All I want is
public interface Table<ColHdr<C>>
and "ColHdr" is what causes compiler error, with or without "? extends".

As for "extends Iterable", that was just a side comment: sure, you can
declare
interface Foo<Iterable> {
<T> void setBar( Iterable T );
}
but wouldn't this
interface Foo<Iterable T> { // or "<T implements Iterable>"
void setBar( T );
}
be much clearer?

Dima
 
H

Hendrik Maryns

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

Dimitri Maziuk schreef:
Ian Pilcher sez:

Not according to my compiler.

It?s not about what you compiler wants, it?s about what you want to do
with the method. If you want to tell the client what the type of the
columns is, then Ian?s suggestion is right. If you want to return some
element from the column, then leave it as is.

H.
--
Hendrik Maryns

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

iD8DBQFEH/Hke+7xMGD3itQRAlaLAJ9w20lZczIvQPLzqPHXV7e+DRmZGACfdjEM
ypamUxLC5kJXXAE7npuuKJI=
=8HP4
-----END PGP SIGNATURE-----
 
H

Hendrik Maryns

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

Dimitri Maziuk schreef:
Thomas Hawtin sez:
Nope. All I want is

Why don?t you start over again and explain _exactly_ what you?re trying?
It?s such a mess now, that I cannot even try to help you.

H.
--
Hendrik Maryns

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

iD8DBQFEH/Ige+7xMGD3itQRAojEAJ0RDV0WlOCbW4OnX40844ySjZ+8mwCcDKsn
l1fogyqWGUmkR7lc+kSKHx4=
=gocx
-----END PGP SIGNATURE-----
 
D

Dimitri Maziuk

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

Dimitri Maziuk schreef:


Why don?t you start over again and explain _exactly_ what you?re trying?
It?s such a mess now, that I cannot even try to help you.

My theory is that thanks to RTTE Foo<Bar<T>> becomes Foo<Bar>,
at which point <T> causes compiler error. Or something.
Anyway, looks like it can't be done and I'm redesigning the
interface instead.

Dima
 
?

=?ISO-8859-15?Q?R=E9mi?= Bastide

Dimitri said:
Let's say I want to write a table that may be backed by an SQL table
or by in-memory store. The table is a list of column headers and a
list of rows.
public interface ColHdr<C> {
String getName();
int getWidth();
C getType();
}
-- where type will be implementation-specific, say, an enum.

Rows may be a simple list of String [] or something else iterable.
So,
public interface Table<R implements Iterable,ColHdr<C>> {
R getRows();
List<ColHdr<C>> getCols();
}
I do not understand how you plan to instantiate these generic definitions to
get a real table, could you give an example ?
 
C

Chris Smith

Dimitri Maziuk said:
Nope. All I want is
public interface Table<ColHdr<C>>
and "ColHdr" is what causes compiler error, with or without "? extends".

That's just not a sensible thing to want. ColHdr is an actual class. A
name for a type parameter is syntactically supposed to go there. You
can't use a real class as a type parameter -- if that's what you wanted,
you'd just leave out the type parameter and use the real type.

Table is parameterized on C, not on ColHdr<C>. The fact that it only
uses C in order to work with values of ColHdr<C> is not really relevant
here. It's C that is the unknown type, so C needs to be the type
parameter.

Now, what problem do you think that's causing? If you explain something
you want to do (in a functional, not syntactic, sense) and can't, then
that provides a starting point for helping you out.
As for "extends Iterable", that was just a side comment: sure, you can
declare
interface Foo<Iterable> {
<T> void setBar( Iterable T );
}

This would be very bad code. Iterable is the name of a standard library
interface. Shadowing it with a type parameter name is quite confusing.
I think you're somehow confused and you believe that Iterable above
actually has something to do with the standard library interface... but
I'm not entirely sure.
but wouldn't this
interface Foo<Iterable T> { // or "<T implements Iterable>"
void setBar( T );
}
be much clearer?

Certainly this is quite valid:

interface Foo<T extends Iterable<?>>
{
void setBar(T t);
}

and this is valid:

interface Foo<T>
{
void setBar(Iterable<T> t);
}

I'm not sure which you mean.

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

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

Chris Smith

Hendrik Maryns said:
It?s not about what you compiler wants, it?s about what you want to do
with the method. If you want to tell the client what the type of the
columns is, then Ian?s suggestion is right. If you want to return some
element from the column, then leave it as is.

In the original post, Ian already explained that C is an enum of types,
and getType returns a value of that enum. The original code seems to be
correct in this regard.

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

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

Ian Pilcher

Chris said:
In the original post, Ian already explained that C is an enum of types,
and getType returns a value of that enum. The original code seems to be
correct in this regard.

Actually, Ian was the person who missed that fact.
 
D

Dimitri Maziuk

Chris Smith sez:
That's just not a sensible thing to want. ColHdr is an actual class. A
name for a type parameter is syntactically supposed to go there.

Huh? A Table is a colection of column headers and rows. ColHdr is
a type (an inteface, BTW, may or may not be quite the same as your
definition of "actual class").

The goal of generics is to communicate the type of a collection
element to the compiler, so it can be checked (quoting Sun). That's
all I'm doing.

....You
can't use a real class as a type parameter -- if that's what you wanted,
you'd just leave out the type parameter and use the real type.

You mean I can't do this:

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

-- javac seems to disagree with you there.

What I can't do is
class Foo<C extends ArrayList<Comparable<T>>> -- error, cannot resolve
symbol T. However, I can fake it by
class Foo<T, C extends List<Comparable<T>>>

In other words, I can do "Table<C, ColHdr<C>>", it's just that
syntax is ugly. (See also C++ "typename" keyword.)

.... said:
This would be very bad code. Iterable is the name of a standard library
interface. Shadowing it with a type parameter name is quite confusing.

Iterable is a type. It is perfectly reasonable to want to tell the
compiler that my home-grown collection Foo takes only Iterable elements
-- and I can, except the syntax is ridiculous: nobody would want to
actually _extend_ Iterable, as syntax suggests. (Again, cf. C++
template specialization.)
and this is valid:

interface Foo<T>
{
void setBar(Iterable<T> t);
}

Yes, but this is also stupid: Foo is not a collection of T, it
is a collection of Iterable<T>. But you can't adequately express
that in current syntax. Besides,

interface Foo {
<T> setBar( Iterable<T> t );
}

is just as valid, so in practice there's no advantage to
generifying the type Foo itself. (ISTR Bruce Eckel making a similar
argument when generics first came out.)

Dima
 
O

Oliver Wong

...You

You mean I can't do this:

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

-- javac seems to disagree with you there.

No, what he means is you can't do this:

class Foo<ArrayList> {
public ArrayList getMyThing() {...}
}

I.e. you can't specify an "actual class" (where ArrayList is an example of
an actual class), because if you knew ahead of time what actual class you
wanted, you could just put it directly into the code (as seen here in
"public ArrayList getMyThing()". Generics are for when you DON'T know the
type, as in:

class Foo<T> {
public T getMyThing() {...}
}


Iterable is a type. It is perfectly reasonable to want to tell the
compiler that my home-grown collection Foo takes only Iterable elements
-- and I can, except the syntax is ridiculous: nobody would want to
actually _extend_ Iterable, as syntax suggests. (Again, cf. C++
template specialization.)

Chris was subtly saying that the code you provided doesn't mean what you
think it means. You've declared a new generic type variable called
"Iterable", when you probably mean to refer to the existing Iterable. It's
similar to this mistake:

class Foo {
int x;

public void someMethod() {
int x; //shadowing is occuring here.
}
}

Instead of referring to the existing field called "x", you've created a
new local variable called "x".

Also, for claritiy, here's the code Chris was referring to as being
"bad" and in which shadowing is occuring (which seems to have gotten
snipped)

<quote>
interface Foo<Iterable> {
<T> void setBar( Iterable T );
}
Yes, but this is also stupid: Foo is not a collection of T, it
is a collection of Iterable<T>. But you can't adequately express
that in current syntax.

The question is, what is it that's generic? Is it what specific subclass of
Iterable that's in use that is generic? If so, then the code should be

interface Foo<T extends Iterable<?>> {
void setBar(T t);
}

Or is it the type of Iterable which is generic? If so, then the code should
be as Chris posted above. Generics are not used only for collections, which
is why it doesn't make sense to restriction the conceptual definition of
generics as "the type of elements contained in a collection".
Besides,

interface Foo {
<T> setBar( Iterable<T> t );
}

is just as valid, so in practice there's no advantage to
generifying the type Foo itself. (ISTR Bruce Eckel making a similar
argument when generics first came out.)

Well, no, it's not valid. You need to specify a return type for the
setBar() method. But let's pretend you specified that it returns T, for
example.

It does something different from the rest of the code posted in this
thread. It's like saying the "Hello World!" program is just as much "valid
Java" as the "Azureus" program. Yes, they both consist of Java code, but
they do two different things.

- Oliver
 
D

Dimitri Maziuk

Oliver Wong sez:
....
No, what he means is you can't do this:

class Foo<ArrayList> {
public ArrayList getMyThing() {...}
}

I.e. you can't specify an "actual class" (where ArrayList is an example of
an actual class), because if you knew ahead of time what actual class you
wanted, you could just put it directly into the code (as seen here in
"public ArrayList getMyThing()". Generics are for when you DON'T know the
type

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.

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
Chris was subtly saying that the code you provided doesn't mean what you
think it means. You've declared a new generic type variable called
"Iterable", when you probably mean to refer to the existing Iterable.

First of all, if that were true I'd get a "Cannot resolve symbol Iterable".
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).

As for your other question, both. As in
Table< ROWS extends List< ROW extends List< CELLSTORAGE > >,
COLS extends List< CELLTYPE > >

Dima
 
C

Chris Smith

Dimitri Maziuk said:
The goal of generics is to communicate the type of a collection
element to the compiler, so it can be checked (quoting Sun). That's
all I'm doing.

I think you've run into a problem with Sun's not particularly general
description of the purpose of generics. Generics are used by the
collections API to allow you to communicate the type of a collection.
Since most people come across generics in the context of the collections
API, it makes some sense for Sun to say that (depending on context,
which I didn't see)... but it's an incomplete statement.

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, as such:

public interface Table<C>
{
...You

You mean I can't do this:

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

No, I mean you can't do this:

class Foo<ArrayList<Comparable<T>>> ...

That doesn't work, because the Java language requires that the formal
type parameter list following a class declaration consists of actual
type parameters. You seem to want to declare type parameters in context
and have the compiler match that against type names in a kind of pattern
matching, and you just can't do it that way.

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

class Foo<T extends ArrayList<Comparable<T>>>
{
public void doSomethingWith(T t) { ... }
public T getSomething() { ... }
}

This says that there's a type called T, and that T is constrained to be,
or to be is a subclass of, ArrayList<Comparable<T>>. Subclasses of
ArrayList are rather rare, but nevertheless you can do this. Its
advantage (though a little dubious) over just writing a non-generic Foo
using ArrayList is that if someone wants to create a Foo that only works
with instances of the subclass javax.management.AttributeList (which
subclasses ArrayList), they can do so, and they can be assured that the
compiler will complain if they pass anything else, and that they don't
need an explicit cast on the result of getSomething().

Note that this only makes sense if you expect someone to use Foo in such
a way that it's important that it only works with some subclass of
ArrayList. Otherwise, you wouldn't write a generic class. (Also, you
probably want a wildcard in the type parameter to Comparable, but that's
a different matter.)
What I can't do is
class Foo<C extends ArrayList<Comparable<T>>> -- error, cannot resolve
symbol T.

Yep, because you haven't declared T.
However, I can fake it by
class Foo<T, C extends List<Comparable<T>>>

That is, in fact, not just faking it, but rather doing what you might
have meant to begin with.
In other words, I can do "Table<C, ColHdr<C>>", it's just that
syntax is ugly.

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? If they do, is it important when they use a table that
they remember exactly which subclass of ColHdr they chose? If the
answer to both of these questions is yes, then the above is what you
want. Otherwise, you really want this simpler code:

interface Table said:
(See also C++ "typename" keyword.)

Why would I see the C++ typename keyword? These are generics in Java.
They have very little to do with templates in C++, aside from solving
some of the same problems and using a similar syntax. The core concepts
are very different. For example, there is no such thing as type bounds
or erasures in C++.

Watch the quoting. My comment referred to this code:

interface Foo said:
Iterable is a type.

No, in the code above, it's the name of a formal type parameter. It has
nothing at all to do with java.lang.Iterable. That was why I said the
code is very poor. But yes, I understand that you wanted it to be
java.lang.Iterable. I'm trying to explain that you are miunderstanding
the purpose of generics when you want that. One more example:
It is perfectly reasonable to want to tell the
compiler that my home-grown collection Foo takes only Iterable elements

Then do it this way:

class Foo
{
public void add(Iterable t) { ... }
public Iterable get(int i) { ... }
...
}

If you want your class to contain only Iterable elements, then you don't
need generics. If you'd like to implement an existing generic
interface, though, then you can do so like this:

class Foo implements Collection said:
-- and I can, except the syntax is ridiculous: nobody would want to
actually _extend_ Iterable, as syntax suggests.

I presume you mean: class Foo<T extends Iterable> { ... }

The syntax suggests that for a good reason. Declaring a class like that
suggests that someone does indeed want to extend the type Iterable, and
that they want to use Foo while remaining aware of the specific subtype
with respect to which this Foo instance if used. Otherwise, you'd write
the non-generic Foo implementation I provided above.
(Again, cf. C++ template specialization.)

Java does not implement C++ template specialization. It is not a
compatible concept with Java generics. Again, generics and C++
templates are conceptually different things, so ideas don't move over
wholesale from one to the other.
Yes, but this is also stupid: Foo is not a collection of T, it
is a collection of Iterable<T>.

The unknown type is T. Therefore, you specify T as the type parameter.
You've definitely gotten too hung up on an incomplete statement in some
tutorial on generics.
But you can't adequately express that in current syntax.

Of course you can. If you don't like the syntax, that's your problem,
and it's aesthetic/psychological rather than technical in nature.
Besides,

interface Foo {
<T> setBar( Iterable<T> t );
}

is just as valid,

Except it means something different. Your form allows:

Iterable<String> a = ...;
Iterable<Number> b = ...;

Foo f = new Foo();
f.setBar(a);
f.setBar(b);

The generic Foo above would look like this:

Iterable<String> a = ...;
Iterable<Number> b = ...;

Foo<String> f = new Foo<String>();
f.setBar(a); // OK
f.setBar(b); // ERROR

As a side note, you may want to have written this instead:

interface Foo<T>
{
void setBar(Iterable<? extends T> t);
}

My original code was copying yours closely to make the point, but it's
really better to use the wildcard in that situation.
(ISTR Bruce Eckel making a similar
argument when generics first came out.)

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.

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

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

Chris Smith

Dimitri Maziuk said:
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.

Then here's how you do it:

class Foo<E>

You can now use ArrayList<E> to your heart's content, and it will work
just fine.

--
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,431
Messages
2,571,679
Members
48,796
Latest member
Greg L.

Latest Threads

Top