I am trying to create a table
class that extends ArrayList
. In it, I would like to be able to create a map
method that takes a lambda expression and returns a new table
with the mapped values. I would also like to do this with filter
. I use the map and filter a lot and I don't like typing out the whole thing over and over.
public abstract class Table<E extends Element> extends ArrayList<E> {
// a lot of other stuff.
public Table<E> map(/*WHAT DO I PUT HERE?*/ mapper) {
return this.stream().map(mapper).collect(/*WHAT DO I PUT HERE?*/);
}
public Table<E> filter(/*WHAT DO I PUT HERE?*/ predicate) {
return this.stream().filter(predicate).collect(/*WHAT DO I PUT HERE?*/);
}
}
I am still trying to figure out generics. Maybe there is a better way. I don't know. I have tried duplicating what is in the original code for the ArrayList
, but everything I try seems to create new problems.
On one hand, it's entirely possible:
public abstract class Table<E extends Element> extends ArrayList<E> {
// implement in concrete subclasses
public abstract <E1 extends Element> Collector<E1, ?, Table<E1>> collector();
// Collector<E, ?, Table<E>> collector() is enough if map doesn't have E1 type parameter
public <E1 extends Element> Table<E1> map(Function<E, E1> mapper) {
return this.stream().map(mapper).collect(collector());
}
public Table<E> filter(Predicate<E> predicate) {
return this.stream().filter(predicate).collect(collector());
}
}
public class ChildTable<E extends Element> extends Table<E> {
@Override
public <E1 extends Element> Collector<E1, ?, Table<E1>> collector() {
return Collectors.toCollection(() -> new ChildTable<E1>());
// or simpler Collectors.toCollection(ChildTable::new);
}
}
collector()
could be implemented, but it would have to return a specific subtype of Table
.
On the other, it may be better to have the list as a field of Table
instead of extending it: prefer composition over inheritance. Do you really want all ArrayList
methods available?