пъзелче

11 отговора 405 прегледа
#1

пъзелче

Даден е следният клас, който вътре в себе си включва свой анонимен наследник. Въпросът е какъв ще бъде изхода от програмата и защо.

public class Twisted {

private final String name;

public Twisted(String name) {
this.name=name;
}

private void printName() {
System.out.println(name);
}

/** ей тука е интересното */
public void doit() {
new Twisted("doit") {
public void f() {
printName();
}
}.f();
}


public static void main(String []ar) {
new Twisted("base").doit();
}

}


при изпълнение на програмата се отпечатва base. Според мен това е защото когато се линква анонимният вътрешен клас неговият printName не е достъпен, f() се връзва към външния printName(). Това обяснение нещо ми се струва криво обаче, вие какво мислите?

Отговор
в отговор на purist #2

Re: пъзелче

Това няма да отпечата "base". Недей литкай хората


All warfare is based on deception.
Sun Tzu [1:18]
Отговор
в отговор на purist #3

Re: пъзелче

Abe otpechatva si 'base'. Tui kato methoda printName e private , naslednicite mu niamat vidimost kum nego. No inner classovete imat dostup do private methodite na enclose-vashtite gi clasove. Zatova se otpechatva i 'base'.
Po interesnia vupros e kakvo da se napravi za da se otechata 'doIt'.
/** ей тука е интересното */
public void doit()
{
new Twisted("doit")
{
public void f()
{
super.printName();
}
}.f();
}
Niakoi ako iska moje da napishe obiasnenie.

Отговор
в отговор на Whitelion #4

Re: пъзелче

отпечатва отпечатва... :-р

Отговор
в отговор на toshe #5

Re: пъзелче

Колеги, не ме правете на луд с тоя "base", щото ще ме карате да го copy/paste-на и да го пробвам


All warfare is based on deception.
Sun Tzu [1:18]
Отговор
в отговор на Whitelion #6

Re: пъзелче

айде де, копи-пействай го! Аз го написах от спомен как изглежда ама сега го пробвах и компилира и тръгва и пише base!

и на тоше солюшъна си пише doit!

Отговор
в отговор на пypиcт #7

Re: пъзелче

Ей сега го копирах и компилирах. Ами doit си пише!


All warfare is based on deception.
Sun Tzu [1:18]
Отговор
в отговор на Whitelion #8

Re: пъзелче

uff, ne che shte mi poviarvash ako ti peistna outputa ama aide:
====================
[zab@localhost zab]$ cat Twisted.java
public class Twisted {

private final String name;

public Twisted(String name) {
this.name=name;
}

private void printName() {
System.out.println(name);
}

/** .. .... . ........... */
public void doit() {
new Twisted("doit") {
public void f() {
printName();
}
}.f();
}


public static void main(String []ar) {
new Twisted("base").doit();
}

}
[zab@localhost zab]$ javac Twisted.java
[zab@localhost zab]$ java Twisted
base
[zab@localhost zab]$


==================
ama aide niakoi independent da potvyrdi

Отговор
в отговор на purist #9

Re: пъзелче

Разбира се, че ти вярвам. Не сме малки деца да се лъжем, но примерчето го тествах с 1.3.1 и 1.4.1_02 под XP. Сега като се прибера в къщи, ще пробвам под Debian да видя, но направо ми се изправя косата, щото ако има разлика...


All warfare is based on deception.
Sun Tzu [1:18]
Отговор
в отговор на toshe #10

Re: пъзелче

Naj-dobre e da se pogledne Java Language Specification ili pyk source-a na samia kompilator.
Sigurno smiatat meteda ot vynshnia class za chast ot konteksta na anonymous class-a, zatova i vinagi go namirat pyrvi. Tochno kakto kogato override-vash metod na base class-a - metoda ot novia class vinagi e po podrazbirane, a tozi ot base class-a se vika sys super.printName()

Taka se poluchava Twisted.this.printName() i se optpechatva "base". (Twisted.this.name = "base")

Ponezhe anonymous class-a ima dostyp do tozi metod kogato se opitash da go izvikash prez super (vypreki che e private) rezultatnia call e Twisted$1.this.printName() i tyj kato Twisted$1.this.name = "doit" rezultata e tova koeto ochakvash ot pyrvata programa

Vyprosa kojto triabva da si zadadesh e dali design-a ti e pravilen shtom se nalaga da pravish podobni hack-ove...

Отговор
в отговор на antijb #11

Re: пъзелче

това силно се съмнявам някой да го употреби в реален дизайн.. освен като параван за този който ти прави code review за да прикриеш някой backdoor друга употреба не виждам.

Отговор
в отговор на purist #12

Re: пъзелче

ууу ... грубичко ...
ето едно обяснение:
Декомпилирам функцията f() до bytecode инструкции (прави се с jad -asm):
public void f()
{
// 0 0:aload_0
// 1 1:getfield #2 <Field Twisted this$0>
// 2 4:invokestatic #3 <Method void Twisted.access$000(Twisted)>
// 3 7:return
}

методът access$000(Twisted t) е изкуствено генериран от компилатора с цел да могат да се викне private метода printName от вътрешен клас (такъв метод се създава за ВСЕКИ викнат от вътрешен клас private метод).
Полето this$0 е също изкуствено генерирано и играе ролята на proxy от вътрешния към външния клас. Ако се вземе през reflection се вижда че стойността му е точно this на "base" (наричам го така условно). Ако са искали да вземат this на "doit" щяха да ползват инструкцията aload_0.

Използвал съм JDK 1.4.2_03 за компилатор и jad 1.5.8e2 за декомпилатор.

Според мен в случая има бъг в компилатора - забравили са частния случай когато вътрешния клас е наследник на външния. Нищо чудно че не винаги се държи така.

Изводите (поне за мен) са два:
1. Да се избягва викането на private методи на външен клас от вътрешни класове - товари се външния клас с изкуствени методи.
2. Да се дава подобна задача е глупост от класа. Не знам кой идиот я е дал, но аз лично не бих желал той после да ми е шеф ...

Отговор