Переменные, имеющие одно и то же имя, но разные типы
Я прочитал здесь, что в Java возможно, что две переменные, имеющие одно и то же имя, но разные типы, будут сосуществовать в одной области. Я имею в виду это
class test
{
private int x;
private double x;
}
Но все java IDE не допускают такой код. Я хочу знать, действительно ли такой код является синтаксически правильным, или просто IDE не позволяет этому коду предотвращать неоднозначность.
В любом случае вот выдержка из сайта
"Если вам повезет, вы можете перекомпилировать вывод из Jad. Однако у виртуальной машины Java есть более мягкие правила для именования переменных, чем сам язык Java. Для экземпляров действительный файл класса может имеют несколько переменных с именем" a", если у них разные типы. Если вы декомпилируете такой класс, исходный код, который вы получите, будет недействительным.
JAD обычно переименовывает оскорбительные поля и делает перекомпилируемый файл... единственная проблема заключается в том, что перекомпилированный файл не будет совместим с исходными классами.
Ответы
Ответ 1
Вы не можете иметь переменные с таким же именем (но другим типом), чтобы существовать в той же области.
Подумайте, возможно ли тогда, как java-компилятор определит, что вы имели в виду.
Рассмотрим этот фрагмент кода
class test
{
private int x;
private double x;
test() //constructor
{
System.out.println(x); //Error cannot determine which x you meant
}
}
Компилятор java не может понять, на какой x вы на самом деле ссылаетесь. Таким образом, такой код не является синтаксически правильным и не компилируемым.
Однако существуют такие инструменты, как ClassEditor, которые могут изменять сгенерированный файл класса после его создания. Там можно изменить имя двух переменных на одно и то же.
Однако такой класс не обязательно запускается java jvm.
Программное обеспечение, которое вы указали i.e JAD
, может переименовать такие повторяющиеся именованные переменные в файле класса, чтобы исходный код, который вы получите, действительно будет синтаксически правильным.
Ответ 2
Как говорили другие, незаконным на Java, но законным в байт-коде.
javac assert
assert
- пример Java, который в Oracle JDK 1.8.0_45 генерирует несколько полей с тем же именем, но с разными типами. Например:.
public class Assert {
// We can't use a primitive like int here or it would get inlined.
static final int[] $assertionsDisabled = new int[0];
public static void main(String[] args) {
System.out.println($assertionsDisabled.length);
// currentTimeMillis so it won't get optimized away.
assert System.currentTimeMillis() == 0L;
}
}
Присутствие assert
генерирует синтетическое поле bool $assertionsDisable
для кэширования вызова метода, см. fooobar.com/questions/16319/....
Тогда:
javac Assert.java
javap -c -constants -private -verbose Assert.class
содержит строки:
#3 = Fieldref #9.#28 // Assert.$assertionsDisabled:[I
#5 = Fieldref #9.#31 // Assert.$assertionsDisabled:Z
#12 = Utf8 $assertionsDisabled
#28 = NameAndType #12:#13 // $assertionsDisabled:[I
#31 = NameAndType #12:#14 // $assertionsDisabled:Z
public static void main(java.lang.String[]);
3: getstatic #3 // Field $assertionsDisabled:[I
10: getstatic #5 // Field $assertionsDisabled:Z
Обратите внимание, что таблица констант даже повторно использует #12
как имя переменной.
Если бы мы объявили еще одно логическое значение, оно не скомпилировалось бы:
static final boolean $assertionsDisabled = false;
с ошибкой:
the symbol $assertionsDisabled conflicts with a compile synthesized symbol
Вот почему очень плохое использование имен полей со знаками доллара: Когда я должен использовать символ доллара ($) в имени переменной?
жасмин
Конечно, мы также можем попробовать его с помощью Jasmin:
.class public FieldOverload
.super java/lang/Object
.field static f I
.field static f F
.method public static main([Ljava/lang/String;)V
.limit stack 2
ldc 1
putstatic FieldOverload/f I
ldc 1.5
putstatic FieldOverload/f F
getstatic java/lang/System/out Ljava/io/PrintStream;
getstatic FieldOverload/f I
invokevirtual java/io/PrintStream/println(I)V
getstatic java/lang/System/out Ljava/io/PrintStream;
getstatic FieldOverload/f F
invokevirtual java/io/PrintStream/println(F)V
return
.end method
который содержит два статических поля: один int
(I
) и один float
(F
), и выводит:
1
1.5
Если работает, потому что:
-
getstatic
указывает на структуру Fieldref
в таблице констант
-
Fieldref
указывает на NameAndType
-
NameAndType
указывает на тип, очевидно
Итак, чтобы различать их, Jasmin просто использует два разных Fieldref
с разными типами.
Ответ 3
В соответствии со спецификацией языка (JLS 8.3):
Это ошибка времени компиляции для тела объявления класса для объявления двух полей с тем же именем.
Заявление, которое вы указали, касается файла класса (то есть скомпилированного файла, а не исходного кода).
Ответ 4
Конечно, у вас не может быть int x и long x полей в одном классе, так же как вы не можете иметь два метода с тем же именем и списком параметров. Но это на уровне источника. JVM и байт-код имеют разные правила. Рассмотрим это:
пакетный тест;
public class Test {
static int x;
@Override
protected Test clone() throws CloneNotSupportedException {
return this;
}
public static void main(String[] args) {
int y = x;
}
}
Если вы используете инструмент построения байт-кода (я использовал плагин Андрея Лоскутова для Eclipse), вы увидите в этой строке int Test.main()
GETSTATIC test/Test.x : I
Так JVM загружает значение из поля x, его полное имя - "test/Test.x: I". Это дает вам подсказку о том, что значение поля присутствует в полном имени поля.
Не секрет, что javac - это не единственное средство для создания класса, есть инструменты/библиотеки для создания байт-кода напрямую, и они могут создавать классы с полями с тем же именем, но с другим типом. То же самое касается JNI.
Трудно показать рабочий пример того, что я пытаюсь доказать. Но рассмотрим методы, аналогичную проблему. Bytecode analisys показывает, что в классе Test есть два метода с тем же именем и параметрами, которые не разрешены JLS:
protected clone()Ltest/Test; throws java/lang/CloneNotSupportedException
protected volatile bridge clone()Ljava/lang/Object; throws java/lang/CloneNotSupportedException
Метод "bridge" был добавлен javac, потому что Test.clone() возвращает Test, и это означает, что он не переопределяет Object.clone(), который возвращает Object, и это связано с тем, что JVM считает, что эти два являются разными методами.
1 clone()Ltest/Test;
2 clone()Ljava/lang/Object;