符号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中是否有用例?确切地说,我并不是指类型参数中的存在类型或更高级的类型,只是那些构成完整类型参数列表的litte [_]
。
答案 0 :(得分: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上很好地崩溃]
答案 1 :(得分: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
,但事实并非如此 这样打印。)请注意实际上未使用类型参数
In
。那么,这是怎么回事 你可以用_
编写它:val const0 = new Function { type Out[_] = Int def apply[In](x: In) = 0 }
在这种情况下,将
_
视为类型参数的名称 实际上不能被提及。这是一个类型的功能 不关心参数的级别,就像值一样 电平:(_: Int) => 3 res4: Int => Int = <function1>
除了......
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
的实例 旧class A
。new FlyingDog[A] { ... } // error: A takes no type parameters, expected: one // (aka 'kind mismatch')
有两种解决方案:
改为声明
class A[_]
。 (不要这样做。)- 醇>
使用类型lambda:
new FlyingDog[({ type λ[α] = A })#λ]
甚至
new FlyingDog[({ type λ[_] = A })#λ]
答案 2 :(得分:2)
Scala中的下划线表示存在类型,即未知类型参数,有两个主要用法:
类型构造函数基本上需要一个类型参数来构造具体类型。例如,您可以采用以下签名。
def strangeStuff[CC[_], B, A](b:B, f: B=>A): CC[A]
这是一个函数,对于某些CC[_]
,例如List[_]
,从B和函数List[A]
开始创建B=>A
。
为什么这会有用?事实证明,如果你将这种机制与implicits和类型类一起使用,你可以通过编译器推理获得所谓的ad-hoc多态性。
想象一下,例如,您有一些更高级的类型: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可以隐含地满足所需的约束,则会抛出编译错误。
答案 3 :(得分:1)
当你处理参数化类型的实例而不关心类型参数时,这很有用。
trait Something[A] {
def stringify: String
}
class Foo extends Something[Bar] {
def stringify = "hop"
}
object App {
def useSomething(thing: Something[_]) :String = {
thing.stringify
}
}