Singleton класс в Котлине

Я хочу знать, как создать одноэлементный класс в Kotlin, чтобы мой класс Util создавал его экземпляр только один раз за выполнение приложения. Однако, когда я преобразовал свой класс Java в kotlin, был сгенерирован приведенный ниже код.

Это правильно?

companion object {
    private var utilProject: UtilProject? = null

    val instance: UtilProject
        get() {
            if (utilProject == null) utilProject = UtilProject()
            return utilProject!!
        }
} 

Я мог найти связанный вопрос, но он с параметрами, и я не могу конвертировать его без параметров.

64
0
65 161
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Ответ принят как подходящий

Только

companion object {
    val instance = UtilProject()
} 

выполнит свою работу, потому что сопутствующий объект сам по себе является синглтоном на уровне языка. (instance будет создан при вызове сопутствующего объекта первый.)

- Обновлено -

Если вам нужно контролировать, когда инициализируется одноэлементный объект, вы можете создать по одному объекту для каждого класса.

class UtilProject {
    ....
    companion object {
        val instance = UtilProject()
    }
}

class AnotherClass {
    ...
    companion object {
        val instance = AnotherClass()
        const val abc = "ABC"
    }
}

fun main(args: Array<String>) {
    val a = UtilProject.instance // UtilProject.instance will be initialized here.
    val b = AnotherClass.abc // AnotherClass.instance will be initialized here because AnotherClass's companion object is instantiated.
    val c = AnotherClass.instance
}

Здесь AnotherClass.instance инициализируется до фактического вызова AnotherClass.instance. Он инициализируется при вызове сопутствующего объекта AnotherClass. Чтобы предотвратить его инициализацию раньше, когда это необходимо, вы можете использовать следующее:

class UtilProject {
    ....
    companion object {
        fun f() = ...
    }
}

class AnotherClass {
    ...
    companion object {
        const val abc = "ABC"
    }
}

object UtilProjectSingleton {
    val instance = UtilProject()
}

object AnotherClassSingleton {
    val instance = AnotherClass()
}

fun main(args: Array<String>) {
    UtilProject.f()
    println(AnotherClass.abc)

    val a = UtilProjectSingleton.instance // UtilProjectSingleton.instance will be initialized here.
    val b = AnotherClassSingleton.instance // AnotherClassSingleton.instance will be initialized here.

    val c = UtilProjectSingleton.instance // c is a.
}

Если вам все равно, когда инициализируется каждый синглтон, вы также можете использовать его следующим образом:

class UtilProject {
    ....
    companion object {
        fun f() = ...
    }
}

class AnotherClass {
    ...
    companion object {
        const val abc = "ABC"
    }
}

object Singletons {
    val utilProject = UtilProject()
    val anotherClass = AnotherClass()
}

fun main(args: Array<String>) {
    val a = Singletons.utilProject
    val b = Singletons.anotherClass 
}

Таким образом,
object или companion object - это одноэлементный объект в Котлине. Вы можете назначать переменные в объект или объекты, а затем использовать переменные так же, как они были одиночными.

object или companion object создается при первом использовании. val и var в object инициализируются, когда object создается впервые (то есть, когда object используется впервые).

Все ли объекты внутри companion синглтон?

Khemraj Sharma 14.08.2018 08:43

@Khemraj Нет. companion object - это одноэлементный объект. Если вам нужно много разных синглтонов, вы можете создать много объектов для каждого синглтона. Обновлю ответ.

Naetmul 14.08.2018 08:44

Хорошее объяснение !, Тем не менее мне нужен год, чтобы понять синтаксис этого нового языка.

Khemraj Sharma 14.08.2018 11:44

Это не лучший ответ, потому что лучший способ создать класс утилиты - использовать ключевое слово объекта вместо ключевого слова класса - тогда это синглтон уровня языка, и нам не нужно беспокоиться о создании экземпляра такого класса. . Мы просто используем функции так же, как статические методы Java. Пожалуйста, посмотрите мой ответ

dey 21.06.2020 03:38
The companion object is one singleton object. If you need many different singletons, you can create many objects for each singleton. - хе-хе, хорошая шутка, братан
i30mb1 24.11.2020 09:11

А как насчет ИНИЦИАЛИЗАЦИИ синглтона? Если у ojbect не может быть конструктора, следует ли использовать блок инициализатора (init {...})?

Paulo Merson 14.12.2020 20:40

В Котлине есть специальное ключевое слово object для синглтонов. Вы можете просто ввести что-то вроде этого, чтобы получить рабочий одноэлементный класс:

object MySingleton

или когда вам нужны некоторые функции-члены:

object MySingleton {
    fun someFunction(...) {...}
}

А потом используйте это:

MySingleton.someFunction(...)

есть ссылка: https://kotlinlang.org/docs/reference/object-declarations.html#object-declarations

Обновлено:

В вашем случае вам просто нужно заменить в своем определении class UtilProject на это:

object UtilProject {

    // here you put all member functions, values and variables
    // that you need in your singleton Util class, for example:

    val maxValue: Int = 100

    fun compareInts(a: Int, b: Int): Int {...}
}

И тогда вы можете просто использовать свой синглтон в других местах:

UtilProject.compareInts(1, 2)
//or
var value = UtilProject.maxValue

Я этого не понял. Чем это может быть полезно для моего класса Util?

Khemraj Sharma 14.08.2018 09:00

против создания companion object, просто измените декларацию class UtilProject на object UtilProject

dey 14.08.2018 09:16

Нравится ответ @Naetmul?

Khemraj Sharma 14.08.2018 09:18

