tifyty

pure Java, what else ?

Zártosztály

Java-ban nem programoznak bolondok, ezért ott ismeretlen fogalom a zártosztály (closure). Bár ha a Java programozás és a későbbi megbolondulás közötti korrelációt nézzük…

Ellenben vannak olyan osztályok, amik másik osztályon belül vannak. Sőt nem csak osztályon, hanem osztály metóduson belül is lehetnek további belső osztályok. Például a következő:

public class WhyFinal {
	interface Outer {
		String method();
	}

	public Outer method(final String s) {
		class Inner implements Outer {
			public String method() {
				return s + " wonka";
			}
		}
		return new Inner();
	}

	public static void main(String[] args) {
		System.out.println(new WhyFinal().method("bonka").method());
	}
}

Ami ebben a posztban igazából érdekel engem az az, hogy miért kell a final kulcsszó a method metódus String argumentuma elé.

Ez az s nevű változó különleges, nem is nagyon látunk máshol hasonlót Java-ban. Elérhető az Inner osztályon belülről, pedig az osztályban nincs deklarálva. Ez már önmagában is különleges, de még inkább az, hogy ez a változó megszűnik, amikor a method() visszatér. Azonban a létrehozott Inner példány tovább él, és eléri ezt a változót. Ami már megszűnt. Vagy nem?

Valójában nem ez történik. A fenti program a következő programmal egyenértékű:

public class WhyFinal {
	interface Outer {
		String method();
	}

	public Outer method(final String s) {
		class Inner implements Outer {
			private String s;
			public Inner(String s){
				this.s = s;
			}
			public String method() {
				return s + " wonka";
			}
		}
		return new Inner(s);
	}

	public static void main(String[] args) {
		System.out.println(new WhyFinal().method("bonka").method());
	}
}

Itt nem a külső method metódus lokális változójára hivatkozunk a belső osztályban, hanem explicit módon átadjuk az s változóértékét a konstruktorban, amelyik azt jól elteszi egy mezőbe, és onnan kezdve azt használja a program.

Ebben az esetben final kulcsszót akár ki is törölhetjük a method(final String s) argumentumból, hiszen explicit módon megmondtuk, hogy amikor létrehozzuk az Inner objektumot, akkor adjuk át az s értékét, és ha utána változik az s ahhoz az Inner példánynak semmi köze. Ha azonban az eredeti példánál maradunk, akkor ez a paraméter átadás nincs explicite kiírva, és annak ellenére, hogy valójában ez történik, a kód úgy néz ki, mintha a belső osztályunk a metódus lokális s változóját használná.

Nézzük meg az eredeti, tehát default Inner konstruktoros példa bájt kódját:

class WhyFinal$1Inner implements WhyFinal$Outer
  SourceFile: "WhyFinal.java"
  EnclosingMethod: #27.#28                // WhyFinal.method
  InnerClasses:
       #37= #9; //Inner=class WhyFinal$1Inner
       static #41= #11 of #27; //Outer=class WhyFinal$Outer of class WhyFinal
  minor version: 0
  major version: 51
  flags: ACC_SUPER
