2012-01-12 3 views
10

Следующий код взят из книги «Программирование в Scala» Мартина Одерски и др. который определяет рациональный тип:Можно ли определить локатор-конструктор в Scala?

class Rational(n: Int, d: Int) { 
    require(d != 0) 
    private val g = gcd(n.abs, d.abs) 
    val numer = n/g 
    val denom = d/g 
    ... 
    private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b) 
} 

Здесь значение г используется только, когда неявный конструктор инициализирует поля NUMER и DENOM. Предположим, программист знает, что его нельзя использовать нигде. В приведенном выше случае он по-прежнему доступен после построения объекта Rational. Это означает, что он также будет занимать пространство, поскольку является частным полем, а не локальной переменной для конструктора.

Мой вопрос: как изменить этот код так, чтобы g использовался только при строительстве, а затем выброшен?

+2

Это одна из тех «нехоровых точек» в Скале. Представьте себе, что если данный объект-конструктор использовался только для извлечения какой-то произвольной информации: если бы он навсегда оставил бы его недоступным для рекультивации, даже если бы требовалось лишь небольшое подмножество информации. Я думаю, что есть синтаксис «pre-class body», но я не могу вспомнить, что это такое. –

+0

@pst Спасибо за это быстрое редактирование. Я делал это между тем. Но ты выглядишь лучше. :) – ciuncan

+2

Если у кого-то есть ссылки на этот синтаксис «пред-класс тела», о котором упоминает pst, мне было бы интересно услышать об этом. –

ответ

10

В этом случае, как насчет чего-то подобного?

class Rational(n: Int, d: Int) { 
    require(d != 0) 
    val (numer, denom) = { 
    val g = gcd(n.abs, d.abs) 
    (n/g, d/g) 
    } 
    private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b) 
} 

EDIT: Это также создает дополнительное поле, которое удерживает кортеж, как показано на рисунке, запустив javap на скомпилированного класса (спасибо, Алексей):

public class Rational extends java.lang.Object implements scala.ScalaObject{ 
    private final scala.Tuple2 x$1; // actually unwanted! 
    private final int numer; 
    private final int denom; 
    public int numer(); 
    public int denom(); 
    private int gcd(int, int); 
    public Rational(int, int); 
} 

В других случаях я иногда использую блок locally, чтобы избежать поворота каждого val в поле:

class A { 
    locally { 
    val value1 = // ... 
    val value2 = // ... 
    } 
} 
+0

Спасибо, что это может быть лучшим решением. Я также узнал о «локальном» блоке, поэтому еще раз спасибо. – ciuncan

+0

Могу ли я использовать значения или переменные, определенные в локальном блоке вне его, только в этом классе? Или они локально локально блокируются? – ciuncan

+2

@ciuncan Все, что объявлено в блоке кода (т. Е. Ограничено фигурными фигурными скобками), доступно только в этом блоке. 'local' - это просто метод удобства для исключения проблем с выводами с точкой с запятой (а не с ключевым словом), поэтому изменить это невозможно. См. Http://stackoverflow.com/questions/3237727/what-does-predef-locally-do-and-how-is-it-different-from-predef-identity для информации о 'local'. –

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