David Forslund said:
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);
}
}