当前位置: 首页 > 知识库问答 >
问题:

Java“reduce left”签名/下限类型参数

黄彬
2023-03-14

以下签名是有效的,在Scala中是常用的:

trait Collection[A] {
    def reduceLeft [B >: A] (f: (B, A) => B): B
}

但是,由于:是Java中super的Scala等价物,所以转换此签名的第一个想法(用bifunction替换函数类型,并使用Use-Site方差注释,也就是有界通配符)是

interface Collection<A> {
    <B super A> B reduceLeft(BiFunction<? super B, ? super A, ? extends B> mapper)
}

但是哦不!编译器抱怨中的super标记,因为您不能有下限类型变量!现在,我如何在Java代码中编写这个方法,而不必穿越时间回到泛型在Java世界中还不存在的时候呢?

是的,我知道您认为我可以使用B extendsA,但这不是一回事,如我的实现所示:

public <R extends E> R reduceLeft(BiFunction<? super R, ? super E, ? extends R> mapper)
{
    if (this.isEmpty())
    {
        return null;
    }

    Iterator<E> iterator = this.iterator();
    R first = iterator.next(); // doesn't work, but would if R was a super-type of E (R super E)
    while (iterator.hasNext())
    {
        mapper.apply(first, iterator.next());
    }

    return first;
}

取而代之的是,我不得不使用这个稍微更受限制的版本:

public E reduceLeft(BiFunction<? super E, ? super E, ? extends E> mapper)
{
    if (this.isEmpty())
    {
        return null;
    }

    Iterator<E> iterator = this.iterator();
    E first = iterator.next();
    while (iterator.hasNext())
    {
        first = mapper.apply(first, iterator.next());
    }

    return first;
}

共有1个答案

罗安和
2023-03-14

Scala方法定义中的b>:a约束是必需的,因为:

  1. Scala使用声明站点方差,不可变集合在它们包含的元素类型上是协变的。
  2. 在概念上,
  3. reduceLeft需要返回a类型的值,但使用a作为返回类型意味着在协变位置使用它,这与已经声明的方差(即a必须是协变的)冲突。

解决这个差异冲突的诀窍是引入b泛型类型。

现在,正如您所提到的,Java使用了use-site差异,所以用Java编写的任何集合都是不变的。这也意味着使用a作为方法的返回类型没有问题,即处于逆变位置。因此,下面的定义就足够了--不需要b类型:

interface Collection<A> {
  A reduceLeft(BiFunction<? super A, ? super A, ? extends A> reducer);
}

但是,正如您所看到的,将a作为一个下界然后作为一个上界的净效果是,a基本上是不变的--如果不使用向下转换,就不可能从通配符界限中获益。这意味着我们可以简化签名(这与stream.reduce)非常相似):

interface Collection<A> {
  A reduceLeft(BiFunction<A, A, A> reducer);
}

此外,bifunction 类型在Java8中已经存在,名称为binaryoperator

 类似资料:
  • 你好,我有两个类:Person类和Employee类,它们扩展了Person 我想写一个函数,将添加人到下界参数化列表 但是eclipse不允许它,错误消息是:“类型列表中的方法add(capture#1-of?super Employee)不适用于参数(Person)

  • 问题内容: 我认为你不能将Java泛型类型参数绑定到下限(即使用关键字)。我正在阅读Angelika Langer泛型常见问题解答对此主题的看法。他们说,这基本上可以归结为无用的下限(“没有任何意义”)。 我不相信。我可以想象它们的用途是帮助你更灵活地调用产生类型化结果的库方法的调用者。想象一下一个方法,该方法创建了用户指定大小的数组列表,并用空字符串填充了该列表。一个简单的声明是 但这不必要地限

  • 为什么Java可以推断出多个上限类型的共同祖先,而不能推断出下限类型的共同祖先? 更具体地说,请考虑以下示例:

  • 当我定义一个类时,我如何在它的方法签名中包含必须属于同一类的参数?我正在构建一个应该这样工作的图结构,但是这里有一个简化的例子: 这将导致以下错误: 我的意思是,我可以用Python 2的方式来做,但是我希望有比这更Python-3-ic的解决方案:

  • 我有一个方法,我想在其中接受必须扩展抽象类的类类型。之间的区别是什么 ? 在第二种情况下,我不能直接引用方法内部的类型。哪些类类型可以传递给这两个方法有什么不同吗?

  • 我已经尝试了网络上提供的所有解决方案,但都无效。请尽快提供帮助。同步错误: 生成文件“C:\Users\AH\U GL\Desktop\apps android commons\apps\Build”。格拉德尔线:149 评估项目“:app”时出现问题。 没有方法的签名:build_bacf6ncg9oj63qiyhi1id1rfi.android()适用于参数类型:(build_bacf6ncg