Multiple type bounds

K

kelvSYC

One thing that I've never been able to get in Java (or most
programming languages for that matter) is stuff like this:

How do you declare a parameter/variable/etc where the variable can be
any object that implements two different interfaces (or extends a
class and implements an interface)? For example, suppose Foo is a
class and Bar and Baz are interfaces. How do I create a function that
takes in an object that implements both Bar and Baz, or extends Foo
and implements Bar? To me, this is entirely reasonable OO
programming. And in Java, I've always resorted to declaring the
parameter as one thing and using reflection to see if it conforms to
the rest (and throwing an IllegalArgumentException if it does not) -
something like

void f(Foo foo) {
Bar bar;
try {
bar = Bar.class.cast(foo);
} catch (ClassCastException e) {
throw new IllegalArgumentException();
}
// ... (since foo and bar now refer to the same thing, I use
whichever is appropriate)
}

I'd thought that when it comes to generics, I can't see that a similar
problem would appear, given that you can use multiple type bounds.
However, why is it that something like

List<? extends Foo & Bar> list = new ArrayList<? extends Foo &
Bar>(); // here, Bar is an interface, Foo can be either class or
interface

or

<T extends Foo & Bar> List<T> list = new ArrayList<T>();

Is it because of weird erasure behaviour? Or is it because there is
still no way to do the first thing (the function with parameter as
described above), thus the second thing is illegal? How do I work
around it (short of code duplication)?

Also, some of you may note that I could simply subinterface/subclass
to avoid the issue, and that is actually acceptable in some
scenarios. However, suppose you have
class A extends Foo;
class B extends Foo implements Bar;
class C extends Foo implements Bar, Baz;
class D extends Foo implements Baz

Then List<? extends Foo & Bar> would mean, to me at least, a
homogeneous container (containing Bs and Cs, but not As). Still, it
would avoid a lot of code duplication (for example, f() would have to
be rewritten to take a B and a C, and possibly anything you make later
matching the conditions)...
 
P

Patricia Shanahan

kelvSYC said:
One thing that I've never been able to get in Java (or most
programming languages for that matter) is stuff like this:

How do you declare a parameter/variable/etc where the variable can be
any object that implements two different interfaces (or extends a
class and implements an interface)? For example, suppose Foo is a
class and Bar and Baz are interfaces. How do I create a function that
takes in an object that implements both Bar and Baz, or extends Foo
and implements Bar? To me, this is entirely reasonable OO
programming. And in Java, I've always resorted to declaring the
parameter as one thing and using reflection to see if it conforms to
the rest (and throwing an IllegalArgumentException if it does not) -
something like

