Covariance of Java primitives

A

Adam Warner

Hi all,

Since the JVM omits a critical 64-bit abstraction (long array indices)
I've created some custom int indexed and long indexed sequence classes.
The long indexed classes have to emulate contiguous blocks of memory (with
all the overhead that this entails). Note that Java's so-called "New IO"
is also an int constrained travesty.

To top it all off there appears to be no clean way to group the classes.
The int indexed classes return int indices. The long indexed classes
return long indices. Consider:

abstract class Sequence {
abstract long length();
}

class Sequence32 extends Sequence {
int length() {return Integer.MAX_VALUE;}
}

class Sequence64 extends Sequence {
long length() {return Long.MAX_VALUE;}
}

This code will not compile. To my mind the int return value should be
treated as a subtype of long and be promoted to long when necessary.

Can anyone come up with a workaround whereby I can operate upon int
indexed classes that return int indices (e.g. class Sequence32) while also
being able to operate upon them as if they're long indexed classes (e.g.
class Sequence)?

Regards,
Adam
 
A

Adam Warner

Consider:

abstract class Sequence {
abstract long length();
}

class Sequence32 extends Sequence {
int length() {return Integer.MAX_VALUE;}
}

class Sequence64 extends Sequence {
long length() {return Long.MAX_VALUE;}
}

This code will not compile. To my mind the int return value should be
treated as a subtype of long and be promoted to long when necessary.

Can anyone come up with a workaround whereby I can operate upon int
indexed classes that return int indices (e.g. class Sequence32) while also
being able to operate upon them as if they're long indexed classes (e.g.
class Sequence)?

My best workaround involves turning length into a class/static method and
writing a custom dispatcher for Sequence:

public abstract class Sequence {
public abstract Sequence32 castSequence32();
public abstract Sequence64 castSequence64();

public final static long length(Sequence seq) {
System.out.println("Sequence length");
Sequence32 seq32=seq.castSequence32();
if (seq32!=null) return Sequence32.length(seq32);
return Sequence64.length(seq.castSequence64());
}
}

public final class Sequence32 extends Sequence {
public Sequence32 castSequence32() {return this;}
public Sequence64 castSequence64() {return null;}

private int length() {return Integer.MAX_VALUE;}

public final static int length(Sequence32 seq) {
System.out.println("Sequence32 length");
return seq.length();
}
}

public final class Sequence64 extends Sequence {
public Sequence32 castSequence32() {return null;}
public Sequence64 castSequence64() {return this;}

private long length() {return Long.MAX_VALUE;}

public final static long length(Sequence64 seq) {
System.out.println("Sequence64 length");
return seq.length();
}
}

public final class SequenceTest {
public final static void main(String[] args) {
Sequence32 seq1=new Sequence32();
Sequence64 seq2=new Sequence64();
Sequence seq3=seq1;
Sequence seq4=seq2;

int len32=seq1.length(seq1);
long len64=seq2.length(seq2);
len64=seq3.length(seq3);
len64=seq4.length(seq4);
}
}

The syntactic overhead is duplication of local variable names [compare
seq1.length(seq1) with a typical object.length()].

Regards,
Adam
 
C

Chris Uppal

Adam said:
Can anyone come up with a workaround whereby I can operate upon int
indexed classes that return int indices (e.g. class Sequence32) while also
being able to operate upon them as if they're long indexed classes (e.g.
class Sequence)?

Why do you need two different APIs for your Sequence classes ? If you define
the entire API in terms of longs then the different implementations can be used
polymorphically.

If you define the API to use 32-bit version to use ints, and the 64-bit version
to use longs, then the cannot be substituted for each other -- Java's type
system will not allow it.

-- chris
 
J

jlowery05

You could rely on an Exception to catch miscasts to integer, but you're
never going to get a wholly transparent cast from a long to int:

abstract class Sequence {
Length length = new Length();

public class Length
{
long length;

public long getLong()
{
return length;
}

int getInteger() throws RuntimeException
{
if (getLong() > Integer.MAX_VALUE) throw new
RuntimeException("too long for int");
return (int)getLong();
}
}
}

final class Sequence32 extends Sequence {
}


final class Sequence64 extends Sequence {
}


final class SequenceTest {
public final static void main(String[] args) {
Sequence32 seq1=new Sequence32();
Sequence64 seq2=new Sequence64();
Sequence seq3=seq1;
Sequence seq4=seq2;

long l = seq3.length.getLong();
int i = seq4.length.getInteger();
}

}
 
A

Adam Warner

Why do you need two different APIs for your Sequence classes ? If you
define the entire API in terms of longs then the different
implementations can be used polymorphically.

To implement interfaces such as CharSequence length() must return an int.
I've decided that length() will return an int unless the sequence is too
large (then I'll throw an exception). If I implement Map then size() must
also be an int, and I'll likely throw an exception if there are too many
entries (since the API of Map.size() is idiotic).

I'll use some synonym of length/size to exclusively denote long sizes from
0 to 2^63.

Regards,
Adam
 
J

jlowery05

How about having Sequence declare getLongLength() and getIntLength(),
then have Sequence32 implement the latter and throw an exception on the
former (visa-versa for Sequence64). True, it's runtime vs compile time
errors, but I doubt you could do much at compile time if something
overran an array index.
 
A

Adam Warner

How about having Sequence declare getLongLength() and getIntLength(),
then have Sequence32 implement the latter and throw an exception on the
former (visa-versa for Sequence64). True, it's runtime vs compile time
errors, but I doubt you could do much at compile time if something
overran an array index.

Thanks for the ideas! I've decided upon `extent' to denote long-based
lengths/sizes. I've also decided to omit strict bounds checking of long
indices. This means any int-based class can cast a long index to an int
without first checking the high bits of the long are zero.

Regards,
Adam
 

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,769
Messages
2,569,577
Members
45,054
Latest member
LucyCarper

Latest Threads

Top