Как можно объединить шаблон класса типов с подтипом? - PullRequest
14 голосов
/ 13 июля 2011

Предположим, я использую шаблон классов типов в Scala. Вот как я делаю класс C часть класса типов Foo:

Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_26).

scala> trait Foo[T] { def foo(t: T) }
defined trait Foo

scala> def foo[T : Foo](t: T) { implicitly[Foo[T]].foo(t) }
foo: [T](t: T)(implicit evidence$1: Foo[T])Unit

scala> class C
defined class C

scala> foo(new C)
<console>:11: error: could not find implicit value for evidence parameter of type Foo[C]
       foo(new C)
          ^

scala> implicit object FooC extends Foo[C] { override def foo(c: C) { println("it's a C!") } }
defined module FooC

scala> foo(new C)
it's a C!

Пока все хорошо. Но предположим, что у меня есть подкласс D в C, и я хочу, чтобы экземпляры D тоже были «в» классе типов:

scala> class D extends C
defined class D

scala> foo(new D)
<console>:13: error: could not find implicit value for evidence parameter of type Foo[D]
       foo(new D)
          ^

Doh! Как мне сделать эту работу без необходимости явно предоставлять экземпляр класса типов для D?

1 Ответ

15 голосов
/ 13 июля 2011

Существуют различные возможные решения для этого, в зависимости от того, хочу ли я решить проблему только для C или хочу ли я решить проблему для всего класса типов .

Только для C, вместо implicit object FooC ... мы говорим:

implicit def CIsFoo[T <: C]: Foo[T] =
  new Foo[T] { override def foo(t: T) { println("it's a C!") } }

Чтобы исправить все Foo, сделайте его контрвариантным:

trait Foo[-T] { def foo(t: T) }

Или если по какой-то причинеВы не можете или не хотите этого делать, вы можете заменить def foo... на:

def foo[T](t: T)(implicit foo: Foo[_ >: T]) =
  foo.foo(t)

(Спасибо #scala обитателям Даниэлю Собралу и Стефану Зайгеру за помощь.)

ОБНОВЛЕНО 20 сентября 2011 года, чтобы включить решение "make Foo контрвариантное", которое я пропустил

...