[Обозначение массива 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.