Reputation: 2490
I have things (say, for context, numbers) that can perform operations on their own type:
interface Number<N> {
N add(N to);
}
class Int implements Number<Int> {
Int add(Int to) {...}
}
and actors that act on all subtypes of a certain upper bound:
interface Actor<U> {
<E extends U> E act(Iterable<? extends E> items);
}
I want to make an actor that acts polymorphically on any numerical type:
class Sum implements Actor<Number> {
<N extends Number<N>> N act(Iterable<? extends N> items) {...}
}
Now, clearly this doesn't work because Number
and Number<N>
are not the same. In fact, since Number
doesn't constrain an implementor's type parameter to be its own type, such an actor could not possibly work. But I don't care to operate on Number
s in general - I am satisfied for my functionality to work only on Numbers of some type N extends Number<N>
As an alternative, I could declare:
interface Actor<E> {
E act(Iterable<? extends E> items);
}
class Sum<N extends Number<N>> implements Actor<N> {
N act(Iterable<? extends N> items) {...}
}
But this doesn't work for me because it forces me to know N
when I construct my Sum
, which my use case doesn't conveniently allow. It also forces an ugly <N extends Number<N>>
on every class or method that polymorphically uses a Sum
, causing a proliferation of type clutter.
Is there any elegant way to do what I want?
Here is some sample code expressing what I would like to do.
interface Folder<U> {
<E extends U> E fold(Iterable<? extends E> items);
}
class Sum implements Folder<Number> {
<N extends Number<N>> N fold(Iterable<? extends N> items) {
Iterator<? extends N> iter = items.iterator();
N item = iter.next();
while (iter.hasNext())
item = item.add(iter.next());
return item;
}
}
class Concat implements Folder<String> {
<S extends String> fold(Iterable<? extends S> items) {
StringBuilder concatenation = new StringBuilder();
for (S item : items)
concatenation.append(item);
return concatenation.toString();
}
}
class FoldUtils {
static <U, E extends U> E foldDeep(Folder<U> folder, Iterable<? extends Iterable<? extends E>> itemses) {
Collection<E> partialResults = new ArrayList<E>();
for (Iterable<? extends E> items : itemses)
partialResults.add(folder.fold(items));
return folder.fold(partialResults);
}
}
Upvotes: 10
Views: 137
Reputation: 81134
Looking at your example, I'm not sure what you gain from having the generic method provide the concrete parameter vs having it in the actor:
class Sum<T extends Number<T>> implements Actor<T> {
T act(Iterable<? extends T> items) {...}
}
What's the virtue of having a Sum<any-self-referential-Number>
vs just having a Sum<Int>
, and a Sum<Float>
, etc?
If you're worried about the tiny memory overhead of creating different instances, you could just return the same instance each time with an unchecked cast, as is common where safe (see e.g. Guava's Optional.absent()
or Collections.emptyList()
).
In your example, somebody's eventually going to have to essentially do:
List<List<Int>> list;
foldDeep(new Sum(), list)
So why not just require the type parameter there?
foldDeep(new Sum<Int>(), list)
Or if encapsulated behind a factory,
foldDeep(Sum.instance(), list)
foldDeep(NumberFolders.sum(), list)
In short, it's not clear to me why this wouldn't work just as well:
interface Folder<U> {
U fold(Iterable<? extends U> items);
}
class Sum<T extends Number<T>> implements Folder<T> {
public T fold(Iterable<? extends T> items) {
//...
}
}
class FoldUtils {
static <E> E foldDeep(Folder<E> folder, Iterable<? extends Iterable<? extends E>> itemses) {
Collection<E> partialResults = new ArrayList<>();
for (Iterable<? extends E> items : itemses)
partialResults.add(folder.fold(items));
return folder.fold(partialResults);
}
}
//...
FoldUtils.foldDeep(new Sum<>(), list);
Upvotes: 1