【问题标题】:Scala type inference with generics and covariance, possible issue in scalacScala 类型推断与泛型和协变,scalac 中的可能问题
【发布时间】:2012-01-24 20:06:36
【问题描述】:

我有一份我今天发现的问题的简短列表。 我是 scala 新手,因此我的问题可能是微不足道的。

假设我们有这样一个类:

abstract class A[+T] { def foo[S >: T](x: S): String }

我们应该为有用的子类提供一些功能。

1) 我的第一次尝试是这样的:

class B extends A[String] { def foo(x: String) = x }

但 scalac 编译器不同意消息:

xxx@yyy:~$ scalac covariant.scala 
covariant.scala:3: error: class B needs to be abstract, since method foo in class A of type [S >: String](x: S)String is not defined
class B extends A[String] { def foo(x: String) = x }
      ^

首先,为什么scalac没有推断出foo的泛型类型参数,它真的是一个复杂的任务吗?

2) 下一次尝试看起来更好,应该被接受,我认为:

class B extends A[String] { def foo[String](x: String) = x }

但现在编译器让我大开眼界:

covariant.scala:3: error: type mismatch;
 found   : String(in method foo)
 required: java.lang.String
class B extends A[String] { def foo[String](x: String) = x }
                                                         ^

String 和 java.lang.String 的不匹配似乎是个问题。这是第二个问题:这真的是一个错误吗?

3) 最后我决定将 String 类型切换为 Date 作为参数:

import java.util.Date
class B extends A[Date] { def foo[Date](x: Date) = x.toString }

它已被编译,没有任何警告。所以最后一个问题是:为什么?我的第二个和第三个 sn-ps 有什么区别?

顺便说一下,scalac版本是2.9.1.final

【问题讨论】:

    标签: generics scala inheritance


    【解决方案1】:

    真的,你所有的问题都源于同一个误解。

    您已将其定义为“具有类型参数和方法 foo 的类,该类可以作用于任何类型 descended from T,而 T 是 T 的后代。

    您提供了一个带有 foo 的类,它可以作用于 T 但不能 necessarily 作用于 descendants(实际上,由于参数是协变的, foo 实际上可以作用于后代,但规范不是足够紧以抓住它) T 的超类。

    在其他两个问题中,您不小心重用了现有类型的名称。当您编写 foo[Date] 或 foo[String] 时,您显然不是指的,正如您所想的那样,java.lang.String 或 java.lang.Date 而是同名的新类型!这就是为什么您会看到 String 和 java.lang.String 不匹配的原因——它们是两种不同的类型。

    试试这个:

    abstract class A[+T] { def foo[S >: T](x: S): String }
    class B extends A[String] { def foo[S >: String](x: S) = x.toString }
    

    是的,S >: String 有点无意义,因为 String 是最终的,但规范无法排除所有可能的边缘情况。

    注意我也是个暴君,所以如果我的回答是错误的/误导性的,请不要感到震惊。请记住:这条建议的价值是您支付的两倍,或者您的钱会欣然退还。

    告诉你大错误:我颠倒了超类和子类。使用 strikeouts 进行的更正。

    一个例子 OP 不清楚为什么所有这些都是必要的。考虑以下函数

    def foo3(a : A[String]) = a.foo(3)
    

    这是合法的,因为 3(如 java.lang.Integer)是 Object 的一个示例,它是 A 的祖先。如果 B.foo 被定义为采用 String 而没有其他内容,则 B 的实例不能传入 foo3。

    【讨论】:

    • 相当不错,除了S >: T 意味着S 必须是Tsuper 类型,而不是后代。因此,例如,这个调用是有效的:new B.foo(new java.util.Date),因为S 被推断为AnyRefString(和java.util.Date)的超类型。
    • 天啊!这更有意义。所以这不是规范中的差距——或者更确切地说是规范中的两个差距,一个以 T 的超类型作为参数的函数和一个以 T 为参数的函数之间存在巨大差异,当然,String 是最终的不会改变任何东西。
    • 我对函数 foo 的参数中的引用类型一无所知。我可以将具体参数传递给 foo 的声明吗?您的代码 def foo[S >: String](x: S) = x.toString 不允许将 x 解释为字符串。
    猜你喜欢
    • 1970-01-01
    • 2012-11-29
    • 2013-11-29
    • 1970-01-01
    • 1970-01-01
    • 2022-08-18
    • 2016-07-27
    • 2023-03-08
    • 1970-01-01
    相关资源
    最近更新 更多