如何区分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)*)?

查看:310
本文介绍了如何区分def foo [A](xs:A *)和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)])

在第二个调用中,可以同时传递FooAnyFooTuple2,但是编译器会根据静态方法重载的规则选择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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