2013-11-17 6 views
23

Если у меня есть следующий класс case с частным конструктором, и я не могу получить доступ к методу apply в сопутствующем объекте.Частный конструктор класса case Scala, но общедоступный метод применения

case class Meter private (m: Int) 

val m = Meter(10) // constructor Meter in class Meter cannot be accessed... 

Есть ли способ, чтобы использовать класс случая с собственным конструктором, но сохранить сгенерированный применить-метод в сопутствующей общественности?

Я знаю, что нет никакой разницы (в моем примере) между двумя вариантами:

val m1 = new Meter(10) 
val m2 = Meter(10) 

, но я хочу, чтобы запретить первый вариант.

- редактировать -

Удивительно следующие работы (но не совсем то, что я хочу):

val x = Meter 
val m3 = x(10) // m3 : Meter = Meter(10) 
+0

Какую версию Scala вы используете? Я просто попробовал это в своем 2.10.0 REPL и 'val m2 = Meter (10)' не дает никаких ошибок] –

+0

@LuigiPlinge Я использую Scala 2.10.3 – Erik

+0

Похоже, что это строка 'case class Meter private (m: Int) ', который вызывает ошибку, когда объявлен как объект верхнего уровня (http://scalafiddle.net/console/eb6fdc36b281b7d5eabf33396c2683a2), но он работает, когда объявлен в рамках другого объекта или REPL (http://scalafiddle.net/console/cdc0d6e63aa8e41c89689f54970bb35f) –

ответ

27

Вот техника, чтобы иметь частный конструктор и общественного применить метод.

trait Meter { 
    def m: Int 
} 

object Meter { 
    def apply(m: Int): Meter = { MeterImpl(m) } 
    private case class MeterImpl(m: Int) extends Meter { println(m) } 
} 

object Application extends App { 
    val m1 = new Meter(10) // Forbidden 
    val m2 = Meter(10) 
} 

Справочная информация private-and-protected-constructor-in-scala

+0

@senia, нет. Вот компиляция для нового Meter (10) {}; признак признака - признак; не принимает аргументы конструктора – Farmor

+1

Я пропустил это. Вы должны определить 'Meter' как' trait Meter {def m: Int} ', чтобы разрешить доступ к' m'. Вы также можете сделать это «запечатанным». – senia

+0

Я думаю, что это хорошее обходное решение, потому что вы получаете заданное поведение и некоторые из преимуществ классов case (например, внедренных равных). Но это тяжело по сравнению с классом одного случая. – Erik

0

Можно с некоторыми неявных уловок:

// first case 
case class Meter[T] private (m: T)(implicit ev: T =:= Int) 
object Meter { 
    def apply(m: Int) = new Meter(m + 5) 
} 

создал некоторый другой конструктор (и применить метод подписи), но гарантию, что параметр может быть только Int.

И после того, как у вас есть случай класса с функциями случай класса (с сопоставления с образцом, хэш-код & равен) исключить конструктор по умолчанию:

scala> val m = Meter(10) 
m: Metter[Int] = Meter(15) 

scala> val m = new Meter(10) 
<console>:9: error: constructor Meter in class Meter cannot be accessed in object $iw 
     val m = new Meter(10) 

ИЛИ с типом мечения (наивная реализация):

trait Private 
case class Meter private (m: Integer with Private) 
object Meter { 
    def apply(m: Int) = new Meter((m + 5).asInstanceOf[Integer with Private]) 
} 

Работает должным образом:

val x = new Meter(10) 
<console>:11: error: constructor Meter in class Meter cannot be accessed in object $iw 
       new Meter(10) 
      ^

val x = Meter(10) 
x: Meter = Meter(15) 

Некоторые возможные проблемы с примитивными типами и тегами типа описаны here

Смежные вопросы