Когда мы используем kotlin, в основном есть два способа объявить константу:
class Foo {
companion object {
private const val a = "A"
}
}
И:
class Foo {
private val a = "A"
}
какая из них лучше?
Я искал способ companion эквивалентен
public static final class Companion {
@NotNull
private static final String a = "A";
}
в Яве.
В этом вопросе
Если константа не является статической, Java выделит память для этой константы в каждом объекте класса (т. е. одну копию константы для каждого объекта).
Если константа статическая, будет только одна копия константы. для этого класса (т. е. по одной копии на класс).
Да, это правда.
НО Если у меня есть 100 или более констант в одном классе, все статические, когда они будут выпущены? Всегда есть в памяти. Они не будут выпущены, пока программа не будет убита/завершена, верно?
Так что я думаю второй путь
class Foo {
private val a = "A"
}
это правильный путь. Поскольку любой экземпляр будет освобожден в какой-то момент, тогда память будет освобождена.
Не совсем уверен, что я что-то пропустил. Любые комментарии? Спасибо!
Должна ли константа быть видна за пределами файла Kotlin? Если нет, просто объявите его как элемент верхнего уровня: val MY_CONST = "..."




НО Если у меня 100 и более контактов в одном классе все статические, когда они освобождаются?
Вы собираетесь записать информацию о 100 контактах в исходный файл? Это... как правило, не туда, куда должны поступать такие данные, но ладно. 100.
Знаешь что? Это кустарная лига. Давайте сделаем 10 000 контактов, записанных в один гигантский kotlin или java файл.
Это все еще арахис, мудрый памятью. Какова доля копейки по сравнению с мировым ВВП? Что-то в этом духе. Всякий раз, когда вы загружаете файл класса, он находится в памяти. И никогда не освобождается. И это не имеет значения ни на йоту.
Все эти постоянные значения являются частью определения класса и загружаются хотя бы один раз в этом смысле, независимо от того, что вы делаете.
Правильный способ, очевидно, заключается в том, чтобы статические данные (т. е. вещи, присущие классу и никогда не меняющиеся), загружались точно один раз, а не «х раз, где х — количество экземпляров». Это семантически корректно (по своей природе неизменный, глобальный для класса материал — это static, вот что означает статика) и увеличивает «нагрузку» того факта, что этот класс был затронут на несколько процентных пунктов (вы просто добавляете ссылки ; все эти строки загружаются только один раз и загружаются независимо от того, сделаете ли вы этот материал статическим или нет, это часть определения класса. Как вы думаете, куда идут эти строки, если есть 0 экземпляров? JVM не собирается перезагружать этот файл класса из disk каждый раз, когда вы вызываете new Foo()!) - тогда как, если это один раз за экземпляр, вы можете просматривать миллионы ссылок без всякой причины.
Спасибо, извините за опечатку contacts = constants. Что я имею в виду, 100 констант в одном классе — это нехорошо, да, но все ваши классы могут иметь более 100 констант. Но я согласен с вашей точкой зрения. Я думаю, что если это обычная бизнес-модель, то статика лучше. Если модель может иметь очень мало экземпляров и не обязательно существовать в течение всей жизни программы, то лучше использовать приватную модель. Еще раз спасибо!
На самом деле это ничего не меняет в ответе. У вас есть 100 штучек. Неважно, какие они. Как я уже сказал, ладно, больше 100. [A] даже если вы сделаете их 10 000, это не имеет значения, это ничего, и [B] даже если вы сделаете их не статическими, эти константы все равно постоянно загружаются в память, или по крайней мере, все соответствующие их части (например, каждая строка). Нет, если у модели может быть очень мало экземпляров, все равно сделайте это static. Вы занимаетесь преждевременной оптимизацией, и, как обычно, это вообще не работает и затрудняет отладку и модификацию вашего кода.
Извините, могу я подтвердить, почему вы сказали, что 10 000 экземпляров константного свойства все еще находятся в памяти? скажем var list: MutableList<Foo>? = listOf(foo1, foo2....) когда я ставлю list = null тогда все должно быть отпущено правильно? Спасибо.
Нет, если код буквально гласит: var list: MutableList<Foo>? = listOf("a string constant typed directly into your java code", "another literal written directly into the java code", "and so on"); list = null; — тогда все эти строки остаются в памяти.
После исследования строки стажер очистите ее сейчас, спасибо!
Возможность переопределения делает разницу между ними. Взгляните на следующий пример. speed может быть свойством, постоянным для класса, но вам может понадобиться другое постоянное значение для разных подклассов.
import kotlin.reflect.KClass
open class Animal{
companion object{
val speed : Int = 1
}
var x: Int = 0
fun move(){
x += speed
print("${this} moved to ${x}\n")
}
}
class Dog : Animal(){
companion object{
val speed : Int = 3
}
}
class Cat : Animal(){
companion object{
val speed : Int = 2
}
}
fun main()
{
val a = Animal()
a.move()
val c = Cat()
c.move()
val d = Dog()
d.move()
}
Выход:
Animal@49c2faae moved to 1
Cat@17f052a3 moved to 1
Dog@685f4c2e moved to 1
Это не работает, потому что speed в move() всегда относится к Animal.speed. Итак, в этом случае вы хотите speed быть членом экземпляра вместо статического (или компаньона).
open class Animal{
open val speed : Int = 1
var x: Int = 0
fun move(){
x += speed
print("${this} moved to ${x}\n")
}
}
class Dog : Animal(){
override val speed : Int = 3
}
class Cat : Animal(){
override val speed : Int = 2
}
Выход:
Animal@37f8bb67 moved to 1
Cat@1d56ce6a moved to 2
Dog@17f052a3 moved to 3
Как правило, если значение является чем-то абсолютно независимым от отдельных экземпляров, сделайте его статическим. Напротив, если свойство звучит так, как будто свойство принадлежит отдельному экземпляру, а не принадлежит типу, даже если оно постоянно для всех экземпляров (на данный момент), я бы поместил его в качестве члена экземпляра, поскольку оно, вероятно, подлежит изменение будущего развития. Хотя совершенно нормально сделать его статичным, пока вы не обнаружите, что вам действительно нужно измениться в будущем. В приведенном выше примере вы даже можете в конечном итоге изменить speed на var вместо val, когда позже обнаружите, что каждая отдельная собака имеет разную скорость. Просто делайте то, что вам нужно в данный момент :)
В Kotlin существует множество способов определения констант. Они различаются объемом и использованием пространств имен, использованием памяти и способностью наследовать и переопределять.
Не существует единого «лучшего» подхода; это зависит от того, что представляют ваши константы и как вы их используете.
Вот несколько (упрощенных) примеров:
const val a = "A"
Объявление «свободно» в файле, не содержится ни в одном классе. Обычно это самый простой и лаконичный способ, но он может не прийти в голову людям, привыкшим к Java, поскольку прямого эквивалента в Java у него нет.
Константа доступна в любом месте файла (в виде голого a); и если он не частный, его также можно использовать где угодно (либо как полностью квалифицированный list.of.packages.a, либо, если он импортирован, просто как a). Он не может быть унаследован или переопределен.
class A {
companion object {
const val a = "A"
}
}
Если вы знаете Java, это примерно эквивалентно статическому полю (как показывает вопрос). Как и в случае объявления верхнего уровня, в памяти находится только один экземпляр свойства.
Основное отличие состоит в том, что теперь он является частью A, что влияет на его масштаб и доступность: он доступен в любом месте в A и его companion object, и (если вы не ограничите его) он также может использоваться в другом месте (как list.of.packages.A.a, и A.a, если A находится в объем и просто a, если все это импортировано). (Вы не можете наследовать от синглтона, такого как сопутствующий объект, поэтому его нельзя наследовать или переопределить.)
class A {
val a = "A"
}
Это отличается как в концепции, так и на практике, потому что каждый экземпляр A имеет свое собственное свойство. Это означает, что каждый экземпляр A будет занимать дополнительные 4 или 8 байтов (или что-то еще, что нужно платформе для хранения ссылки) — даже если все они содержат одну и ту же ссылку. (Сам объект String интернирован.)
Если A или a закрыты (как здесь), это вряд ли имеет смысл ни с точки зрения смысла кода, ни с точки зрения использования памяти. (Если у вас всего несколько экземпляров, это не будет иметь большого значения — но что, если у вас в памяти сотни тысяч экземпляров?) быть под рукой. (Однако см. ниже.)
Еще раз, свойство доступно в любом месте в пределах A и (если не ограничено) в любом месте, которое может видеть a. (Обратите внимание, что в этом случае свойство не может быть open, что означает, что компилятор не может использовать его встраивание.)
class A {
val a get() = "A"
}
Концептуально это очень похоже на предыдущий случай: каждый экземпляр A имеет собственное свойство, которое можно переопределить в подклассах. И доступ к нему осуществляется точно таким же образом.
Однако реализация более эффективна. Эта версия предоставляет функцию-получатель — и, поскольку она не ссылается на резервное поле, компилятор его не создает. Таким образом, вы получаете все преимущества свойства класса, но без накладных расходов на память.
enum class A {
A
}
Это имеет смысл только в том случае, если у вас есть ряд этих значений, которые являются примерами некоторой общей категории; но если вы это сделаете, то обычно это гораздо лучший способ сгруппировать их вместе и сделать их доступными как именованные константы.
val letterConstants = mapOf('a' to "A")
Этот подход имеет смысл, если вы хотите искать значения программно, но если у вас много значений и вы хотите избежать загрязнения пространств имен, он все равно может иметь смысл, даже если вы когда-либо обращаетесь к нему только с помощью констант.
Его также можно загрузить (или расширить) во время выполнения (например, из файла или базы данных).
(Я уверен, что есть и другие подходы, о которых я не думал.)
Как я уже сказал, трудно рекомендовать конкретную реализацию, потому что это будет зависеть от проблемы, которую вы пытаетесь решить: что означают константы, с чем они связаны и как и где они будут использоваться.
const val A вверху более или менее «жизнь вне пространства имен классов» public static final String A = "..."; — почему это должно быть незнакомо программистам Java?
@rzwitserloot Я не уверен, что вы имеете в виду… В Java все должно быть определено в классе или интерфейсе (включая цитируемую вами строку); он не позволяет вам определять константы вне любого класса/объекта/интерфейса, как это делает Kotlin.
Если все константы являются
Stringконстантами, то они независимо кэшируются во внутреннем пулеString.companionконцептуально чище, но на практике я сомневаюсь, что это вообще имеет значение.