11

In C# I can overload methods on generic type as shown in the example below:

// http://ideone.com/QVooD
using System;
using System.Collections.Generic;

public class Test {
  public static void Foo(List<int> ints) {
    Console.WriteLine("I just print");
  }

  public static void Foo(List<double> doubles) {
    Console.WriteLine("I iterate over list and print it.");
    foreach(var x in doubles)
      Console.WriteLine(x);
  }

  public static void Main(string[] args) {
    Foo(new List<int> {1, 2});
    Foo(new List<double> {3.4, 1.2});
  }
}

However if I try to do the same in Scala, it will raise a compile time error that List[Int] and List[Double] erase to the same type due to erasure. I heard Scala's Manifests can be used to work around this, but I don't know how. I didn't find anything helpful in the docs either.

So my question is: How do I use Manifests (or whatever else that works) to overload methods over generic types that erase to same type due to erasure?

Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
Miguel Fernandez
  • 211
  • 1
  • 3
  • 6
  • possible duplicate of [How can I differentiate between def foo\[A\](xs: A*) and def foo\[A, B\](xs: (A, B)*)?](http://stackoverflow.com/questions/3422336/how-can-i-differentiate-between-def-fooaxs-a-and-def-fooa-bxs-a-b) – Ken Bloom Feb 13 '11 at 05:50
  • Actually, I may want to take back that close vote: the referenced question has an added wrinkle that some of the solutions only work coincidentally because one of the types is list of tuples with two generics. You can work out solutions that will work for this version of the problem from the comments, but it takes a lot of work to sort out exactly what's going on over there. – Ken Bloom Feb 13 '11 at 05:57

3 Answers3

23

The Manifest won't really help either becuase those will have the same type after erasure.

What will help having different numbers of arguments (or different types after erasure). I find having different numbers of implicit arguments can transparently solve this problem, and by using scala.Predef.DummyImplicit, you don't even have to import an implicit anywhere.

class Test{
  def foo(ints : List[Int])
  def foo(doubles : List[Double])(implicit i1:DummyImplicit)
  def foo(strings : List[String])(implicit i1:DummyImplicit, i2:DummyImplicit)
}
Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
11

You would not do it like that in Scala. Why try to emulate something that can never work properly given JVM restrictions? Try idiomatic Scala instead:

trait Fooable[T] {
  def foo : Unit
}

object IntListFoo extends Fooable[List[Int]] {
  def foo {
    println("I just print")
  }
}

class DoubleListFoo(val l : List[Double]) extends Fooable[List[Double]] {
  def foo {
    println("I iterate over list and print it.")
    l.foreach { e =>
      println(e)
    }
  }
}

implicit def intlist2fooable(l : List[Int]) = IntListFoo
implicit def doublelist2fooable(l : List[Double]) = new DoubleListFoo(l)

Then, you can execute code like

List(1,2,3,4).foo
List(1.0,2.0,3.0).foo
Raphael
  • 9,779
  • 5
  • 63
  • 94
3

Kinda hackish, and both methods need the same return type (here: Unit)...

def fooInt(list: List[Int]) = println("int")
def fooDouble(list: List[Double]) = println("double")

def foo[N <: AnyVal](list:List[N])(implicit m:ClassManifest[N]) = m.erasure match {
    case c if c == classOf[Int] => fooInt(list.asInstanceOf[List[Int]])
    case c if c == classOf[Double] => fooDouble(list.asInstanceOf[List[Double]])
    case _ => error("No soup for you!")
}

foo(List(1,2,3,4))
//--> int

foo(List(1.0,2.0,3.0))
//--> double
Landei
  • 54,104
  • 13
  • 100
  • 195