Scala双重定义(2种方法具有相同类型的擦除)

Jér*_*ôme 67 scala overloading compilation typeclass type-erasure

我在scala中写了这个,它不会编译:

class TestDoubleDef{
  def foo(p:List[String]) = {}
  def foo(p:List[Int]) = {}
}
Run Code Online (Sandbox Code Playgroud)

编译通知:

[error] double definition:
[error] method foo:(List[String])Unit and
[error] method foo:(List[Int])Unit at line 120
[error] have same type after erasure: (List)Unit
Run Code Online (Sandbox Code Playgroud)

我知道JVM没有对泛型的原生支持,所以我理解这个错误.

我可以写包装List[String],List[Int]但我很懒:)

我很怀疑,但是,有没有另一种方式表达List[String]不是同一种类型List[Int]

谢谢.

Lan*_*dei 50

我喜欢MichaelKrämer使用implicits的想法,但我认为它可以更直接地应用:

case class IntList(list: List[Int])
case class StringList(list: List[String])

implicit def il(list: List[Int]) = IntList(list)
implicit def sl(list: List[String]) = StringList(list)

def foo(i: IntList) { println("Int: " + i.list)}
def foo(s: StringList) { println("String: " + s.list)}
Run Code Online (Sandbox Code Playgroud)

我认为这是非常易读和直截了当的.

[更新]

还有另一种简单的方法似乎有效:

def foo(p: List[String]) { println("Strings") }
def foo[X: ClassManifest](p: List[Int]) { println("Ints") }
def foo[X: ClassManifest, Y: ClassManifest](p: List[Double]) { println("Doubles") }
Run Code Online (Sandbox Code Playgroud)

对于每个版本,您需要一个额外的类型参数,所以这不会扩展,但我认为对于三个或四个版本它没关系.

[更新2]

对于两种方法,我发现了另一个好方法:

def foo(list: => List[Int]) = { println("Int-List " + list)}
def foo(list: List[String]) = { println("String-List " + list)}
Run Code Online (Sandbox Code Playgroud)

  • `ClassManifest`是一个2.10之前的Scala解决方案,从那时起就有`TypeTag`和`ClassTag`.你能用plz更新你的解决方案吗?更多信息:http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html (4认同)

Jea*_*let 50

您可以使用其中DummyImplicit定义的定义,Predef而不是发明虚拟隐式值:

class TestMultipleDef {
  def foo(p:List[String]) = ()
  def foo(p:List[Int])(implicit d: DummyImplicit) = ()
  def foo(p:List[java.util.Date])(implicit d1: DummyImplicit, d2: DummyImplicit) = ()
}
Run Code Online (Sandbox Code Playgroud)


Vik*_*ang 10

由于类型擦除的奇迹,您的方法列表的类型参数在编译期间被擦除,因此将两种方法都减少到相同的签名,这是编译器错误.

  • Downvote,因为作者(至少在当前形式的问题中)承认他们理解了原因 - 但要求更好的解决方法. (2认同)

Aar*_*rup 10

要理解MichaelKrämer的解决方案,有必要认识到隐含参数的类型并不重要.什么重要的是,它们的类型是不同的.

以下代码以相同的方式工作:

class TestDoubleDef {
   object dummy1 { implicit val dummy: dummy1.type = this }
   object dummy2 { implicit val dummy: dummy2.type = this }

   def foo(p:List[String])(implicit d: dummy1.type) = {}
   def foo(p:List[Int])(implicit d: dummy2.type) = {}
}

object App extends Application {
   val a = new TestDoubleDef()
   a.foo(1::2::Nil)
   a.foo("a"::"b"::Nil)
}
Run Code Online (Sandbox Code Playgroud)

在字节码级别,两种foo方法都成为双参数方法,因为JVM字节码不知道隐式参数或多个参数列表.在调用站点,Scala编译器foo通过查看传入的列表的类型(直到稍后才擦除)来选择适当的方法来调用(因此传入适当的虚拟对象).

虽然它更冗长,但这种方法减轻了调用者提供隐式参数的负担.实际上,如果dummyN对象是TestDoubleDef类的私有对象,它甚至可以工作.


Mic*_*mer 8

正如Viktor Klang所说,泛型类型将被编译器擦除.幸运的是,有一个解决方法:

class TestDoubleDef{
  def foo(p:List[String])(implicit ignore: String) = {}
  def foo(p:List[Int])(implicit ignore: Int) = {}
}

object App extends Application {
  implicit val x = 0
  implicit val y = ""

  val a = new A()
  a.foo(1::2::Nil)
  a.foo("a"::"b"::Nil)
}
Run Code Online (Sandbox Code Playgroud)

感谢Michid提示!

  • 这对我来说似乎是一个可怕的蠢货,并不值得努力.更好的kludge(仍值得怀疑)将使用具有默认值的参数来区分这两种方法. (7认同)
  • @peletom:您的方法(默认参数)无法编译,错误"方法foo的多个重载替代方法定义了默认参数". (3认同)

olu*_*ies 6

如果我结合丹尼尔小号响应桑德尔Murakozi这里的回应,我得到:

@annotation.implicitNotFound(msg = "Type ${T} not supported only Int and String accepted")   
sealed abstract class Acceptable[T]; object Acceptable {
        implicit object IntOk extends Acceptable[Int]
        implicit object StringOk extends Acceptable[String]
}

class TestDoubleDef {
   def foo[A : Acceptable : Manifest](p:List[A]) =  {
        val m = manifest[A]
        if (m equals manifest[String]) {
            println("String")
        } else if (m equals manifest[Int]) {
            println("Int")
        } 
   }
}
Run Code Online (Sandbox Code Playgroud)

我得到一个类型安全的(ISH)变种

scala> val a = new TestDoubleDef
a: TestDoubleDef = TestDoubleDef@f3cc05f

scala> a.foo(List(1,2,3))
Int

scala> a.foo(List("test","testa"))
String

scala> a.foo(List(1L,2L,3L))
<console>:21: error: Type Long not supported only Int and String accepted
   a.foo(List(1L,2L,3L))
        ^             

scala> a.foo("test")
<console>:9: error: type mismatch;
 found   : java.lang.String("test")
 required: List[?]
       a.foo("test")
             ^
Run Code Online (Sandbox Code Playgroud)

该逻辑还可以被包括在类型类作为这样(由于jsuereth):@ annotation.implicitNotFound(MSG ="富不支持$ {T】仅int和string接受")密封性状的Foo [T] {DEF申请(列表:列表[T]):单位}

object Foo {
   implicit def stringImpl = new Foo[String] {
      def apply(list : List[String]) = println("String")
   }
   implicit def intImpl = new Foo[Int] {
      def apply(list : List[Int]) =  println("Int")
   }
} 

def foo[A : Foo](x : List[A]) = implicitly[Foo[A]].apply(x)
Run Code Online (Sandbox Code Playgroud)

这使:

scala> @annotation.implicitNotFound(msg = "Foo does not support ${T} only Int and String accepted") 
     | sealed trait Foo[T] { def apply(list : List[T]) : Unit }; object Foo {
     |         implicit def stringImpl = new Foo[String] {
     |           def apply(list : List[String]) = println("String")
     |         }
     |         implicit def intImpl = new Foo[Int] {
     |           def apply(list : List[Int]) =  println("Int")
     |         }
     |       } ; def foo[A : Foo](x : List[A]) = implicitly[Foo[A]].apply(x)
defined trait Foo
defined module Foo
foo: [A](x: List[A])(implicit evidence$1: Foo[A])Unit

scala> foo(1)
<console>:8: error: type mismatch;
 found   : Int(1)
 required: List[?]
       foo(1)
           ^    
scala> foo(List(1,2,3))
Int
scala> foo(List("a","b","c"))
String
scala> foo(List(1.0))
<console>:32: error: Foo does not support Double only Int and String accepted
foo(List(1.0))
        ^
Run Code Online (Sandbox Code Playgroud)

请注意,我们必须编写,implicitly[Foo[A]].apply(x)因为编译器认为这 implicitly[Foo[A]](x)意味着我们implicitly使用参数调用.