Использование параметров типа и mixins в Scala - PullRequest
3 голосов
/ 22 июля 2010

РЕДАКТИРОВАТЬ 2:

Мне удалось добиться безопасности типов, которую я хотел в своем упражнении с RomanNumerals, используя комбинацию mixins и параметров типов с кодом ниже. По сути, после импорта всего в RomanNumerals я могу написать L X V I, но не L L или X L X, потому что тогда я получаю ошибку компиляции несоответствия типов (поскольку это были бы недопустимые комбинации римских цифр ). Теперь мне интересно, если этот способ использования свойств, миксинов и параметров типов считается нормальным или я злоупотребляю языком, если можно так выразиться :) Есть ли лучший способ достижения такого же типа безопасности типов с помощью более простого / чистого код

object RomanNumerals {
  trait Numeral {
    def num:Int
  }
  trait NumeralI[N<:Numeral] extends Numeral
  trait NumeralV[N<:Numeral] extends NumeralI[N] {
    def I = new RomanNumeral[Numeral](1 + this.num) with Numeral
    def II = new RomanNumeral[Numeral](2 + this.num) with Numeral
    def III = new RomanNumeral[Numeral](3 + this.num) with Numeral
  }

  trait NumeralX[N<:Numeral] extends NumeralV[N] {
    def IV = new RomanNumeral[Numeral](4 
              + this.num) with Numeral
    def V = new RomanNumeral[NumeralI[Numeral]](5 
             + this.num) with NumeralV[NumeralI[Numeral]]
    def IX = new RomanNumeral[Numeral](9
              + this.num) with Numeral
  }

  trait NumeralL[N<:Numeral] extends NumeralX[N] {
    def X = new RomanNumeral[NumeralV[Numeral]](10
             + this.num) with NumeralX[NumeralV[Numeral]]
    def XX = new RomanNumeral[NumeralV[Numeral]](20 
              + this.num) with NumeralX[NumeralV[Numeral]]
    def XXX = new RomanNumeral[NumeralV[Numeral]](30 
               + this.num) with NumeralX[NumeralV[Numeral]]
  }

  class RomanNumeral[T <: Numeral](val num:Int) {
    override def toString = num toString
    def apply[N >: T <: Numeral](rn:NumeralI[N]) = 
      new RomanNumeral[Numeral](rn.num + num) with Numeral

    def apply[N >: T <: Numeral](rn:NumeralV[N]) = 
      new RomanNumeral[NumeralI[Numeral]](rn.num
       + num) with NumeralV[NumeralI[Numeral]]

    def apply[N >: T <: Numeral](rn:NumeralX[N]) = 
      new RomanNumeral[NumeralV[Numeral]](rn.num 
       + num) with NumeralX[NumeralV[Numeral]]

    def apply[N >: T <: Numeral](rn:NumeralL[N]) = 
      new RomanNumeral[NumeralX[Numeral]](rn.num 
       + num) with NumeralL[NumeralX[Numeral]]

  }

  val I = new RomanNumeral[NumeralI[Numeral]](1) with NumeralI[Numeral]
  val II = new RomanNumeral[NumeralI[Numeral]](2) with NumeralI[Numeral]
  val III = new RomanNumeral[NumeralI[Numeral]](3) with NumeralI[Numeral]
  val IV = new RomanNumeral[NumeralI[Numeral]](4) with NumeralI[Numeral]
  val V = new RomanNumeral[NumeralI[Numeral]](5) with NumeralV[NumeralV[Numeral]]
  val IX = new RomanNumeral[NumeralI[Numeral]](9) with NumeralI[Numeral]
  val X = new RomanNumeral[NumeralV[Numeral]](10) with NumeralX[NumeralX[Numeral]]
  val XX = new RomanNumeral[NumeralV[Numeral]](20) with NumeralX[NumeralX[Numeral]]
  val XXX = new RomanNumeral[NumeralV[Numeral]](30) with NumeralX[NumeralX[Numeral]]
  val XL = new RomanNumeral[NumeralV[Numeral]](40) with NumeralX[NumeralX[Numeral]]
  val L = new RomanNumeral[NumeralX[Numeral]](50) with NumeralL[NumeralL[Numeral]] 
}

EDIT:

Дальнейший вопрос основан на ответе Виктора. Хорошо, но что, если я добавлю верхние и нижние границы к параметру типа, чтобы B был признаком? Э.Г.

trait Bar
class Foo[T<:Bar](n:Int) {
  def apply[B >: T <: Bar](f:Foo[B]) = {
    new Foo[B](n + f.n) with B
  }
}

Или, может быть, B все еще может быть классом в этом случае? Что если я знаю, что аргумент f тоже относится к типу Foo[B] with B? Есть ли способ использовать это для смешивания B с типом возврата?

ОРИГИНАЛЬНЫЙ ВОПРОС НИЖЕ

Я хочу смешать черту, которую я получаю в качестве параметра типа при создании объекта в Scala:

class Foo(val num:Int) { 
  def withM[B](foo:Foo) = new Foo(foo.num) with B
}

Это приводит к ошибке компиляции:

error: class type required but B found
def withM[B](foo:Foo) = new Foo(foo.num) with B
                                              ^

Я также пробовал:

class Foo(val num:Int) { 
  def withM[B](foo:Foo) = new Foo(foo.num) with classOf[B]
}

Но это не работает:

error: not found: type classOf
def withM[B](foo:Foo) = new Foo(foo.num) with classOf[B]
                                              ^

Есть ли способ обойти это? Таким образом, возвращаемый тип withM становится Foo with B, где B - параметр типа, передаваемый в withM

1 Ответ

5 голосов
/ 22 июля 2010

Такая функциональность просто невозможна (или недоступна) Кроме того, возможно смешивать только черты (и интерфейсы), и ваш параметр типа может быть Int, насколько знает компилятор.

Вам необходимо указать конкретную черту или тип интерфейса следующим образом:

trait T
class Foo
object Test {
  def apply = new Foo with T
}
...