[Обозначение массива L - откуда оно взялось?

Я часто видел сообщения, которые используют [L, затем тип для обозначения массива, например:

[Ljava.lang.Object; cannot be cast to [Ljava.lang.String;

(Вышеприведенный пример я просто вытащил.) Я знаю, что это означает массив, но откуда взялся синтаксис? Почему начало [, но нет закрывающей квадратной скобки? И почему L? Это чисто произвольно или есть какая-то другая историческая/техническая причина?

Ответ 1

[ означает Array, Lsome.type.Here означает тип. Это похожее на дескрипторы типа, используемые внутри в байт-кодеке, как показано в и разделе 4.3 спецификации виртуальной машины Java - выбрано как как можно короче. Единственное отличие состоит в том, что для реальных дескрипторов вместо . используется / для обозначения пакетов.

Например, для примитивов значение: [I для массива int, двумерный массив будет: [[I.

Поскольку классы могут иметь любое имя, было бы труднее определить, какой класс он есть, следовательно, L, имя класса заканчивается на ;

Дескрипторы также используются для представления типов полей и методов.

Например:

(IDLjava/lang/Thread;)Ljava/lang/Object;

... соответствует методу, параметры которого int, double и Thread, а тип возврата Object

изменить

Вы также можете увидеть это в .class файлах с помощью java dissambler

C:>more > S.java
class S {
  Object  hello(int i, double d, long j, Thread t ) {
   return new Object();
  }
}
^C
C:>javac S.java

C:>javap -verbose S
class S extends java.lang.Object
  SourceFile: "S.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method       #2.#12; //  java/lang/Object."<init>":()V
const #2 = class        #13;    //  java/lang/Object
const #3 = class        #14;    //  S
const #4 = Asciz        <init>;
const #5 = Asciz        ()V;
const #6 = Asciz        Code;
const #7 = Asciz        LineNumberTable;
const #8 = Asciz        hello;
const #9 = Asciz        (IDJLjava/lang/Thread;)Ljava/lang/Object;;
const #10 = Asciz       SourceFile;
const #11 = Asciz       S.java;
const #12 = NameAndType #4:#5;//  "<init>":()V
const #13 = Asciz       java/lang/Object;
const #14 = Asciz       S;

{
S();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return
  LineNumberTable:
   line 1: 0


java.lang.Object hello(int, double, long, java.lang.Thread);
  Code:
   Stack=2, Locals=7, Args_size=5
   0:   new     #2; //class java/lang/Object
   3:   dup
   4:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   7:   areturn
  LineNumberTable:
   line 3: 0


}

И в файле raw-класса (смотрите строку 5):

enter image description here

Ссылка: Описание поля в спецификации JVM

Ответ 2

Дескрипторы массива JVM.

[Z = boolean
[B = byte
[S = short
[I = int
[J = long
[F = float
[D = double
[C = char
[L = any non-primitives(Object)

Чтобы получить основной тип данных, вам необходимо:

[Object].getClass().getComponentType();

Он вернет значение null, если "объект" не является массивом. чтобы определить, является ли это массивом, просто вызовите:

[Any Object].getClass().isArray()

или

Class.class.isArray();

Ответ 3

Это используется в JNI (и внутри JVM вообще), чтобы указать тип. Примитивы обозначаются одной буквой (Z для boolean, я для int и т.д.), [ обозначает массив, а L используется для класса (завершается символом ;).

Смотрите здесь: Типы JNI

РЕДАКТИРОВАТЬ: Чтобы выяснить, почему нет завершающего ] - этот код должен позволить JNI/JVM быстро идентифицировать метод и его подпись. Он должен быть как можно более компактным, чтобы сделать синтаксический анализ быстрым (= как можно меньше символов), поэтому [ используется для массива, который довольно прост (какой лучший символ использовать?). I для int одинаково очевидно.

Ответ 4

[Обозначение массива L - откуда оно взялось?

Из спецификации JVM. Это представление имен типов, указанное в формате classFile и других местах.

  • '[' обозначает массив. Фактически, имя типа массива [<typename>, где <typename> - это имя базового типа массива.
  • 'L' на самом деле является частью имени базового типа; например Строка "Ljava.lang.String;". Обратите внимание на trailing ';'!!

И да, нотация задокументирована и в других местах.

Почему?

Нет сомнений в том, что это представление имени внутреннего типа было выбрано, потому что оно:

  • компактный,
  • self-delimiting (это важно для представлений сигнатур методов, и почему они 'L' и конечные ';'), и
  • использует печатные символы (для удобочитаемости... если не читаемость).

Но непонятно, почему они решили разоблачить внутренние типы имен типов массивов с помощью метода Class.getName(). Я думаю, они могли бы сопоставить внутренние имена с чем-то более "дружеским" человеком. Мое лучшее предположение заключается в том, что это была одна из тех вещей, которые они не смогли решить, пока не стало слишком поздно. (Никто не идеален... даже не гипотетический "умный дизайнер".)

Ответ 5

Другим источником для этого будет документация Class.getName(). Конечно, все эти спецификации соответствуют друг другу, так как они сделаны, чтобы соответствовать друг другу.

Ответ 6

Я думаю, это потому, что C был сделан char, поэтому следующая буква в классе L.