Я использую систему, которая должна инициализировать многие объекты с использованием транзакций, и по причинам, выходящим за рамки этого вопроса, эти транзакции должны быть переданы в конструкторы. Как это:
trait Mutable
class Txn(i: Int) {
def newID(implicit m: Mutable): Int = i
override def finalize(): Unit = println("Finalised " + i)
}
class User(t0: Txn) extends Mutable {
val id = t0.newID(this)
}
Теперь я боюсь, что существует проблема с сборкой мусора транзакций:
val u = new User(new Txn(1234))
System.gc() // hmmm, nothing seems to happen?
Итак, мой вопрос: получает ли когда-нибудь аргумент конструктора t0
сборщик мусора, или я создаю здесь утечку памяти? В эквивалентном Java-коде, я думаю, у меня будет что-то вроде этого:
public class User implements Mutable {
final int id;
public User(Txn t0) {
id = t0.newID(this);
}
}
и я уверен, что t0
собрано. Но так ли это в случае с Scala?
Если нет, как я могу убедиться, что t0
собирает мусор? Помните, что я должен передать транзакцию в качестве аргумента конструктора, поскольку класс User
реализует некоторые черты, которые должны быть переданы в методы Txn
, поэтому эти методы (например, newID
) не могут быть вызванным перед построением User
.
Ранее я пытался сконструировать все, что использует транзакцию, за пределами пользовательского объекта с тоннами lazy
взаимозависимых значений, но это было действительно грязно. Например, это, которое уже наполовину не читается, вызывает переполнение стека:
trait User extends Mutable { def id: Int }
def newUser(implicit tx: Txn): User = {
lazy val _id: Int = tx.newID(u)
lazy val u = new User { val id: Int = _id } // oops, should be lazy val id!
u
}
val u = newUser(new Txn(1234))
Вы можете себе представить, что действительно ужасно, что компилятор не обнаружит проблему с отсутствующим отложенным значением val, поэтому я определенно предпочел бы вариант arg конструктора.