фактически, использование object вместо class - это ваш синглтон. упомянутая ссылка выше стоит прочитать ;-)

Roland 14.08.2018 09:21

Как мы можем создать одноэлементный (объектный) класс с расширенным базовым классом? Кроме того, базовый класс ожидает некоторого контекста в качестве параметра конструктора? Это возможно?. В моем понимании у объектного класса не будет конструктора.

Nithinjith 26.06.2019 13:42

Насколько я понимаю, вы хотите создать синглтон, который расширяет какой-то другой класс, вы можете просто сделать это, просто сделав это: object MySingleton: BaseClass("some value"), где abstract class BaseClass(val value: String), конечно, ваш BaseClass не должен быть абстрактным.

dey 27.06.2019 11:11

Использование object вместо class - наиболее оптимизированное решение. Если вы хотите вызвать метод внутри своего объекта из класса JAVA, вы можете использовать Singleton.INSTANCE.methodeName()..

Ahmed Nezhi 12.07.2021 12:03

В Kotlin вы должны полностью избавиться от понятия служебного класса singleton. Идиоматический способ - просто переместить все объявления на верхний уровень.

Джава:

public final class Util {
    public static final Util UTIL = new Util();

    private int prefixLength = 4;

    private Util() {}

    public void setPrefixLength(int newLen) {
        prefixLength = newLen;
    }

    public String extractVin(String input) {
        return input.substring(prefixLength);
    }
}

Использование:

String vin = UTIL.extractVin("aoeuVN14134230430")

В Kotlin просто создайте отдельный файл с именем util.kt со следующим:

var prefixLength = 4

fun String.extractVin() = this.substring(prefixLength)

Использование:

val vin = "aoeuVN14134230430".extractVin()

Но ... вы загрязняете пространство имен верхнего уровня!

Если ваша интуиция Java вызывает здесь красный флаг, просто помните, что упаковка - это конструкция пространства имен, и в отличие от Java, Kotlin не объединяет проблемы размещения имен и инкапсуляции. Не существует уровня доступа «частный пакет», поэтому вы свободны от бремени решения, что что-то должно оставаться в том же пакете, чтобы его можно было сделать частным для пакета.

Итак, если в Java вы создаете вырожденный класс в качестве обходного пути, в Kotlin вы просто создаете файл в собственном пакете.

Требуется только слово «объект».

object UtilProject {
    var bar: Int = 0
    fun foo() {        
    }
}

И вы напрямую получаете доступ к объекту, который имеет только один экземпляр

fun main(args: Array<String>) {
    UtilProject.bar = 1
    println(UtilProject.bar)    
}

Пример модернизации Singleton для поддержки вызова API.

object RetrofitClient {

    private var instance: Api? = null
    private val BASE_URL = "https://jsonplaceholder.typicode.com/"

    fun getInstance(): Api? {
        if (instance == null) {
            val retrofit = Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
            instance = retrofit.create(Api::class.java)
        }
        return instance
    }
}

То, как вы реализуете метод getInstance(), неверно. Логика внутри getInstance() говорит, что он всегда будет возвращать значение non-null, но ваш тип возврата (Api?) говорит, что он может возвращать значение null. Если вы попытаетесь преобразовать тип возвращаемого значения в Api, вы получите предупреждение Smart cast to a mutable property is not possible.

Farid 10.12.2019 17:47

Супер простой ленивый пример:

companion object {
    val instance: UtilProject by lazy { UtilProject() }
}

не нужно! просто используйте ключевое слово объекта

roghayeh hosseini 04.03.2020 16:31

Истинный. Собственно, ключевое слово object делает то же самое

Michał Powłoka 05.03.2020 10:26
 class TestMySingleton private constructor() {
​
   companion object {
        var single = TestMySingleton()

        fun getInstance(): TestMySingleton {
            if (single == null)
                single = TestMySingleton()
            return single
        }
    }

}
class MyClass {


    init {
        println("init is called")
    }

    companion object {

        private var obj: MyClass? = null
        fun getInstance(): MyClass {
            if (obj == null) {
                obj = MyClass()
            }
            return obj as MyClass 
        }

    }

    fun printHello() {
        println("Hello World")
    }

Вы можете создать его экземпляр с помощью MyClass.getInstance(), например, java

Это поможет. Я использую класс Dialog, но вы можете использовать этот пример, чтобы понять, как его реализовать.

class MyClass(context: Context) : Dialog(context) {
    companion object {
    lateinit var INSTANCE: MyClass

    @JvmStatic
    fun getInstance(context: Context): MyClass{
        if (!::INSTANCE.isInitialized) {
            INSTANCE = MyClass(context)
        }

        return INSTANCE
    }
}}

Вариант с параметрами

open class SingletonHolder<out T: Any, in A>(creator: (A) -> T) {
    private var creator: ((A) -> T)? = creator
    @Volatile private var instance: T? = null

    fun getInstance(arg: A): T {
        val checkInstance = instance
        if (checkInstance != null) {
            return checkInstance
        }

        return synchronized(this) {
            val checkInstanceAgain = instance
            if (checkInstanceAgain != null) {
                checkInstanceAgain
            } else {
                val created = creator!!(arg)
                instance = created
                creator = null
                created
            }
        }
    }
}


Отличный ответ, но это только часть всего. Этот код скопирован отсюда: blog.mindorks.com/how-to-create-a-singleton-class-in-kotlin Взгляните, чтобы получить полную картину и почему это способ справиться с этим.

C.Schone 20.06.2021 22:29

Другие вопросы по теме