如何在参数和结果之间链接lambda函数中的泛型

时间:2018-08-28 08:01:54

标签: java generics lambda java-8

我想从参数链接泛型并产生一个Function。 这是一个简单的示例:

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());

我不想进行强制转换,因为很明显我的函数返回列表中的内容。我想写这样的东西:

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);
}

有什么办法可以实现?

预先感谢

编辑: 有人要求提供更真实的代码。 我尝试在列表列表上创建迭代器,但我希望子列表仅在需要时才加载。 我想做一个通用方法,由我自己的ListIterator :: next和上一个调用。

这是更具体的代码:

    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();
        }
    }

此代码可以编译并可能起作用,但是我想在get方法(不转换为'String'和'List')中都进行转换。

3 个答案:

答案 0 :(得分:4)

在我看来,您需要定义自己的Function接口。 像这样:

interface IterFunction {
    <T> T apply(Iterator<T> t);
}

现在代码可以正常工作了

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);
}

答案 1 :(得分:3)

this answer为基础,您可以像这样解决实际的问题:

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);
    }
}

具有泛型函数方法的函数接口无法通过lambda表达式实现,因为没有用于为其声明类型变量的语法。但是它们可以通过方法引用来实现,这在这里很简单,因为大多数函数只是调用现有方法。

仅获取指向列表末尾的列表迭代器并不能表示为对现有方法的单次调用,这就是为什么我们必须在此处创建新方法atEnd的原因。请注意,这基本上是编译器在后台执行lambda表达式的操作,生成了容纳lambda主体的合成方法,并创建了对其的方法引用。但是,当手动声明该方法时,我们可以使它通用,这与lambda表达式不同。

答案 2 :(得分:0)

在该示例中,您尝试将T绑定到IntegerString,但是不能保证T是其中之一。就Java而言,TObject,因为没有绑定到任何更具体的东西,因此需要强制转换,以便您了解自己正在执行类型不安全的操作。

但是,仅使用泛型类型而不是将T与具体类型混合使用的代码可以很好地工作:

private <T> T take(ListIterator<T> itr, Function<ListIterator<T>, T> takeFunction) {
    return takeFunction.apply(itr);
}

显然,您的示例代码不是您的真实代码,因为您要遍历方法中的空列表,因此,如果您显示真实代码,我们可以查看如何重构它以使其正常工作。