Constant pool:
   #1 = Fieldref           #9.#29         //  WhyFinal$1Inner.this$0:LWhyFinal;
   #2 = Fieldref           #9.#30         //  WhyFinal$1Inner.val$s:Ljava/lang/String;
   #3 = Methodref          #10.#31        //  java/lang/Object."<init>":()V
   #4 = Class              #32            //  java/lang/StringBuilder
   #5 = Methodref          #4.#31         //  java/lang/StringBuilder."<init>":()V
   #6 = Methodref          #4.#33         //  java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #7 = String             #34            //   wonka
   #8 = Methodref          #4.#35         //  java/lang/StringBuilder.toString:()Ljava/lang/String;
   #9 = Class              #36            //  WhyFinal$1Inner
  #10 = Class              #39            //  java/lang/Object
  #11 = Class              #40            //  WhyFinal$Outer
  #12 = Utf8               val$s
  #13 = Utf8               Ljava/lang/String;
  #14 = Utf8               this$0
  #15 = Utf8               LWhyFinal;
  #16 = Utf8               <init>
  #17 = Utf8               (LWhyFinal;Ljava/lang/String;)V
  #18 = Utf8               Code
  #19 = Utf8               LineNumberTable
  #20 = Utf8               Signature
  #21 = Utf8               ()V
  #22 = Utf8               method
  #23 = Utf8               ()Ljava/lang/String;
  #24 = Utf8               SourceFile
  #25 = Utf8               WhyFinal.java
  #26 = Utf8               EnclosingMethod
  #27 = Class              #42            //  WhyFinal
  #28 = NameAndType        #22:#43        //  method:(Ljava/lang/String;)LWhyFinal$Outer;
  #29 = NameAndType        #14:#15        //  this$0:LWhyFinal;
  #30 = NameAndType        #12:#13        //  val$s:Ljava/lang/String;
  #31 = NameAndType        #16:#21        //  "<init>":()V
  #32 = Utf8               java/lang/StringBuilder
  #33 = NameAndType        #44:#45        //  append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #34 = Utf8                wonka
  #35 = NameAndType        #46:#23        //  toString:()Ljava/lang/String;
  #36 = Utf8               WhyFinal$1Inner
  #37 = Utf8               Inner
  #38 = Utf8               InnerClasses
  #39 = Utf8               java/lang/Object
  #40 = Utf8               WhyFinal$Outer
  #41 = Utf8               Outer
  #42 = Utf8               WhyFinal
  #43 = Utf8               (Ljava/lang/String;)LWhyFinal$Outer;
  #44 = Utf8               append
  #45 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #46 = Utf8               toString
{
  final java.lang.String val$s;
    flags: ACC_FINAL, ACC_SYNTHETIC


  final WhyFinal this$0;
    flags: ACC_FINAL, ACC_SYNTHETIC


  WhyFinal$1Inner();
    flags: 
    Code:
      stack=2, locals=3, args_size=3
         0: aload_0       
         1: aload_1       
         2: putfield      #1                  // Field this$0:LWhyFinal;
         5: aload_0       
         6: aload_2       
         7: putfield      #2                  // Field val$s:Ljava/lang/String;
        10: aload_0       
        11: invokespecial #3                  // Method java/lang/Object."<init>":()V
        14: return        
      LineNumberTable:
        line 7: 0
    Signature: #21                          // ()V

  public java.lang.String method();
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: new           #4                  // class java/lang/StringBuilder
         3: dup           
         4: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
         7: aload_0       
         8: getfield      #2                  // Field val$s:Ljava/lang/String;
        11: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        14: ldc           #7                  // String  wonka
        16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        22: areturn       
      LineNumberTable:
        line 9: 0
}

Amit érdemes megnézni, az az, hogy a 12-es sorban deklarál a kód egy WhyFinal$1Inner.val$s nevű és String típusú változót. A belső osztály ezt fogja használni, és nem a külső method
lokális a változóját.

A következő a 70. sornál kezdődik. Itt az Inner konstruktora, amelyik ugye default konstruktor, és ezért nincs argumentuma, két argumentumot is elpakol mezőkbe, amiket szintén nem deklaráltunk. Az első a külső osztály “this” értéke, azaz maga a külső osztály, hiszen az Inner nem statikus, tehát mindig meg kell, hogy legyen a WhyFinal példány, ami őt létrehozta, és el is érhető Java szintről a WhyFinal.this hivatkozással. A másik pedig az s amit a WhyFinal$1Inner.val$s névvel illet a fordító.

Amikor pedig a 91-es sorban az s értékéhez akar hozzáférni a program, akkor ehhez a változóhoz nyúl, hiszen a lokális változó ekkor már régen a szemétgyűjtőé.

2 responses to “Zártosztály

  1. Visszajelzés:final, fin ül « tifyty

  2. Visszajelzés:Tudásteszt multi thread, 2 megoldás | tifyty

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s

%d blogger ezt kedveli: