Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Java Generics: limitations?

2 views
Skip to first unread message

Hung Jung Lu

unread,
Nov 21, 2003, 1:56:56 PM11/21/03
to
Hi,

I am trying out the beta release of Java generics. (Java 1.5). I have:

class Utils<T extends A> {
public void incx(T me) {
me.x += 1;
}
}

class A {
public int x;
Utils<A> utils;

void run() {
x = 1;
utils = new Utils<A>();
utils.incx(this);
System.out.println(this.x);
}

public static void main(String[] args) {
A a = new A();
a.run();
}
}

It works fine. But if T is not specified as extension of A:

class Utils<T> {
public void incx(T me) {
me.x += 1; // error: cannot find symbol
}
}

this will throw an error. This is kind of disappointing, because in
C++ the template parameter T could be anything. (The address shifts
are calculated intelligently during type-safe compilation.) In Java,
if T does not have a specific parent class, the compiler won't know
what attributes it may have.

Question:

(a) Is this limitation is only temporary in the beta release of
generics?
(b) Is there a way out of this limitation?
(c) Or are Java generics just not very generic at all?

regards,

Hung Jung

David Forslund

unread,
Nov 22, 2003, 11:47:33 PM11/22/03
to
I don't understand how the later case could work. How can it reference
the variable x in T, if such a variable doesn't exist? If it is to
work for any T then one could create a class without the variable x.
Then the error would have to occur at runtime. Is that what you are
looking for? This "limitation" seems reasonable to me because the
compiler is trying to check for errors. If you want to perform
operations on the template class, it seems reasonable that you should
have to know something about its signature to avoid a runtime error. I
basically view generics in Java as trying to move runtime errors to
compilation errors.

Dave

Hung Jung Lu

unread,
Nov 23, 2003, 2:36:06 PM11/23/03
to
David Forslund <fors...@mail.com> wrote in message news:<SXWvb.5275$3R3....@nntp-post.primus.ca>...

> I don't understand how the later case could work. How can it reference
> the variable x in T, if such a variable doesn't exist?

It works in C++. Basically the compiler, at compile time, identifies
what x means. Some people argue it's pre-compile time. Anyway, you may
or may not like how C++ works. But the word "generics" means exactly
that: you don't need to know the specifics. That is also why "generic
programming" is meaningless in weakly-typed languages like Python,
because everything there is generic!

> Then the error would have to occur at runtime.

Depends on the language. In weakly-typed languages like Python, yes,
at runtime. In type-safe, strongly-typed languages like C++, at
compile time. C++ templates are pretty much like macros. Per each
usage of the template, a whole new class (or function) binary code is
created, with the right offsets for the right variables like x. So C++
is able to detect problems at compile time.

> looking for? This "limitation" seems reasonable to me because the
> compiler is trying to check for errors.

You are correct in saying about the checking of error. But if Java
cannot go around and do something similar to what I've said, then Java
generics cannot be called generics, at all. As it turns out, Java has
its way out of this limitation by using getters/setters. This has been
discussed in detail in the Java's Adding Generics developer's forum,
now, by myself and others.

All in all, generics are a bit difficult to use in Java, especially
when they are compounded with multiple inheritance (which in Java is
through containment + delegation). When you hit upon these advanced
designs, you'll understand what I mean. It's surely not for everybody
to understand or worry about it.

> If you want to perform
> operations on the template class, it seems reasonable that you should
> have to know something about its signature to avoid a runtime error. I
> basically view generics in Java as trying to move runtime errors to
> compilation errors.

Correct.

Here is part of what I wrote in the Adding Generics forum. As others
have pointed out, the HasXY interface is not necessary, and could be
replaced by HasX & HasY in the extends statment.

--------------------
....

Utils<String> will throw an error. That's the behavior in C++. I
already said C++ has type-safe templates. To others, I already figured
out the way out in Java. See sample code below. Basically, Java does
not allow virtual data fields. Not at runtime, not in generics. C++
does not allow virtual data fields at runtime, but it allows virtual
data fields in templates. Python allows virtual data fields at any
moment. To mimic virtual data fields, Java has to use getters/setters.

My following example is not concise, but it does show the general case
on how to implement utility template classes that operate on target
classes that are bloodline-unrelated. Moreover, it shows in general
how to implement more and more utility template classes. Basically,
per each utility class, you have to define its workspace interface
(HasXY in the example) first. Then, implement the workspace interface
in the target classes. Multiple overlapping workspace interfaces are
OK. That's the essence.

regards,

Hung Jung
-----------------------------------

interface HasX {
public int getX();
public void setX(int value);
}

interface HasY {
public int getY();
public void setY(int value);
}

interface HasXY extends HasX, HasY {
}

class UtilX<T extends HasX> {
public void incX(T me) {
int x = me.getX();
x += 1;
me.setX(x);
}
}

class UtilY<T extends HasY> {
public void doubleY(T me) {
int y = me.getY();
y *= 2;
me.setY(y);
}
}

class UtilXY<T extends HasXY> {

public void addX2Y(T me) {
int x = me.getX();
int y = me.getY();
y += x;
me.setY(y);
}

}

