Peculiar generics problem with static factory method (solved, butinteresting and maybe instructional

J

John Ersatznom

Here's the code snippets.

@SuppressWarnings("unchecked")
public static <S extends Scalar<S>, D extends Dimension, T extends
Point<S, D, T>> Point<S, D, T> getPoint (S component, D dim) {
if (dim.dimensions == 0) return (Point<S, Zero,
ZeroDimensionalPoint<S>>)ZeroDimensionalPoint.getInstance(component.getScalarClass(),
component.isUndefined());


Compiler claims that it cannot convert Point<S, Zero,
ZeroDimensionalPoint<S>> to Point<S, D, T>.

It should warn that the cast requires unchecked conversion instead.

Changing the cast to (Point<S, D,
T>)ZeroDimensionalPoint.getInstance(foo) causes "Cannot cast from
ZeroDimensionalPoint<S> to Point<S, D, T>" instead of a warning.

Changing it simply to "(Point)" warns of an unnecessary cast. Omitting
the cast however just gives the first error noted above!

The @SuppressWarnings("unchecked") annotation doesn't prevent the
"unnecessary cast" warning.

Of course, if the method is called with a dim whose "dimensions == 0" it
is in fact the case that a ZeroDimensionalPoint<S> will be run-time
compatible with any Point<S, D, T> where S matches up and D is a
dimension class that defines its dimensions to be zero, with T being
ZeroDimensionalPoint<S>...

AFAICT the problem is that D might not actually be Zero, but the base
class Dimension or a different Dimension subclass that happens to have
an instance with a dimension of zero. Since the dimension objects are
just used for run-time dimension-compatibility checking and their
classes, as type parameters, to allow users of my math classes to have
compile-time checking for vector math with compile-time-known numbers of
dimensions, this is annoying. There aren't even ever any run-time uses
of dimension object instances except to extract "dimensions", call
toString, and the like.

Anyone know how to make the compiler ignore the dubious casts?

Huh -- that's odd. I just solved it myself when I tried

Point temp = ZeroDimensionalPoint.getInstance(foo);
return temp;

I then get "unchecked" warning on "return temp;" which @SuppressWarnings
makes go away.
 
J

John Ersatznom

John said:
Here's the code snippets.

@SuppressWarnings("unchecked")
public static <S extends Scalar<S>, D extends Dimension, T extends
Point<S, D, T>> Point<S, D, T> getPoint (S component, D dim) {

I think I'm going to be dumping the use of marker classes to do static
dimension match checking. Between the proliferation of dubious casts and
@SuppressWarnings usage, as well as screwy temporaries, it is making the
code less readable and maintainable (and maybe even correct?) instead of
more. The last straw was the matrix multiplication method whose
signature is longer than its body:

public <U extends Dimension, V extends Dimension, W extends Dimension, X
extends Matrix<S, E, U, V, X>, Y extends Matrix<S, D, U, W, Y>> Y
matrixTimes (X that) {

(in Matrix<S extends Scalar<S>, D extends Dimension, E extends
Dimension, F extends Dimension, T extends Matrix<S, D, E, F, T> which
extends Point<S, F, T>)

C++ lets you use int as a template parameter type, where I could have had

template <class S, int D, int E> class matrix : point<S, D*E> { ...

template <int U, int V> matrix<S, D, U> times (matrix<S, E, U> that) {

OTOH, C++ doesn't have bounding of type parameters, which doesn't let me
force S to extend Scalar or use the derived subclass parameter and
expect the same implementation class everywhere.

So I'm going to end up with just Point<S extends Scalar<S>, T extends
Point<S, T>>, Matrix<S extends Scalar<S>, T extends Matrix<S, T>>, etc.
and perhaps derive specialized classes such as Point2D, Point3D,
Matrix2X3, and so on. The latter can even just use the any-dimensional
implementation to do everything while defining constructors that only
generate appropriate-dimensioned objects. It'll have to be by
containment rather than extension, due to the covariant argument types
on some of the methods, of course, and to avoid having to cast to the
covariant return type even on the rest. The inner instance of the
generalized can be invoked and a new instance of the self type
constructed to wrap it and returned.
 
C

Chris Uppal

John said:
I think I'm going to be dumping the use of marker classes to do static
dimension match checking. Between the proliferation of dubious casts and
@SuppressWarnings usage, as well as screwy temporaries, it is making the
code less readable and maintainable (and maybe even correct?) instead of
more.

Seems like a good idea to me.

I had been intending to post something to the effect that the generified
declaration was effectively unreadable (even with improved layout), and
probably incomprehensible once decoded. Sort of a type-system-level analogue
of code like:

i = ++i;

(and similar abominations) in that the compiler would understand it (modulo
bugs), but few programmers would, and fewer still would want to have to.

-- chris
 
J

John Ersatznom

Chris said:
John Ersatznom wrote:




Seems like a good idea to me.

I had been intending to post something to the effect that the generified
declaration was effectively unreadable (even with improved layout), and
probably incomprehensible once decoded. Sort of a type-system-level analogue
of code like:

i = ++i;

(and similar abominations) in that the compiler would understand it (modulo
bugs), but few programmers would, and fewer still would want to have to.

Yeah. The fly in the ointment is that instead of just writing

public class Four extends Dimension {
public static final FOUR = new Four();
private Four () { super(4); }
private Object readResolve() { return FOUR; }
}

and getting compile-time-typesafe four-D vectors, 4xX and Xx4 matrices,
and so forth for free, they end up having to write a Vector4D class,
matrix classes for every Xx4 and 4xX combination ... to get the same
level of typesafety. At least I can have AbstractFixedSizeVector<S
extends Scalar<S>, D extends AbstractFixedSizeVector<S, D>> for them to
extend and only have to write the constructors, and the like.
 

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

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top