Another generics limitation

D

Daniel Dyer

When using generics with multiple bounds, like this:

private <T extends List & Serializable> void method(T t)
{
// Do something.
}

It seems that this method is useless to me without knowing, in advance,
the concrete type of T. What I really want is to be able to cast to a
type intersection. For example:

method((List & Serializable) myObject)

But, of course, this isn't legal. It appears that the only option is to
write a wrapper that implements both interfaces and use that.

Unless there's something I've missed... (I'm not actually interested in
serializable lists, it's just the simplest example)

Dan.
 
M

Mike Schilling

Daniel Dyer said:
When using generics with multiple bounds, like this:

private <T extends List & Serializable> void method(T t)
{
// Do something.
}

It seems that this method is useless to me without knowing, in advance,
the concrete type of T. What I really want is to be able to cast to a
type intersection. For example:

method((List & Serializable) myObject)

The actual object referenced by myObject must be of a type that implements
both List and Serializable for this to work. So I presume you're discussing
the case where the reference is not, but is some wider type (say, Object.)
And you're right; there's no way I know of to cast an Object to an
implementor of two interfaces. Logically, (List)(Serializable)myObject
should do it, but the compiler doesn't treat it that way.

On the other hand, in the world of generics, it's less likely than it used
to be that you'll need to cast an Object that way.
 
G

Guest

Mike said:
The actual object referenced by myObject must be of a type that implements
both List and Serializable for this to work. So I presume you're discussing
the case where the reference is not, but is some wider type (say, Object.)

That seems impossible to me. The 'T extends List' says that the type
must be a List or a subclass of List. Object is a superclass of List.
The method above can treat T as a List and as implementing the
Serializable interface. If that is not concrete enough then the method
signature is wrong.
 
D

Daniel Dyer

The actual object referenced by myObject must be of a type that
implements
both List and Serializable for this to work. So I presume you're
discussing
the case where the reference is not, but is some wider type (say,
Object.)

Yes, that's exactly the situation.
And you're right; there's no way I know of to cast an Object to an
implementor of two interfaces. Logically, (List)(Serializable)myObject
should do it, but the compiler doesn't treat it that way.

That's pretty much as I expected. Even reflection is problematic because
to look-up the method you'd need to pass a Class object that matches the
bounds and I can't see any way to create/obtain such a Class object.

The best that I can do, as far as I can see, is to get the entire list of
methods from the class and loop through it until finding one with a
matching name. It would be interesting to see what Class object would be
returned by the getParameterTypes() method of that Method object (I will
try this later).
On the other hand, in the world of generics, it's less likely than it
used
to be that you'll need to cast an Object that way.

The actual problem that I am trying to solve is that I want to implement
the java.util.concurrent.ExecutorService interface but with the added
restriction that all of the Callables and Runnables submitted must be
Serializable (I am attempting to write a DistributedExecutorService that
sends tasks to worker nodes using RMI). Because I can't modify the
interface I was first checking, with instanceof, that the objects are
Serializable (and throwing an IllegalArgumentException if not). Then I
delegate to a remote method with the Callable & Serializable parameter
type. I guess I can just change the signature of this method and drop the
Serializable restriction since I am already checking the type before
invoking it.

Thanks,

Dan.
 
D

Daniel Dyer

That seems impossible to me. The 'T extends List' says that the type
must be a List or a subclass of List. Object is a superclass of List.The
method above can treat T as a List and as implementing the Serializable
interface. If that is not concrete enough then the method signature is
wrong.

The problem is not in the method, it's in the code calling the method. If
you have a reference of type Object, you cannot pass it to the above
method without first casting it to a concrete type that implements both
interfaces. If I know the object is a List and I know it is Serializable
this is not enough to be able to invoke the method. If, on the other
hand, I know that it is actually an ArrayList, I can cast it as such and
invoke the method without any problems. Unfortunately, at compile time I
might not know if I will be dealing with ArrayLists or LinkedLists. Both
are lists, both are Serializable but one can't be cast as the other and
there is no common super-type that they can be cast to that is both a List
and Serializable.

Dan.
 
T

Thomas Hawtin

Daniel said:
When using generics with multiple bounds, like this:

private <T extends List & Serializable> void method(T t)
{
// Do something.
}

It seems that this method is useless to me without knowing, in advance,
the concrete type of T. What I really want is to be able to cast to a
type intersection. For example:

method((List & Serializable) myObject)

But, of course, this isn't legal. It appears that the only option is to
write a wrapper that implements both interfaces and use that.

It is possible to perform such a cast, but I suggest you don't. I think
your design will need a bit of a rethink. In this case, Serializable is
a marker interface and isn't usually used as a type of a variable.

Tom Hawtin
 
D

Daniel Dyer

It is possible to perform such a cast, but I suggest you don't. I think
your design will need a bit of a rethink. In this case, Serializable is
a marker interface and isn't usually used as a type of a variable.

That's fine, the design will most probably be different. The idea behind
adding the Serializable bound was to make it clear in the method
declaration that only Serializable objects were valid in this context (the
actual full method signature that I came up with was a monstrosity of
nested generics). The reason for posting was to see if anybody had any
ideas about how you would achieve this if it were required. You say you
can do this kind of cast, what would it look like?

Dan.
 
M

Mike Schilling

Daniel Dyer said:
On Tue, 24 Oct 2006 06:10:40 +0100, Mike Schilling


That's pretty much as I expected. Even reflection is problematic because
to look-up the method you'd need to pass a Class object that matches the
bounds and I can't see any way to create/obtain such a Class object.

Well, not quite. By erasure, the method is simply of type

void method(java.util.List);

All the parameter types except the first one get erased. So you can find it
via reflection; just be sure you recall which interface was mentioned first
:)
 