class A implements HasX, HasY, HasXY {

public int x;
public int y;
UtilX<A> utilX;
UtilY<A> utilY;
UtilXY<A> utilXY;

public int getX() {
return x;
}

public void setX(int value) {
this.x = value;
}

public int getY() {
return y;
}

public void setY(int value) {
this.y = value;
}

void run() {
x = 1;

y = 2;
utilX = new UtilX<A>();
utilY = new UtilY<A>();
utilXY = new UtilXY<A>();
utilX.incX(this);
utilY.doubleY(this);
utilXY.addX2Y(this);
System.out.println("x=" + x);
System.out.println("y=" + y);
}

public static void main(String[] args) {
A a = new A();
a.run();

B b = new B();
b.run();
}

}

class B implements HasX, HasY, HasXY {

public int x;
public int y;
UtilX<B> utilX;
UtilY<B> utilY;
UtilXY<B> utilXY;

public int getX() {
return x;
}

public void setX(int value) {
this.x = value;
}

public int getY() {
return y;
}

public void setY(int value) {
this.y = value;
}

void run() {
x = 2;
y = 3;
utilX = new UtilX<B>();
utilY = new UtilY<B>();
utilXY = new UtilXY<B>();
utilX.incX(this);
utilY.doubleY(this);
utilXY.addX2Y(this);
System.out.println("x=" + x);
System.out.println("y=" + y);
}

}

John C. Bollinger

unread,
Nov 24, 2003, 9:43:33 AM11/24/03
to
Hung Jung Lu wrote:

> David Forslund <fors...@mail.com> wrote in message news:<SXWvb.5275$3R3....@nntp-post.primus.ca>...
>
>>I don't understand how the later case could work. How can it reference
>>the variable x in T, if such a variable doesn't exist?
>
>
> It works in C++. Basically the compiler, at compile time, identifies
> what x means. Some people argue it's pre-compile time.

Apparently, then, it only works as long as T is not structurally
modified between compilation of Utils and runtime. And only if both are
compiled by the same compiler on the same OS and class of hardware. And
if it fails because one of those restrictions does not hold, then you
get erroneous results without much clue as to why.

> Anyway, you may
> or may not like how C++ works.

Apparently I don't. But I already knew that.

> But the word "generics" means exactly
> that: you don't need to know the specifics.

And Java's new generics provide that. You don't need to know the
specifics. But you do need to know the broad outlines. That's the case
in C++, too, but C++ doesn't enforce it as formally. In other words,
what if class T in your example didn't have a field "x"? Even C++
generics place requirements on the classes of objects they are applied
to; they don't, however, advertise exactly what those requirements are.
Java's implementation of generics makes it explicit, and in doing so
provides better binary compatibility, better documentation,
cross-platform compatibility, etc..

[...]

>>looking for? This "limitation" seems reasonable to me because the
>>compiler is trying to check for errors.
>
>
> You are correct in saying about the checking of error. But if Java
> cannot go around and do something similar to what I've said, then Java
> generics cannot be called generics, at all. As it turns out, Java has
> its way out of this limitation by using getters/setters. This has been
> discussed in detail in the Java's Adding Generics developer's forum,
> now, by myself and others.

I think you have a somewhat odd view of generics, apparently based on
your C++ experience. Java generics are not as different as you claim.
As I discussed above, few "generic" procedures are in fact so generic
that they can work with any object at all. For those that are you can
always base them on type Object. For the rest, Java allows (requires,
in fact) that the requirements be explicitly expressed and that
compliance with them be explicitly expressed. I am fully satisfied with
that approach, and indeed, it seems better attuned to the Java way of
doing things in general.

> All in all, generics are a bit difficult to use in Java, especially
> when they are compounded with multiple inheritance (which in Java is
> through containment + delegation). When you hit upon these advanced
> designs, you'll understand what I mean. It's surely not for everybody
> to understand or worry about it.

Oh, forgive me. I didn't realize that I was carrying on a discussion
with an _advanced_ programmer. What I thought were blind faith in the
C++ language design, rigidity, and lack of clear sight must instead be
superior intelligence, vast experience, and sublime transcedental
comprehension of all aspects of software design and development. 8^|

>>If you want to perform
>>operations on the template class, it seems reasonable that you should
>>have to know something about its signature to avoid a runtime error. I
>>basically view generics in Java as trying to move runtime errors to
>>compilation errors.
>
>
> Correct.
>
> Here is part of what I wrote in the Adding Generics forum. As others
> have pointed out, the HasXY interface is not necessary, and could be
> replaced by HasX & HasY in the extends statment.

[elided]

And am I supposed to be impressed? Surprised? Annoyed? It's exactly
the approach I would have assumed would be appropriate. As I wrote,
Java requires that the underlying contracts be explicit. I see that as
an advantage.

But wait, I'm just too ignorant to know what's best for me. 8^|


John Bollinger
jobo...@indiana.edu


Tor Iver Wilhelmsen

unread,
Nov 24, 2003, 12:13:57 PM11/24/03
to
hungj...@yahoo.com (Hung Jung Lu) writes:

> It works in C++.

It works in C++ because a C++ template isn't really "code" until it's
used. They have a closer realtion to the preprocessor than to the
compiler proper.

Java templates _are_ code/real classes.

0 new messages