I would like to link generic type from argument and result in a Function. Here is a simple example :
private void take(final Function<ListIterator<?>, ?> takeFunction) {
final ListIterator<String> stringItr = new ArrayList<String>().listIterator();
final ListIterator<Integer> intItr = new ArrayList<Integer>().listIterator();
final String string = (String) takeFunction.apply(stringItr);
final Integer integer = (Integer) takeFunction.apply(intItr);
}
take(itr -> itr.next());
take(itr -> itr.previous());
I don't want to do cast because it is obvious that my function returns what is in the list. I would like to write something like this :
private void take(final Function<ListIterator<T>, T> takeFunction) {
final ListIterator<String> stringItr = new ArrayList<String>().listIterator();
final ListIterator<Integer> intItr = new ArrayList<Integer>().listIterator();
final String string = takeFunction.apply(stringItr);
final Integer integer = takeFunction.apply(intItr);
}
Is there any way to achieve that ?
Thanks in advance
Edit : Some people asked a more real code. I try to create an iterator on a list of list, but I want my sub lists are loading only when needing. I would like to make a generic method to be called by my own ListIterator::next and previous.
Here is a more concrete code :
List<List<String>> lists;
ListIterator<List<String>> curListsIter;
ListIterator<String> curStringIter;
@Override
public String next() {
return get(
listIterator -> listIterator.hasNext(),
listIterator -> listIterator.next(),
list -> list.listIterator());
}
@Override
public String previous() {
return get(
listIterator -> listIterator.hasPrevious(),
listIterator -> listIterator.previous(),
list -> list.listIterator(list.size()));
}
private String get(final Function<ListIterator<?>, Boolean> has,
final Function<ListIterator<?>, ?> item,
final Function<List<String>, ListIterator<String>> iterator) {
// The current sub list has an item to get
if (has.apply(curStringIter)) {
return (String) item.apply(curStringIter);
}
// There is a list in the direction
else if (has.apply(curListsIter)) {
curStringIter = iterator.apply(
(List<String>) item.apply(curListsIter));
return get(has, item, iterator);
}
// No more item in sub list nor list
else {
throw new NoSuchElementException();
}
}
This code compiles and potentially works but I would like to do that without both casts in get method (into 'String' and 'List').
It seems to me that you need to define your own Function
interface.
Something like:
interface IterFunction {
<T> T apply(Iterator<T> t);
}
and now the code works:
private void take(final IterFunction takeFunction) {
final ListIterator<String> stringItr = new ArrayList<String>().listIterator();
final ListIterator<Integer> intItr = new ArrayList<Integer>().listIterator();
final String string = takeFunction.apply(stringItr);
final Integer integer = takeFunction.apply(intItr);
}
Building on this answer, you can solve your actual problem like this:
public class Example {
List<List<String>> lists;
ListIterator<List<String>> curListsIter;
ListIterator<String> curStringIter;
@Override
public String next() {
return get(ListIterator::hasNext, ListIterator::next, List::listIterator);
}
@Override
public String previous() {
return get(ListIterator::hasPrevious, ListIterator::previous, Example::atEnd);
}
private static <T> ListIterator<T> atEnd(List<T> list) {
return list.listIterator(list.size());
}
interface IterFunction {
<T> T apply(ListIterator<T> t);
}
interface GetIterFunction {
<T> ListIterator<T> apply(List<T> t);
}
private String get(Predicate<ListIterator<?>> has,
IterFunction item, GetIterFunction getIter) {
if(curListsIter == null) curListsIter = getIter.apply(lists);
while(curStringIter == null || !has.test(curStringIter)) {
// may throw NoSuchElementException
curStringIter = getIter.apply(item.apply(curListsIter));
}
return item.apply(curStringIter);
}
}
A functional interface with a generic function method can not get implemented via lambda expression, as there is no syntax for declaring type variables for them. But they can be implemented via method references, which is simple here, as most functions do just call an existing method.
Only acquiring a list iterator pointing at the list’s end is not expressible as a single invocation of an existing method, that’s why we have to create a new one here, the atEnd
method. Note that this is basically what the compiler does for lambda expressions under the hood, generating a synthetic method holding the lambda’s body and creating a method reference to it. But when declaring the method manually, we can make it generic, unlike lambda expressions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With