
Здравствуйте, мои маленькие любители поебатися с байткодом. Сегодня я вам расскажу о crucial difference различных access specifiers при объявлении мемберов inner-классов.
Слово в сторону: любой порядочный параноик объявит мембер своего класса как private
, а не как либо ещё. Крайне ленивый раздолбай вообще access specifier не напишет. И вот тут-то и зарыта собака.
Возьмём вот такой класс с двумя inner-классами:
public class Main {
class Inner {
int field;
}
class Pinner {
private int field;
}
int test(final Inner obj) {
return obj.field;
}
int test(final Pinner obj) {
return obj.field;
}
}
Ничег сложного, просто два inner-класса, у одного поле int
, у другого — private int
. Ну и из двух разных функций обращаемся к этим полям.
Скомпилим и посмотрим на байткод:
$ javac Main.java
$ javap -p -c Main.class
Compiled from "Main.java"
public class Main {
public Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
int test(Main$Inner);
Code:
0: aload_1
1: getfield #2 // Field Main$Inner.field:I
4: ireturn
int test(Main$Pinner);
Code:
0: aload_1
1: invokestatic #3 // Method Main$Pinner.access$000:(LMain$Pinner;)I
4: ireturn
}
Ну ничего себе! Было private int
простое, а стало золотое — вместо банального getfield
получаем invokestatic
, который...
$ javap -p -c Main\$Pinner.class
Compiled from "Main.java"
class Main$Pinner {
private int field;
final Main this$0;
Main$Pinner(Main);
Code:
0: aload_0
1: aload_1
2: putfield #2 // Field this$0:LMain;
5: aload_0
6: invokespecial #3 // Method java/lang/Object."<init>":()V
9: return
static int access$000(Main$Pinner);
Code:
0: aload_0
1: getfield #1 // Field field:I
4: ireturn
}
... внутри уже сам делает getfield
, т.е. на ровном месте получили лишний вызов функции. А ещё когда вы сделаете что-то вроде:
Pinner pinner = null;
return pinner.field;
То в стектрейсе NullPointerException вы получите лишний фрейм с этим самым Main$Pinner.access$000
, который неопытного программиста просто вводит в ступор.
В общем, я сейчас борюсь с желанием повсюду в коде поудалять все private
.