D

Daniel Dyer

That's fine, the design will most probably be different. The idea
behind adding the Serializable bound was to make it clear in the method
declaration that only Serializable objects were valid in this context
(the actual full method signature that I came up with was a monstrosity
of nested generics). The reason for posting was to see if anybody had
any ideas about how you would achieve this if it were required. You say
you can do this kind of cast, what would it look like?

I just checked the JLS:

"It is not possible to write an intersection type directly as part of a
program; no syntax supports this."

http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.9

Dan.
 
D

Daniel Dyer

Well, not quite. By erasure, the method is simply of type

void method(java.util.List);

All the parameter types except the first one get erased. So you can
find it
via reflection; just be sure you recall which interface was mentioned
first
:)

Good point, that hadn't occurred to me. I'll probably alter the design,
as Tom suggests, but it's good to have an answer.

Thanks,

Dan.
 
P

Piotr Kobzda

Daniel said:
When using generics with multiple bounds, like this:

private <T extends List & Serializable> void method(T t)
{
// Do something.
}

It seems that this method is useless to me without knowing, in advance,
the concrete type of T. What I really want is to be able to cast to a
type intersection. For example:

method((List & Serializable) myObject)

But, of course, this isn't legal. It appears that the only option is to
write a wrapper that implements both interfaces and use that.

Ignoring type safety you can cast to intersection type being the method
type variable, this way:

@SuppressWarnings("unchecked")
private <T extends List & Serializable> void methodFor(List list)
{
method((T)list);
}

And than make a use of type erasure to invoke the method without
specifying concrete type arguments, like this:

private void methodForSerializableList(Object obj)
{
abstract class AnySuitableType implements List, Serializable {};
this.<AnySuitableType>methodFor((List)(Serializable)obj);
}


This is very awful solution and I can not recommend it. But as well as
Java Generics is implemented using the type erasure it should work as
expected. That is it should work fine for:

methodForSerializableList(new ArrayList());

and throw ClassCastException for:

methodForSerializableList(new Object());
methodForSerializableList(new Serializable() {});
...


piotr
 

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,774
Messages
2,569,596
Members
45,144
Latest member
KetoBaseReviews
Top