объект-компаньон как фабрика в scala

Я только начинаю работать со Scala и работаю над некоторыми учебниками. Я наткнулся на объект-компаньон и использовал его как фабрику. Я попробовал несколько вещей. Однако я не заставляю следующее работать должным образом. Не могу уложиться в голове..

import math._

abstract class Point{
  // ...
}
object Point{
  private class PointInt(val x:Int,val y:Int) extends Point{
    def +(that:PointInt) = new PointInt(this.x + that.x, this.y + that.y)
    def distance(that:PointInt) = 
      sqrt(pow((this.x - that.x), 2) + pow((this.y - that.y), 2))
  }
  private class PointDouble(val x:Double,val y:Double) extends Point{
    def +(that:PointDouble) = new PointDouble(this.x + that.x, this.y + that.y)
    def distance(that:PointDouble) = 
      sqrt(pow((this.x - that.x), 2) + pow((this.y - that.y), 2))
  }
  def apply(x:Int,y:Int):Point = new PointInt(x,y)
  def apply(x:Double,y:Double):Point = new PointDouble(x,y)
}

val a = Point(1,2)
val b = Point(3,4)
val c = a+b // does not work... 

Просто пытаюсь сложить две целые точки, как я определил в методах... Кто-нибудь знает, что я делаю неправильно??

EDIT: я пытался обернуть следующий (рабочий) класс в Factory.

class Point(val x:Int,val y:Int){
  def +(that:Point) = new Point(this.x + that.x, this.y + that.y)
  def distance(that:Point) = sqrt(pow((this.x - that.x),2) + pow((this.y - that.y),2))

}

val a = new Point(1,2)              //> a  : week1.OU2.Point = week1.OU2$Point@73e48fa7
val b = new Point(3,4)              //> b  : week1.OU2.Point = week1.OU2$Point@677bb8fe
val c = a+b                         //> c  : week1.OU2.Point = week1.OU2$Point@6bae60c5
c.x                                 //> res0: Int = 4
c.y                                 //> res1: Int = 6

person Kevinw1983    schedule 02.06.2013    source источник
comment
Откуда a взять метод +? Все, что он знает, это то, что он имеет тип Point. Он не знает, что это на самом деле PointInt — вот что означает возвращаемый тип Point! Вы хотели добавить метод + к Point?   -  person Rex Kerr    schedule 02.06.2013
comment
В дополнение к комментариям Рекса, метод + должен был бы принимать Point, а не подкласс Point, если вы хотите передать PointInt в PointDouble.   -  person Boris the Spider    schedule 02.06.2013
comment
Привет, Рекс. Разве метод применения не должен давать объект PointInt?   -  person Kevinw1983    schedule 02.06.2013
comment
Если вы аннотируете метод с типом возвращаемого значения def apply(...): Point =, то этот тип будет виден с сайта вызова, даже если возвращаемое значение на самом деле является подтипом Point. Сайт вызова не видит дальше : Point =, поэтому понятия не имеет, что у вас есть PointInt с методом +.   -  person 0__    schedule 03.06.2013
comment
Ааа, это имеет смысл. Таким образом, применения должны быть изменены на: def apply(x:Int,y:Int):PointInt = new PointInt(x,y); def apply(x:Double,y:Double):PointDouble = new PointDouble(x,y)? Это работает!   -  person Kevinw1983    schedule 03.06.2013
comment
@ Kevinw1983 Kevinw1983 Если я не ошибаюсь, ваше последнее изменение будет работать только в том же файле. Если вы попытаетесь добавить две точки снаружи, вы столкнетесь с той же проблемой, поскольку PointInt и PointDouble равны private.   -  person Viktor Seifert    schedule 03.06.2013


Ответы (1)


Я не совсем уверен, какие ограничения на самом деле наложены на вас, например, какие классы должны/должны быть приватными, но использование F-ограниченного полиморфизма может стать ступенькой к вашему желаемому решению.

/* Simplified interface (adding sqrt is straight-forward) */

abstract class Point[P <: Point[P]] {
  def +(that: P): P
}

/* Two implementations */

class PointInt(val x:Int,val y:Int) extends Point[PointInt] {
  def +(that:PointInt) = new PointInt(this.x + that.x, this.y + that.y)
}

class PointDouble(val x:Double,val y:Double) extends Point[PointDouble] {
  def +(that:PointDouble) = new PointDouble(this.x + that.x, this.y + that.y)
}

/* Companion object */

object Point {
  def apply(x:Int,y:Int) = new PointInt(x,y)
  def apply(x:Double,y:Double) = new PointDouble(x,y)
}

/* Use cases */

val a = Point(1,2)
val b = Point(3,4)
val c = a+b // ok
val d = Point(1.0, 2.5)
val e = c+d // error: type mismatch

Обратите внимание, однако, что это не поможет вам, если вы хотите скрыть свои реализации, т. Е. Сделать их частными и объявить общедоступные интерфейсы, используя только общий Point - как уже указывали другие.

person Malte Schwerhoff    schedule 03.06.2013