7

符号的类型class A[_]def a[_](x: Any)具有无法在正文中引用的类型参数,因此我看不出它在哪里有用以及它编译的原因。如果尝试引用此类型参数,则会引发错误:

scala> class A[_] { type X = _ }
<console>:1: error: unbound wildcard type
       class A[_] { type X = _ }
                             ^

scala> def a[_](x: Any) { type X = _ }
<console>:1: error: unbound wildcard type
       def a[_](x: Any) { type X = _ }
                                   ^

有人能告诉我这种类型在 Scala 中是否有用例吗?确切地说,我不是指类型参数中的存在类型或更高种类的类型,仅指[_]构成完整类型参数列表的那些小类型。

4

4 回答 4

4

我对这里的含义有一些随意的想法:

https://issues.scala-lang.org/browse/SI-5606

除了琐碎的用例,要求编译器编一个名字,因为我真的不在乎(虽然也许我稍后会在实现类时命名它),这个仍然让我觉得很有用:

另一个用例是不推荐使用类型参数,因为类型推断的改进使其变得多余。

trait T[@deprecated("I'm free","2.11") _, B <: S[_]] 

然后,假设,人们可以警告使用T[X, Y]但不是T[_, Y]

尽管注释是在(值参数样式)之前还是之后(类型样式的注释)并不明显。

[编辑:“为什么它编译”:case class Foo[_](i: Int)在 2.9.2 上仍然很好地崩溃]

于 2012-10-09T20:48:18.437 回答
4

因为我没有得到我期望的答案,所以我把它带到了scala-language

我在这里粘贴来自 Lars Hupel 的答案(因此,所有学分都适用于他),这主要解释了我想知道的内容:

我要在这里试一试。我认为在谈论类型成员时,该功能的使用会变得很清楚。

假设您必须实现以下特征:

trait Function {
  type Out[In]
  def apply[In](x: In): Out[In]
}

这将是一个(通用)函数,其中返回类型取决于输入类型。一个例子:

val someify = new Function {
  type Out[In] = Option[In]   def
  apply[In](x: In) = Some(x)
}

someify(3) res0: Some[Int] = Some(3)

到现在为止还挺好。现在,你将如何定义一个常量函数?

val const0 = new Function {
  type Out[In] = Int
  def apply[In](x: In) = 0
}

const0(3) res1: const0.Out[Int] = 0

(类型const0.Out[Int]等价于Int,但不是那样打印的。)

注意 type 参数In实际上是如何不被使用的。因此,您可以使用以下方式编写它_

val const0 = new Function {
  type Out[_] = Int
  def apply[In](x: In) = 0
}

在这种情况下_,可以将其视为实际上无法引用的类型参数的名称。它是类型级别的函数,它不关心参数,就像值级别一样:

(_: Int) => 3 res4: Int => Int = <function1>

除了……</p>

type Foo[_, _] = Int
<console>:7: error: _ is already defined as type _
       type Foo[_, _] = Int

将其与以下内容进行比较:

(_: Int, _: String) => 3 res6: (Int, String) => Int = <function2>

所以,总而言之:

type F[_] = ConstType // when you have to implement a type member def
foo[_](...) // when you have to implement a generic method but don't
            // actually refer to the type parameter (occurs very rarely)

您提到的主要内容class A[_]是完全对称的,只是没有真正的用例。

考虑一下:

trait FlyingDog[F[_]] { def swoosh[A, B](f: A => B, a: F[A]): F[B] }

现在假设你想FlyingDog为你的普通 old创建一个实例class A

new FlyingDog[A] { ... }
// error: A takes no type parameters, expected: one
// (aka 'kind mismatch')

有两种解决方案:

  1. class A[_]改为声明。(不要那样做。)

  2. 使用类型 lambda:

    new FlyingDog[({ type λ[α] = A })#λ]
    

甚至

new FlyingDog[({ type λ[_] = A })#λ]
于 2012-10-10T10:31:40.157 回答
2

Scala中的下划线表示存在类型,即未知类型参数,主要有两种用法:

  • 它用于不关心类型参数的方法
  • 它用于您想要表示一个类型参数是类型构造函数的方法。

类型构造函数基本上是需要类型参数来构造具体类型的东西。例如,您可以采用以下签名。

def strangeStuff[CC[_], B, A](b:B, f: B=>A): CC[A] 

这是一个函数,对于某些人CC[_]来说,例如 a ,从 B 和一个函数开始List[_]创建一个。List[A]B=>A

为什么会有用?事实证明,如果你将这种机制与隐式和类型类一起使用,由于编译器的推理,你可以获得所谓的临时多态性。

想象一下,例如,您有一些更高种类的类型:Container[_]具有具体实现的层次结构:BeautifulContainer[_], BigContainer[_], SmallContainer[_]. 要构建一个容器,您需要一个

trait ContainerBuilder[A[_]<:Container[_],B] { 

def build(b:B):A[B]

}

所以基本上 ContainerBuilder 是针对特定类型的容器 A[_] 可以使用 B 构建 A[B] 的东西。

虽然那会有用吗?好吧,您可以想象您可能在其他地方定义了一个函数,如下所示:

def myMethod(b:B)(implicit containerBuilder:ContainerBuilder[A[_],B]):A[B] = containerBuilder.build(b)

然后在你的代码中你可能会这样做:

val b = new B()
val bigContainer:BigContainer[B] = myMethod(b)
val beautifulContainer:BeautifulContainer[B] = myMethod(b)

实际上,编译器将使用 myMethod 的所需返回类型来查找满足所需类型约束的隐式,如果没有满足所需约束的 ContainerBuilder 隐式可用,则会抛出编译错误。

于 2012-10-08T11:54:18.467 回答
1

当您处理参数化类型的实例而不关心类型参数时,这很有用。

trait Something[A] {
  def stringify: String
}

class Foo extends Something[Bar] {
  def stringify = "hop"
}

object App {
  def useSomething(thing: Something[_]) :String = {
    thing.stringify
  }
}
于 2012-10-08T11:32:05.657 回答