Scala: Method overloading over generic types

4.3k views Asked by At

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?

3

There are 3 answers

0
Landei On

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
0
Ken Bloom On

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)
}
0
Raphael On

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