Рубрики
Без рубрики

Котлин – Дженерики и различия в типах

Программирование – это все, что связано с абстракциями. Вместо того, чтобы инструктировать машины двоичными файлами (1 и 0), w… Помечен как kotlin, java.

Программирование – это все, что связано с абстракциями.

Вместо того, чтобы инструктировать машины двоичными файлами (1 и 0), мы создали абстракции более высокого уровня. Эти языки более высокого уровня (абстракции) облегчают нам инструктирование машин.

Во время выполнения машины должны выделять и освобождать память. Мы добавили типы в абстракции для управления тем, как распределять и освобождать память.

С Типами , его легко читать, понимать и отлаживать. Типы позволяют компиляторам выделять нужную память и гарантировать отсутствие неожиданностей во время выполнения.

Затем мы создали статически типизированные языки. В статически типизированном языке компиляторы заставляют вас иметь конкретные типы (ссылочные или примитивные) в (почти) каждом выражении.

Примечание: Примитивные типы – это такие типы, как Int, Float, Character и т.д., А ссылочные типы – это те, которые созданы вами.

✨ Типы потрясающие , верно ✨

Но что, если мне нужно реализовать Список как для целого числа , так и для Строка ?

Вы можете создать две отдельные реализации для обоих Целых чисел и Строка . Но это создает избыточный код, который сложнее поддерживать и отлаживать.

Дженерики помогут вам здесь.

Универсальное программирование – это стиль компьютерного программирования, в котором алгоритмы написаны в терминах типов, которые будут определены позже, которые затем создаются при необходимости для определенных типов, предоставляемых в качестве параметров. – Википедия

Дженерики добавляют еще один уровень абстракции. С помощью дженериков мы можем написать фрагмент кода, который является общим для различных типов.

Дженерики не позволяют нам писать повторяющийся код для каждого отдельного типа , который нам нужно реализовать. Мы предоставляем компилятору некоторую информацию об общем типе. Компилятор (или) среда выполнения сделает все остальное.

В некоторых языках, таких как C++, компилятор расширит общий код необходимой информацией о типе. Эта генерация кода происходит во время компиляции.

Например, если вы определили класс Список с универсальным типом T и список использованных для Строка и Целое число . Затем компилятор сгенерирует отдельный класс для обеих Строк и Целое число .

Но почему Тот ? T – это общий способ определения типа. Вы вполне можете использовать там любую букву. О! эй, T для типа .

✅ Сгенерированный код будет иметь более высокую производительность( ⚡️ ), потому что среда выполнения точно знает, чего ожидать и как работать с типами.

✅ Среде выполнения нет необходимости распаковывать значение при его использовании и вставлять его при возврате.

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

С другой стороны, в языках JVM компилятор полностью стирает информацию о типе.

Механизм, который использует Java, называется Стирание типов .

Подробнее об удалении типов читайте здесь

Компиляторы создадут класс без информации о типе в нем. Затем JVM во время выполнения использует приведение для получения и установки значения.

Таким образом, один и тот же класс используется для обеих Строк и Целое число .

✅ Это приводит к меньшему раздуванию кода и обратная совместимость .

💥 С другой стороны , это приводит к загрязнению кучи и меньшая производительность .

Чтобы справиться с этим, JVM предоставляет гарантию чугуна при определении универсального. Таким образом, дженерики в Java являются инвариантными по умолчанию.

Предположим, что Класс Animal является Супертип из класса Собака .

open class Animal

class Dog: Animal()

Примечание: для расширения класса Animal нам нужно открыть класс животных. В Kotlin классы по умолчанию являются окончательными.

interface List {
    fun getAll(): List
}

Затем у нас есть общий Список определенный интерфейс. Интерфейс имеет один метод GetAll .

Теперь давайте создадим Список животных и Список классов, реализующих определенный универсальный интерфейс.

class AnimalList: List {
    override fun getAll(): List {
        TODO("not implemented")
    }
}

class DogList: List {
    override fun getAll(): List {
        TODO("not implemented")
    }
}

Ковариация без

