如何区分def foo [A](xs:A *)和def foo [A,B](xs:(A,B)*)? [英] How can I differentiate between def foo[A](xs: A*) and def foo[A, B](xs: (A, B)*)?
问题描述
我知道类型擦除使它们在运行时看起来像类型一样,因此:
I know that type erasure makes them look equal, type-wise, at runtime, so that:
class Bar {
def foo[A](xs: A*) { xs.foreach(println) }
def foo[A, B](xs: (A, B)*) { xs.foreach(x => println(x._1 + " - " + x._2)) }
}
给出以下编译器错误:
<console>:7: error: double definition:
method foo:[A,B](xs: (A, B)*)Unit and
method foo:[A](xs: A*)Unit at line 6
have same type after erasure: (xs: Seq)Unit
def foo[A,B](xs: (A, B)*) { xs.foreach(x => println(x._1 + " - " + x._2)
) }
^
但是有一种简单的书写方式:
But is there a simple way to be able to write:
bar.foo(1, 2, 3)
bar.foo(1 -> 2, 3 -> 4)
并让它们调用foo的不同重载版本,而不必显式命名它们:
and having these call different overloaded versions of foo, without having to explicitly name them:
bar.fooInts(1, 2, 3)
bar.fooPairs(1 -> 2, 3 -> 4)
推荐答案
您可以以相当合理的方式进行操作. Foo
是类型类,并且编译器隐式传递该类型类的实例,该实例与(推断的)类型参数A
兼容.
You can, in a fairly round about way. Foo
is a type class, and the compiler implcitly passes an instance of the type class, compatible with the (inferred) type parameter A
.
trait Foo[X] {
def apply(xs: Seq[X]): Unit
}
object Foo {
implicit def FooAny[A]: Foo[A] = new Foo[A] {
def apply(xs: Seq[A]) = println("apply(xs: Seq[A])")
}
implicit def FooTuple2[A, B]: Foo[(A, B)] = new Foo[(A, B)] {
def apply(xs: Seq[(A, B)]) = println("apply(xs: Seq[(A, B)])")
}
def apply[A](xs: A*)(implicit f: Foo[A]) = f(xs)
}
Foo(1, 2, 3) // apply(xs: Seq[A])
Foo(1 -> 2, 2 -> 3) // apply(xs: Seq[(A, B)])
在第二个调用中,可以同时传递FooAny
和FooTuple2
,但是编译器会根据静态方法重载的规则选择FooTuple2
. FooTuple2
被认为比FooAny
更具体.如果认为两个候选者彼此一样具体,则会引起歧义错误.您也可以通过将一个放置在超类中来优先使用另一个,就像在scala.LowPriorityImplicits
中所做的那样.
In the second call, both FooAny
and FooTuple2
could be passed, but the compiler picks FooTuple2
, based on the rules of static method overloading. FooTuple2
is considered more specific that FooAny
. If two candidates are considered to be as specific as each other, an ambiguity error is raised. You can also prefer one over the other by placing one in a superclass, as is done in scala.LowPriorityImplicits
.
更新
摆脱DummyImplicit的想法,并在scala-user上跟踪线程:
Riffing off the DummyImplicit idea, and the followup thread on scala-user:
trait __[+_]
object __ {
implicit object __ extends __[Any]
}
object overload {
def foo(a: Seq[Boolean]) = 0
def foo[_: __](a: Seq[Int]) = 1
def foo[_: __ : __](a: Seq[String]) = 2
}
import overload._
foo(Seq(true))
foo(Seq(1))
foo(Seq("s"))
这将声明类型参数化的特征__
,在其未命名的类型参数_
中是协变的.它的伴随对象__
包含__[Any]
的隐式实例,稍后我们将需要它. foo
的第二个和第三个重载包括一个虚拟类型参数,再次未命名.这将被推断为Any
.此类型参数具有一个或多个上下文边界,这些上下文边界已分解为其他隐式参数,例如:
This declares a type-parameterized trait __
, covariant in its unnamed type parameter _
. Its companion object __
contains an implicit instance of __[Any]
, which we'll need later on. The second and third overloads of foo
include a dummy type parameters, again unnamed. This will be inferred as Any
. This type parameter has one or more context bounds, which are desugared into additional implicit parameters, for example:
def foo[A](a: Seq[Int])(implicit ev$1: __[A]) = 1
多个参数列表在字节码中串联为一个参数列表,因此避免了双重定义问题.
The multiple parameter lists are concatenated into a single parameter list in the bytecode, so the double definition problem is circumvented.
请认为这是学习擦除,上下文边界和隐式搜索的机会,而不是作为在实际代码中应用的模式!
Please consider this as a opportunity to learn about erasure, context bounds and implicit search, rather than as a pattern to be applied in real code!
这篇关于如何区分def foo [A](xs:A *)和def foo [A,B](xs:(A,B)*)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!