Есть ли в Scala что-то похожее на явную реализацию интерфейса в C #?

В C # вы можете явно реализовать интерфейсы. В этом случае явно реализованные методы могут быть вызваны только через переменную, интерфейс которой является статическим типом. Это позволяет избежать конфликтов типа имя / возвращаемый тип и предоставить разные реализации одного и того же метода в зависимости от статического типа this.

Например:

interface IFoo
{
    int DoSomething();
}

interface IBar
{
    string DoSomething();
}

class Impl : IFoo, IBar
{
    int IFoo.DoSomething() { /* Implementation for IFoo */ }
    string IBar.DoSomething() { /* A different implementation for IBar */ }
    public void DoSomething() { /* Yet another implementation for Impl */ }
}

Как бы вы справились с этим случаем в Scala:

trait Foo {
    def doSomething(): Int
}

trait Bar {
    def doSomething(): String
}

class Impl extends Foo with Bar {
    /* only one "doSomething()" visible here (that of Bar?) */
    /* what now... ? */
}

person Tobias Brandt    schedule 16.07.2013    source источник
comment
Вы можете использовать дженерики T? Не на 100% то, что вы получаете, но похоже, что вы можете это использовать. Вы пытаетесь вернуть другой тип на основе того же метода подписи? Например: у вас может быть T DoSomething ‹T› ();   -  person Dr Schizo    schedule 16.07.2013


Ответы (2)


Если вы просто пытаетесь заставить свой класс следовать двум отдельным несовместимым интерфейсам, вам придется вместо этого написать оболочки. Например,

implicit case class ImplAsFoo(impl: Impl) extends Foo {
  def asFoo = this
  def doSomething: Int = 5
}

Теперь вы можете

impl.asFoo

на сайте использования, чтобы переключиться на обертку Foo.

В некоторых случаях, однако, может быть более естественным использовать шаблон класса типа для обеспечения подключаемых функций:

trait IFoo[A] { def doSomething: Int }
trait IBar[A] { def doSomething: String }

// These need to be companions so :paste if you're using REPL
class Impl { def doSomething { println("Hey!") } }
object Impl {
  implicit object FooImpl extends IFoo[Impl] { def doSomething = 5 }
  implicit object BarImpl extends IBar[Impl] { def doSomething = "salmon" }
}

def needsFoo[A <: Impl: IFoo](a: A) = implicitly[IFoo[Impl]].doSomething

scala> needsFoo(new Impl)
res1: Int = 5

scala> (new Impl).doSomething
Hey!

Это не в точности, но это также решает проблему наличия разных реализаций без сбоев в схемах именования. (Если вам нужно doSomething с объектом impl, вы должны передать его как параметр в implicit object, который обрабатывает этот случай.)

Если у вас уже есть черты характера, то это, конечно, вам не поможет. Но когда вы разрабатываете с нуля, вместо того, чтобы иметь кучу черт с несовместимыми методами, вы можете вместо этого попробовать классы типов.

Наконец, если вы не можете избавиться от кучи нетипизированных вещей, перемешанных вместе, из которых вам нужно выбрать Foos, вам придется изобрести более сложные схемы вроде этой:

trait CanBeFoo { def asFoo: Foo }
trait Foo { def doSomething: Int }

// :paste these two together
class Impl extends CanBeFoo {
  def doSomething { println("Ho!") } 
  def asFoo = ImplAsFoo(this)
}
case class ImplAsFoo(impl: Impl) extends Foo {
  def doSomething = 6
}

val myList = List("salmon", new Impl, new Foo { def doSomething = 4 })
def doIt(f: Foo) { println(f.doSomething) }
myList.foreach {
  case f: Foo => doIt(f)
  case cf: CanBeFoo => doIt(cf.asFoo)
  case _ => println("nuh-uh")
}

// Produces
// nuh-uh
// 6
// 4

Вы можете предпочесть промежуточную карту:

myList.map{ case cf: CanBeFoo => cf.asFoo; case x => x }.foreach{
  case f: Foo => println(f.doSomething)
  case _ => println("nuh-uh")
}
person Rex Kerr    schedule 16.07.2013

Нет, это строго несовместимые базовые типы. Когда класс расширяет черту, он имеет эту черту как часть идентичности типа. У него не может быть взаимно несовместимых типов идентификаторов, как в случае любого класса, который extends Foo with Bar (как вы написали эти черты).

person Randall Schulz    schedule 16.07.2013