void f(Foo foo) {
Bar bar;
try {
bar = Bar.class.cast(foo);

Why not "bar = (Bar)foo;" or an instanceof test?

Patricia
 
S

Stefan Ram

kelvSYC said:
How do you declare a parameter/variable/etc where the variable can be
any object that implements two different interfaces

For example:

<T extends List<String> & RandomAccess> void foo(T list) { ... }
 
K

kelvSYC

Why not "bar = (Bar)foo;" or an instanceof test?

Patricia

I've been largely taught to avoid explicit casting or instanceof
because they're evil. But those are equally valid in the above, I
guess.
 
K

kelvSYC

For example:

<T extends List<String> & RandomAccess> void foo(T list) { ... }

Well, that shows a function parameter (and I like the solution because
it fits in with what I believe is OO), but how about fields or local
variables? If I use something like

private <T extends List<String> & RandomAccess> T
randomAccessStringList;

I get a syntax error (ReferenceType expected instead), even though
this seems to make sense. Why is that? Is there a way around this?
 
S

Stefan Ram

kelvSYC said:
private <T extends List<String> & RandomAccess> T
randomAccessStringList;

class Example
< T extends java.util.List< java.lang.String >& java.util.RandomAccess >
{ private T randomAccessStringList; }
 
R

Roedy Green

How do I create a function that
takes in an object that implements both Bar and Baz,

1. You create a new class, possibly abstract, that implements both Bar
and Baz.

2. You use generics where you can apply multiple restrictions.

3. You declare one class for automatic checking then use instanceof to
check if the other were indeed supplied.
 
A

Andreas Leitgeb

kelvSYC said:
I've been largely taught to avoid explicit casting or instanceof
because they're evil.

While I don't even see, how "instanceof" could be considered
generally evil, I see even less so, how .class.cast(...)
could be even a tiny bit less evil.

It's like: they told me not to rob people, so I kill them
first, because they didn't tell me not to rob corpses.
- except that I don't find instanceof to be any evil
in contrast to robbing :)
 
K

kelvSYC

class Example
< T extends java.util.List< java.lang.String >& java.util.RandomAccess >
{ private T randomAccessStringList; }

Wouldn't that be genericizing the type?

Suppose you add a method

public setRandomAccessStringList(T list) { randomAccessStringList =
list; }

Then we have it that Example<ArrayList<String>> would accept setting
ArrayList<String> but not Vector<String>, while
Example<Vector<String>> would accept setting Vector<String> but not
ArrayList<String> - yet the original intention was for something that
could take both (and anything else that is both RandomAccess and
List<String>). It seems that there is still no way (short of raw
types) to have a T so that Example<T> takes ArrayList<String>,
Vector<String>, or MyCustomRandomAccessStringList as arguments.

So now working backwards: suppose we have

public <T extends List<String> & RandomAccess>
setRandomAccessSringList(T list) { randomAccessStringList = list; }

as a method in Example. How would you declare randomAccessStringList?
 
D

Daniel Pitts

kelvSYC said:
One thing that I've never been able to get in Java (or most
programming languages for that matter) is stuff like this:

How do you declare a parameter/variable/etc where the variable can be
any object that implements two different interfaces (or extends a
class and implements an interface)? For example, suppose Foo is a
class and Bar and Baz are interfaces. How do I create a function that
takes in an object that implements both Bar and Baz, or extends Foo
and implements Bar? To me, this is entirely reasonable OO
programming. And in Java, I've always resorted to declaring the
parameter as one thing and using reflection to see if it conforms to
the rest (and throwing an IllegalArgumentException if it does not) -
something like

void f(Foo foo) {
Bar bar;
try {
bar = Bar.class.cast(foo);
} catch (ClassCastException e) {
throw new IllegalArgumentException();
}
// ... (since foo and bar now refer to the same thing, I use
whichever is appropriate)
}

I'd thought that when it comes to generics, I can't see that a similar
problem would appear, given that you can use multiple type bounds.
However, why is it that something like

List<? extends Foo & Bar> list = new ArrayList<? extends Foo &
Bar>(); // here, Bar is an interface, Foo can be either class or
interface

or

<T extends Foo & Bar> List<T> list = new ArrayList<T>();

Is it because of weird erasure behaviour? Or is it because there is
still no way to do the first thing (the function with parameter as
described above), thus the second thing is illegal? How do I work
around it (short of code duplication)?

Also, some of you may note that I could simply subinterface/subclass
to avoid the issue, and that is actually acceptable in some
scenarios. However, suppose you have
class A extends Foo;
class B extends Foo implements Bar;
class C extends Foo implements Bar, Baz;
class D extends Foo implements Baz

Then List<? extends Foo & Bar> would mean, to me at least, a
homogeneous container (containing Bs and Cs, but not As). Still, it
would avoid a lot of code duplication (for example, f() would have to
be rewritten to take a B and a C, and possibly anything you make later
matching the conditions)...

I wrote a blog post about this a short while ago
<http://virtualinfinity.net/wordpress/java/esoteric-java-features/2007/11/23/type-intersection/>

Basically, I feel that its a feature that Sun should have added when
they added it for Generics, but it got left out for some reason.
 
L

Lew

kelvSYC said:
Wouldn't that be genericizing the type?

Suppose you add a method

