В чем разница между MyClass и MyClass.this.type и как превратить один в другой? - PullRequest
0 голосов
/ 28 января 2019

У меня похожая ситуация:

trait Abst{
  type T
  def met1(p: T) = p.toString
  def met2(p: T, f: Double=>this.type){
    val v = f(1.0)
    v.met1(p)
  }
}
class MyClass(x: Double) extends Abst{
  case class Param(a:Int)
  type T = Param
  val s = met2(Param(1), (d: Double) => new MyClass(d))
}

И до тех пор, пока я его не запущу, ошибки не отображаются, а затем:

несоответствие типов;Найдено: MyClass, требуется: MyClass.this.type

Я пробовал также решение с универсальным типом, но у меня конфликт, что this.T отличается от vT

Так что мне просто нужночтобы по возможности преодолеть приведенное выше сообщение об ошибке?


Обновление

Итак, получается, что this.type является синглтоном типа для этого единственного экземпляра.И я предложил в комментарии использовать

val s = met2(Param(1), (d: Double) => (new MyClass(d)).asInstanceOf[this.type])

Так что, если кто-то сделает комментарий по этому поводу, я знаю, насколько это уродливо, просто интересно, насколько это небезопасно?

ТакжеВы все предложили перенести определение Param за пределы класса, с чем я определенно согласен.Так что его определение будет в сопутствующем объекте MyClass

Ответы [ 3 ]

0 голосов
/ 28 января 2019

Если вы не возражаете, чтобы черта принимала T в качестве универсального параметра, это довольно простое и простое эквивалентное решение:

trait Abst[T]{
  def met1(p: T) = p.toString
  def met2(p: T, f: Double=>Abst[T]){
    val v = f(1.0)
    v.met1(p)
  }
}

case class Param(a:Int)
class MyClass(x: Double) extends Abst[Param]{
  val s = met2(Param(1), (d: Double) => new MyClass(d))
}

Я говорю, что это эквивалентно, потому что вы не теряетелюбая информация, если met2 использовать супертип вместо подтипа.Классический вариант использования для ссылки на подтип в признаке, например, имеет метод, который вы хотите вернуть MyClass вместо Abst, даже если он определен в Abst, но вы не находитесь в этой ситуации.Единственное место, где используется ссылка на ваш подтип, - это определение f, и поскольку типы функций ковариантны по своим выходным параметрам, вы можете без проблем передать любой f: Double => MyClass в f: Double => Abst[T].

Если выв любом случае вы хотите сослаться на подтип, посмотрите ответ Маркуса ... и если вы не хотите, чтобы T был универсальным параметром, все снова становится намного сложнее, потому что теперь у вас есть потенциальные конфликты между TAbst против T подтипа в определении met2.

0 голосов
/ 28 января 2019

this.type - это одноэлементный тип , который содержит одно единственное значение, а именно this.Поэтому принятие функции типа f: X => this.type в качестве аргумента гарантированно будет бессмысленным, поскольку каждый вызов f может быть просто заменен на this (плюс побочные эффекты, выполняемые f).

Вот способ заставить ваш код компилироваться с минимальными изменениями:

trait Abst { self =>
  type T
  def met1(p: T) = p.toString
  def met2(p: T, f: Double => Abst { type T = self.T }){
    val v = f(1.0)
    v.met1(p)
  }
}

case class Param(a:Int)
class MyClass(x: Double) extends Abst {
  type T = Param
  val s = met2(Param(1), (d: Double) => new MyClass(d))
}

Но, честно говоря: не делайте этого.И также не делайте ничего из F-ограниченного материала, это, вероятно, закончится полным беспорядком, особенно если вы не знакомы с шаблоном.Вместо этого выполните рефакторинг своего кода, чтобы в нем не было никаких самоссылочных спиралей.


Обновление

Замечание о том, почему указывать компилятору, что(new MyClass(d)) имеет тип this.type для некоторых других this: MyClass - действительно плохая идея:

abstract class A {
  type T
  val x: T
  val f: T => Unit
  def blowup(a: A): Unit = a.asInstanceOf[this.type].f(x)
}

object A {
  def apply[X](point: X, function: X => Unit): A = new A {
    type T = X
    val x = point
    val f = function
  }
}

val a = A("hello", (s: String) => println(s.size))
val b = A(42, (n: Int) => println(n + 58))

b.blowup(a)

Это взрывается с ClassCastException, несмотря на то, что a и b оба имеюттип A.

0 голосов
/ 28 января 2019

Чтобы преодолеть это сообщение об ошибке, вы должны использовать F-ограниченный полиморфизм .

Ваш код будет выглядеть примерно так:

trait Abst[F <: Abst[F, T], T]{ self: F =>

  def met1(p: T): String = p.toString

  def met2(p: T, f: Double => F): String = {
    val v = f(1.0)
    v.met1(p)
  }
}

case class Param(a:Int)

class MyClass(x: Double) extends Abst[MyClass, Param] {
  val s = met2(Param(1), (d: Double) => new MyClass(d))
}

Объяснение:

Использование self: F => внутри определения черты или класса ограничивает значение this.Таким образом, ваш код не скомпилируется, если this не относится к типу F.

Мы используем ограничение циклического типа F: F <: Abst[F, T].Хотя это противоречит интуиции, компилятор не возражает против этого.

В реализации MyClass мы затем расширяем MyClass на Abst[MyClass, Param], что, в свою очередь, удовлетворяет F <: Abst[F, T].

Теперьвы можете использовать F в качестве типа возврата функции в Abst и иметь MyClass return MyClass в реализации.


Вы можете подумать, что это решение уродливо, и если выделайте, тогда вы правы.

Вместо использования F-ограниченного полиморфизма всегда рекомендуется использовать классов типов для ad-hoc-полиморфизма .

Более подробную информацию вы найдете по ссылке, которую я предоставил ранее.

Действительно, прочитайте ее.Это навсегда изменит ваш взгляд на общее программирование.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...