Даже несмотря на то, что Animal является родительским классом Dog , Список<Животное> не является родительским классом Список<Собака> .

fun main() {
    val dogList = DogList()
    val d: List = dogList.getAll() // 💥 Type mismatch
}

Компилятор не позволит нам использовать Подтип вместо суперТипа . То есть типы Животное и Собака не являются ковариантными .

В Kotlin мы можем сообщить компилятору, чтобы он принял Подтип вместо суперТипа с out ключевое слово.

interface List {
    fun getAll(): List
}

Выход из ключевое слово сообщает компилятору, что Животное и Собака являются ковариантными типами, и их можно использовать взаимозаменяемо.

Теперь это сработает,

fun main() {
    val dogList = DogList()
    val d: List = dogList.getAll() // ✅
}

Используйте out для неизменяемых типов, чтобы избежать неприятных ошибок во время выполнения.

Контравариантность внутри

Контравариантность противоположна Ковариации . Контравариантность позволяет нам использовать Супертип вместо подтипа .

Рассмотрим следующий класс Инвентаризация . Класс инвентаря принимает товар типа T .

class Inventory (item: T) 

У нас есть Магазин в котором хранится Инвентаризация .

class Shop (items: Inventory) 

Учтите, что у нас есть два магазина: Магазин для животных и Магазин для собак. Затем по какой-то причине магазин животных решает продать всех своих собак в магазин собак.

Но собаки в Зоомагазине все еще помечены внутри Инвентарь<Животное> .

Если мы захотим Магазин<Собака> для приема всех Животных из из

fun main() {
    val animal = Animal()
    val dog = Dog()

    val animalList = Inventory(a)
    val dogShop = Shop(animalList) // 💥 Type error
}    

из Магазина<Животное> , тогда , но мы пытаемся использовать

из Магазина<Животное> , тогда , но мы пытаемся использовать Супертип вместо

class Inventory (item: T) 

из Магазина<Животное> , тогда , но мы пытаемся использовать Супертип вместо подтипа , компилятор выдает ошибку типа. Мы можем дать указание компилятору принять

|||| из || Магазина<Животное> || , тогда , но мы пытаемся использовать || Супертип || вместо || подтипа || , компилятор выдает ошибку типа. Мы можем дать указание компилятору принять || Супертип || вместо || подтипа || с ключевым словом || в || в определении типа. Об этом сообщит компилятор (т.е. || Животное || вместо || Собаки || ). для использования супертипа |||| Хорошая цитата, которая может помочь запомнить эти правила: будьте либеральны в том, что вы принимаете, и консервативны в том, что вы производите. на месте || подтипа ||

|||| из || Магазина<Животное> || , тогда , но мы пытаемся использовать || Супертип || вместо || подтипа || , компилятор выдает ошибку типа. Мы можем дать указание компилятору принять || Супертип || вместо || подтипа || с ключевым словом || в || в определении типа. Об этом сообщит компилятор (т.е. || Животное || вместо || Собаки || ). для использования супертипа |||| Хорошая цитата, которая может помочь запомнить эти правила: будьте либеральны в том, что вы принимаете, и консервативны в том, что вы производите. на месте || подтипа ||

Тип , который мы передаем , может быть либо подтипом , Супертип .

Существуют следующие типы дисперсии типов в целом:

  1. Ковариантный – это позволяет использовать Супертип вместо подтипа .
  2. Контравариантный – это позволяет использовать Подтип вместо суперТипа .
  3. Двумерный – ковариантный и контравариантный.
  4. Инвариантный – ни ковариантный, ни контравариантный.

Ознакомьтесь с этим сообщением о том, как создать приложение с полным стеком с помощью Kotlin, React и Spring Boot с помощью Hipster здесь.

Интересно, что такое хипстер – посмотрите здесь .

Вы можете следовать за мной по Твиттер .

Если вам понравилась эта статья, пожалуйста, оставьте лайк или комментарий. ❤ ️

Заинтересованы в дальнейшем изучении:

Оригинал: “https://dev.to/sendilkumarn/kotlin-generics-type-variance-9ee”