Статьи

Примитивно-объектные массивы Котлина

Я изначально намеревался написать этот пост, потому что я играл с некоторым кодом отражения и думал, что нашел что-то интересное. Увы, это точно не тот случай. Вместо этого это была просто базовая функция Kotlin, которую мне еще не нужно было использовать или на которой я должен сосредоточиться. Хотя этот пост оказался не таким, каким я хотел его видеть, я все же думаю, что это хороший небольшой пост, чтобы внести некоторую ясность в эту тему.

В Java есть концепция примитивных типов и их упакованных версий . Благодаря автобоксам и распаковкам типы могут меняться между их примитивными и упакованными версиями. Другими словами, в большинстве ситуаций вы можете использовать long вместо Long или Long вместо long . Если вы не заметили, где в последнем предложении были заглавные буквы, тогда я думаю, что это выглядело довольно странно. Формулировка в этом предложении также имеет решающее значение. Более конкретно, заявление «в большинстве ситуаций».

Автобокс и распаковка не работает при попытке поменять примитивный массив и обернутый ( Object ) массив. Например,

1
2
3
4
5
6
7
8
9
public class PrimitiveArrays {
 
  public static void main(String args[]) {
    Long[] longArray = new Long[] {1L, 2L, 3L};
    takesInPrimitiveLongArray(longArray);
  }
 
  static void takesInPrimitiveLongArray(long[] array) {}
}

Это не работает, и попытка его скомпилировать приводит к следующей ошибке:

1
2
error: incompatible types: Long[] cannot be converted to long[]
    takesInPrimitiveLongArray(longArray);

Переключение метода на Long[] и передача long[] также не скомпилируется по тем же причинам. Это не то, что большинство разработчиков Java найдет интересным, но помогает заложить основу для реального содержания этого поста.

Kotlin должен предоставить вам эквивалент примитивных массивов Java. Но Kotlin не позволяет вам определять массивы, используя тот же синтаксис, что и в Java. В Kotlin инициализация массива выглядит так:

1
2
3
val array = Array<Long>(3)
// or
val array: Array<Long> = arrayOf(1,2,3)

Тот факт, что вы можете видеть, что Array использует обобщенные элементы, должен подчеркнуть, что он не является примитивным массивом. Это факт как в Java, так и в Kotlin, что универсальные типы не могут быть примитивами. В противном случае это можно было бы отключить для Array<long> , и мы все были бы счастливы. Приведенный выше код компилируется в массив объектов Long[] вместо примитива long[] .

Эта ситуация несколько уникальна для массивов. Сам по себе Kotlin Long может компилироваться в байт-код JVM либо в Long либо в Long . Скомпилированный тип зависит от обнуляемости поля. Массивы более явные, поэтому их типы не изменятся при компиляции.

Чтобы обойти это, Kotlin предоставляет набор классов, которые становятся примитивными массивами при компиляции в байт-код JVM.

Эти классы включают в себя:

Котлин Ява
ByteArray байт[]
CharArray символ []
ShortArray короткая[]
INTArray ИНТ []
LongArray долго[]
DoubleArray двойной []
FloatArray плавать []
BooleanArray логическое []

Есть также дополнительные классы для массивов беззнаковых типов.

Эти классы также можно обменивать между Kotlin и Java без каких-либо дополнительных усилий.

В качестве последнего доказательства, показывающего различия между массивами примитивов и массивов / объектов в Kotlin, я хочу показать вам некоторый код Kotlin, который преобразуется в его Java-аналог:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
@file:JvmName("PrimitiveArrays")
package dev.lankydan
 
fun main(args: Array<String>) {
  // long and Long arrays
  val longArray = longArrayOf(1,2,3,4)
  val arrayOfLongs = arrayOf<Long>(1,2,3,4)
  // int and Integer arrays
  val intArray = intArrayOf(1,2,3,4)
  val arrayOfInts = arrayOf<Int>(1,2,3,4)
  // boolean and Boolean arrays
  val booleanArray = booleanArrayOf(true, false)
  val arrayOfBooleans = arrayOf<Boolean>(true, false)
  // char and Character arrays
  val charArray = charArrayOf('a','b','c')
  val arrayOfChars = arrayOf<Char>('a', 'b', 'c')
}

Используя декомпилятор байт-кода Intellij Kotlin, фрагмент декомпилируется для:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public final class PrimitiveArrays {
   public static final void main(String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      // long and Long arrays
      long[] var10000 = new long[]{1L, 2L, 3L, 4L};
      Long[] var9 = new Long[]{1L, 2L, 3L, 4L};
      // int and Integer arrays
      int[] var10 = new int[]{1, 2, 3, 4};
      Integer[] var11 = new Integer[]{1, 2, 3, 4};
      // boolean and Boolean arrays
      boolean[] var12 = new boolean[]{true, false};
      Boolean[] var13 = new Boolean[]{true, false};
      // char and Character arrays
      char[] var14 = new char[]{'a', 'b', 'c'};
      Character[] var15 = new Character[]{'a', 'b', 'c'};
   }
}

Во-первых, обратите внимание, что Kotlin предоставляет вам полезные функции инициализации для ваших массивов. Как для примитивных, так и для объектных массивов. Во-вторых, как они составлены. Например, LongArray становится long[] а Array<Long> становится Long[] .

Теперь вы можете увидеть различия между этими массивами. Но я не упомянул, какие из них вы должны использовать. Вы должны откладывать примитивные типы так же, как это делает Java. Это связано с влиянием на производительность, которое могут иметь автобокс и распаковка для вашего приложения.

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

Если вам нужно хранить пустые значения в ваших массивах, вам все равно придется обращаться к массиву с оболочкой / объектом. В большинстве ситуаций, я думаю, вы должны быть в состоянии использовать примитивные массивы, но всегда будут времена, когда вы не сможете. При этом, как правило, мы все просто используем List s, поэтому ничего из этого не имеет значения.

Теперь вы должны лучше понимать различия между примитивными массивами, такими как LongArray и объектными массивами, такими как Array<Long> . Если нет, то я подвел тебя, и я прошу прощения за это.

Опубликовано на Java Code Geeks с разрешения Дэна Ньютона, партнера нашей программы JCG . Смотреть оригинальную статью здесь: примитивные и объектные массивы Kotlin

Мнения, высказанные участниками Java Code Geeks, являются их собственными.