public setRandomAccessStringList(T list) { randomAccessStringList =
list; }

Then we have it that Example<ArrayList<String>> would accept setting
ArrayList<String> but not Vector<String>, while
Example<Vector<String>> would accept setting Vector<String> but not
ArrayList<String> - yet the original intention was for something that
could take both (and anything else that is both RandomAccess and
List<String>). It seems that there is still no way (short of raw
types) to have a T so that Example<T> takes ArrayList<String>,
Vector<String>, or MyCustomRandomAccessStringList as arguments.

So now working backwards: suppose we have

public <T extends List<String> & RandomAccess>
setRandomAccessSringList(T list) { randomAccessStringList = list; }

as a method in Example. How would you declare randomAccessStringList?

T randomAccessStringList;
 
K

kelvSYC

T randomAccessStringList;

What I meant was that suppose Example was not generic - like so:

public class Example {
public <T extends List<String> & RandomAccess>
setRandomAccessStringList(T list) { randomAccessStringList = list; }

private ??? randomAccessStringList;
}

What would go in ???.
 
D

Daniele Futtorovic

One thing that I've never been able to get in Java (or most
programming languages for that matter) is stuff like this:

How do you declare a parameter/variable/etc where the variable can be
any object that implements two different interfaces (or extends a
class and implements an interface)? For example, suppose Foo is a
class and Bar and Baz are interfaces. How do I create a function that
takes in an object that implements both Bar and Baz, or extends Foo
and implements Bar?

Use two parameters.

What's the point in forcing the data/functionality you need to perform
your method to be contained within one object? On the contrary, allowing
distinct objects provides more flexible access and allows for later
restructuration of your calling architecture.

DF.
 
P

Piotr Kobzda

kelvSYC said:
What I meant was that suppose Example was not generic - like so:

public class Example {
public <T extends List<String> & RandomAccess>
setRandomAccessStringList(T list) { randomAccessStringList = list; }

private ??? randomAccessStringList;
}

What would go in ???.

Try the following:

public class Example {
public <T extends List<String> & RandomAccess>
void setRandomAccessStringList(T list) {
randomAccessStringListRef = new ListRef<T>(list);
}

private class ListRef<T extends List<String> & RandomAccess> {
final T value;

ListRef(T value) {
this.value = value;
}
}
private ListRef<? extends List<String>> randomAccessStringListRef;

// randomAccessStringListRef.value is both List<String> and
RandomAccess, without any explicit cast needed
}


piotr
 
P

Piotr Kobzda

Piotr Kobzda wrote:

.... and a few improvements:
private class ListRef<T extends List<String> & RandomAccess> {

Inner class is not needed here, static is enough.
final T value;

ListRef(T value) {
this.value = value;
}
}
private ListRef<? extends List<String>> randomAccessStringListRef;

The list reference holder field may be declared also with unbound
wildcard, i.e.:

private ListRef<?> randomAccessStringListRef;

Every bound of ListRef's type variable (an intersection type) is known
by the compiler, so effectively we have equivalent of the following (not
allowed by the language) declaration:

private ListRef<? extends List<String> & RandomAccess>
randomAccessStringListRef;


piotr
 
K

kelvSYC

The wrapper idea is a good one, but how does it do in other
situations? For example, you wanted something like

private <T extends List<String> & RandomAccess> Map<Class<? extends
T>, T> randomAccessStringListClassesAndInstances; // some kind of
prototype-flyweight-manager-thing

So you'd make a wrapper for the map (call its instance
randomAccessStringListClassesAndInstancesWrapper), but how would you
go about instantiating the wrapper (in particular, what type bounds
should I put down), and say do something like

randomAccessStringListClassesAndInstancesWrapper.value.put(ArrayList.class,
new ArrayList<String>()); // I realize that the ArrayList part is an
issue unto itself, but let's ignore it here
randomAccessStringListClassesAndInstancesWrapper.value.put(Vector.class,
new Vector<String>());
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,067
Latest member
HunterTere

Latest Threads

Top