영어로 읽는 코딩

52_[자바] inner class 01

Creating inner classes

You create an inner class just as you’d expect—by placing the class definition inside a surrounding class:

//: innerclasses/Parcel1.java
// Creating inner classes.
public class Parcel1 {
    class Contents {
		private int i = 11;
		public int value() { return i; }
	}
	class Destination {
		private String label;
		Destination(String whereTo) {
		label = whereTo;
		}
	String readLabel() { return label; }
	}
	// Using inner classes looks just like
	// using any other class, within Parcel1:
	public void ship(String dest) {
		Contents c = new Contents();
		Destination d = new Destination(dest);
			System.out.println(d.readLabel());
	}
	public static void main(String[] args) {
		Parcel1 p = new Parcel1();
		p.ship("Tasmania");
	}
} /* Output:
Tasmania
*///:~

 

The inner classes used inside ship( ) look just like ordinary classes. Here, the only practical difference is that the names are nested within Parceli. You’ll see in a while that this isn’t the only difference. More typically, an outer class will have a method that returns a reference to an inner class, as you can see in the to( ) and contents( ) methods:

//: innerclasses/Parcel2.java
// Returning a reference to an inner class.
public class Parcel2 {
    class Contents {
		private int i = 11;
		public int value() { return i; }
	}
	class Destination {
		private String label;
		Destination(String whereTo) {
			label = whereTo;
		}
		String readLabel() { return label; }
	}
	public Destination to(String s) {
		return new Destination(s);
	}
	public Contents contents() {
		return new Contents();
	}
	public void ship(String dest) {
		Contents c = contents();
		Destination d = to(dest);
	System.out.println(d.readLabel());
	}
	public static void main(String[] args) {
		Parcel2 p = new Parcel2();
		p.ship("Tasmania");
		Parcel2 q = new Parcel2();
	// Defining references to inner classes:
		Parcel2.Contents c = q.contents();
		Parcel2.Destination d = q.to("Borneo");
	}
} /* Output:
Tasmania
*///:~

 

If you want to make an object of the inner class anywhere except from within a non-static method of the outer class, you must specify the type of that object as OuterClassName.InnerClassName, as seen in main( ).

 

The link to the outer class

So far, it appears that inner classes are just a name-hiding and code organization scheme, which is helpful but not totally compelling. However, there’s another twist. When you create an inner class, an object of that inner class has a link to the enclosing object that made it, and so it can access the members of that enclosing object—without any special qualifications. In addition, inner classes have access rights to all the elements in the enclosing class.1 The following example demonstrates this:

//: innerclasses/Sequence.java
// Holds a sequence of Objects.
interface Selector {
    boolean end();
	Object current();
	void next();
}
public class Sequence {
	private Object[] items;
	private int next = 0;
	public Sequence(int size) { items = new Object[size]; }
	public void add(Object x) {
		if(next < items.length)
		items[next++] = x;
	}
	private class SequenceSelector implements Selector {
		private int i = 0;
		public boolean end() { return i == items.length; }
		public Object current() { return items[i]; }
		public void next() { if(i < items.length) i++; }
	}
	public Selector selector() {
		return new SequenceSelector();
	}
	public static void main(String[] args) {
		Sequence sequence = new Sequence(10);
		for(int i = 0; i < 10; i++)
			sequence.add(Integer.toString(i));
		Selector selector = sequence.selector();
		while(!selector.end()) {
			System.out.print(selector.current() + " ");
			selector.next();
		}
	}
} /* Output:
0 1 2 3 4 5 6 7 8 9
*///:~

 

 

The Sequence is simply a fixed-sized array of Object with a class wrapped around it. You call add( ) to add a new Object to the end of the sequence (if there’s room left). To fetch each of the objects in a Sequence, there’s an interface called Selector. This is an example of the Iterator design pattern that you shall learn more about later in the book. A Selector allows you to see if you’re at the end( ), to access the current( ) Object, and to move to the next( ) Object in the Sequence. Because Selector is an interface, other classes can implement the interface in their own ways, and other methods can take the interface as an argument, in order to create more general-purpose code.

Here, the SequenceSelector is a private class that provides Selector functionality. In main( ), you can see the creation of a Sequence, followed by the addition of a number of String objects. Then, a Selector is produced with a call to selector( ), and this is used to move through the Sequence and select each item.

At first, the creation of SequenceSelector looks like just another inner class. But examine it closely. Note that each of the methods—end( ), current( ), and next( )—refers to items, which is a reference that isn’t part of SequenceSelector, but is instead a private field in the enclosing class. However, the inner class can access methods and fields from the enclosing class as if it owned them. This turns out to be very convenient, as you can see in the preceding example.

So an inner class has automatic access to the members of the enclosing class. How can this happen? The inner class secretly captures a reference to the particular object of the enclosing class that was responsible for creating it. Then, when you refer to a member of the enclosing class, that reference is used to select that member. Fortunately, the compiler takes care of all these details for you, but now you can see that an object of an inner class can be created only in association with an object of the enclosing class (when, as you shall see, the inner class is non-static). Construction of the inner-class object requires the reference to the object of the enclosing class, and the compiler will complain if it cannot access that reference. Most of the time this occurs without any intervention on the part of the programmer.

 

Using .this and .new

If you need to produce the reference to the outer-class object, you name the outer class followed by a dot and this. The resulting reference is automatically the correct type, which is known and checked at compile time, so there is no runtime overhead. Here’s an example that shows how to use .this:

//: innerclasses/DotThis.java
// Qualifying access to the outer-class object.
public class DotThis {
    void f() { System.out.println("DotThis.f()"); }
	public class Inner {
		public DotThis outer() {
			return DotThis.this;
			// A plain "this" would be Inner’s "this"
		}
	}
	public Inner inner() { return new Inner(); }
	public static void main(String[] args) {
		DotThis dt = new DotThis();
		DotThis.Inner dti = dt.inner();
		dti.outer().f();
	}
} /* Output:
DotThis.f()
*///:~

 

Sometimes you want to tell some other object to create an object of one of its inner classes. To do this you must provide a reference to the other outer-class object in the new expression, using the .new syntax, like this:

//: innerclasses/DotNew.java
// Creating an inner class directly using the .new syntax.
public class DotNew {
    public class Inner {}
	public static void main(String[] args) {
		DotNew dn = new DotNew();
		DotNew.Inner dni = dn.new Inner();
	}
} ///:~

 

To create an object of the inner class directly, you don’t follow the same form and refer to the outer class name DotNew as you might expect, but instead you must use an object of the outer class to make an object of the inner class, as you can see above. This also resolves the name scoping issues for the inner class, so you don’t say (indeed, you can’t say) dn.new DotNew.Inner( ).

It’s not possible to create an object of the inner class unless you already have an object of the outer class. This is because the object of the inner class is quietly connected to the object of the outer class that it was made from. However, if you make a nested class (a static inner class), then it doesn’t need a reference to the outer-class object.

Here, you see the use of .new applied to the "Parcel" example:

//: innerclasses/Parcel3.java
// Using .new to create instances of inner classes.
public class Parcel3 {
    class Contents {
		private int i = 11;
		public int value() { return i; }
	}
	class Destination {
		private String label;
		Destination(String whereTo) { label = whereTo; }
		String readLabel() { return label; }
	}
	public static void main(String[] args) {
		Parcel3 p = new Parcel3();
		// Must use instance of outer class
		// to create an instance of the inner class:
		Parcel3.Contents c = p.new Contents();
		Parcel3.Destination d = p.new Destination("Tasmania");
	}
} ///:~

 

 

Inner classes and upcasting

Inner classes really come into their own when you start upcasting to a base class, and in particular to an interface. (The effect of producing an interface reference from an object that implements it is essentially the same as upcasting to a base class.) That’s because the inner class — the implementation of the interface — can then be unseen and unavailable, which is convenient for hiding the implementation. All you get back is a reference to the base class or the interface.

We can create interfaces for the previous examples:

//: innerclasses/Destination.java 
public interface Destination { 
    String readLabel(); 
} ///:~ 

 

Now Contents and Destination represent interfaces available to the client programmer. Remember that an interface automatically makes all of its members public.

When you get a reference to the base class or the interface, it’s possible that you can’t even find out the exact type, as shown here:

//: innerclasses/TestParcel.java 
class Parcel4 { 
    private class PContents implements Contents { 
		private int i = 11; 
		public int value() { return i; } 
	} 
	protected class PDestination implements Destination { 
		private String label; 
		private PDestination(String whereTo) { 
			label = whereTo; 
		} 
		public String readLabel() { return label; } 
	} 
	public Destination destination(String s) { 
		return new PDestination(s); 
	} 
	public Contents contents() { 
		return new PContents(); 
	} 
} 
public class TestParcel { 
	public static void main(String[] args) { 
		Parcel4 p = new Parcel4(); 
		Contents c = p.contents(); 
		Destination d = p.destination("Tasmania"); 
		// Illegal -- can’t access private class: 
		//! Parcel4.PContents pc = p.new PContents(); 
	} 
} ///:~ 
 

 

In Parcel4, something new has been added: The inner class PContents is private, so nothing but Parcel4 can access it. Normal (non-inner) classes cannot be made private or protected; they may only be given public or package access. PDestination is protected, so nothing but Parcel4, classes in the same package (since protected also gives package access), and the inheritors of Parcel4 can access PDestination. This means that the client programmer has restricted knowledge and access to these members. In fact, you can’t even downcast to a private inner class (or a protected inner class unless you’re an inheritor), because you can’t access the name, as you can see in class TestParcel. Thus, the private inner class provides a way for the class designer to completely prevent any type-coding dependencies and to completely hide details about implementation. In addition, extension of an interface is useless from the client programmer’s perspective since the client programmer cannot access any additional methods that aren’t part of the public interface. This also provides an opportunity for the Java compiler to generate more efficient code.

 

Inner classes in methods and scopes

What you’ve seen so far encompasses the typical use for inner classes. In general, the code that you’ll write and read involving inner classes will be "plain" inner classes that are simple and easy to understand. However, the syntax for inner classes covers a number of other, more obscure techniques. Inner classes can be created within a method or even an arbitrary scope. There are two reasons for doing this:

1. As shown previously, you’re implementing an interface of some kind so that you can create and return a reference.

2. You’re solving a complicated problem and you want to create a class to aid in your solution, but you don’t want it publicly available.

In the following examples, the previous code will be modified to use:

1. A class defined within a method

2. A class defined within a scope inside a method

3. An anonymous class implementing an interface

4. An anonymous class extending a class that has a non-default constructor

5. An anonymous class that performs field initialization

6. An anonymous class that performs construction using instance initialization (anonymous inner classes cannot have constructors)

The first example shows the creation of an entire class within the scope of a method (instead of the scope of another class). This is called a local inner class:

 

//: innerclasses/Parcel5.java
// Nesting a class within a method.
public class Parcel5 {
    public Destination destination(String s) {
		class PDestination implements Destination {
			private String label;
			private PDestination(String whereTo) {
				label = whereTo;
			}
			public String readLabel() { return label; }
		}
		return new PDestination(s);
	}
	public static void main(String[] args) {
		Parcel5 p = new Parcel5();
		Destination d = p.destination("Tasmania");
	}
} ///:~

 

The class PDestination is part of destination( ) rather than being part of Parcels. Therefore, PDestination cannot be accessed outside of destination( ). Notice the upcasting that occurs in the return statementnothing comes out of destination( ) except a reference to Destination, the base class. Of course, the fact that the name of the class PDestination is placed inside destination( ) doesn’t mean that PDestination is not a valid object once destination( ) returns.

You could use the class identifier PDestination for an inner class inside each class in the same subdirectory without a name clash.

The next example shows how you can nest an inner class within any arbitrary scope:

//: innerclasses/Parcel6.java 
// Nesting a class within a scope. 
public class Parcel6 { 
    private void internalTracking(boolean b) { 
		if(b) { 
			class TrackingSlip { 
				private String id; 
				TrackingSlip(String s) { 
					id = s; 
				} 
				String getSlip() { return id; } 
			} 
		TrackingSlip ts = new TrackingSlip("slip"); 
		String s = ts.getSlip(); 
		} 
	// Can’t use it here! Out of scope: 
	//! TrackingSlip ts = new TrackingSlip("x"); 
	} 
	public void track() { internalTracking(true); } 
	public static void main(String[] args) { 
		Parcel6 p = new Parcel6(); 
		p.track(); 
	} 
} ///:~ 

 

The class TrackingSlip is nested inside the scope of an if statement. This does not mean that the class is conditionally created—it gets compiled along with everything else. However, it’s not available outside the scope in which it is defined. Other than that, it looks just like an ordinary class.

 

Anonymous inner classes

The next example looks a little odd:

//: innerclasses/Parcel7.java
// Returning an instance of an anonymous inner class.
public class Parcel7 {
    public Contents contents() {
		return new Contents() { // Insert a class definition
			private int i = 11;
			public int value() { return i; }
		}; // Semicolon required in this case
	}
	public static void main(String[] args) {
		Parcel7 p = new Parcel7();
		Contents c = p.contents();
	}
} ///:

 

The contents( ) method combines the creation of the return value with the definition of the class that represents that return value! In addition, the class is anonymous; it has no name. To make matters a bit worse, it looks like you’re starting out to create a Contents object, But then, before you get to the semicolon, you say, "But wait, I think I’ll slip in a class definition."

What this strange syntax means is "Create an object of an anonymous class that’s inherited from Contents." The reference returned by the new expression is automatically upcast to a Contents reference. The anonymous inner-class syntax is a shorthand for:

//: innerclasses/Parcel7b.java
// Expanded version of Parcel7.java
public class Parcel7b {
    class MyContents implements Contents {
		private int i = 11;
		public int value() { return i; }
	}
	public Contents contents() { return new MyContents(); }
	public static void main(String[] args) {
		Parcel7b p = new Parcel7b();
		Contents c = p.contents();
	}
} ///:

In the anonymous inner class, Contents is created by using a default constructor.

The following code shows what to do if your base class needs a constructor with an argument:

 

//: innerclasses/Parcel8.java
// Calling the base-class constructor.
public class Parcel8 {
    public Wrapping wrapping(int x) {
	// Base constructor call:
		return new Wrapping(x) { // Pass constructor argument.
			public int value() {
				return super.value() * 47;
			}
		}; // Semicolon required
	}
	public static void main(String[] args) {
		Parcel8 p = new Parcel8();
		Wrapping w = p.wrapping(10);
	}
} ///:~

 

 

That is, you simply pass the appropriate argument to the base-class constructor, seen here as the x passed in new Wrapping(x). Although it’s an ordinary class with an implementation, Wrapping is also being used as a common "interface" to its derived classes:

//: innerclasses/Wrapping.java
public class Wrapping {
    private int i;
	public Wrapping(int x) { i = x; }
	public int value() { return i; }
} ///:~

 

You’ll notice that Wrapping has a constructor that requires an argument, to make things a bit more interesting.

The semicolon at the end of the anonymous inner class doesn’t mark the end of the class body. Instead, it marks the end of the expression that happens to contain the anonymous class. Thus, it’s identical to the use of the semicolon everywhere else.

You can also perform initialization when you define fields in an anonymous class:

//: innerclasses/Parcel9.java
// An anonymous inner class that performs
// initialization. A briefer version of Parcel5.java.
public class Parcel9 {
    // Argument must be final to use inside
	// anonymous inner class:
	public Destination destination(final String dest) {
		return new Destination() {
			private String label = dest;
			public String readLabel() { return label; }
		};
	}
	public static void main(String[] args) {
		Parcel9 p = new Parcel9();
		Destination d = p.destination("Tasmania");
	}
} ///:~

 

If you’re defining an anonymous inner class and want to use an object that’s defined outside the anonymous inner class, the compiler requires that the argument reference be final, as you see in the argument to destination( ). If you forget, you’ll get a compile-time error message.

As long as you’re simply assigning a field, the approach in this example is fine. But what if you need to perform some constructor-like activity? You can’t have a named constructor in an anonymous class (since there’s no name!), but with instance initialization, you can, in effect, create a constructor for an anonymous inner class, like this:

//: innerclasses/AnonymousConstructor.java
// Creating a constructor for an anonymous inner class.
import static net.mindview.util.Print.*;
abstract class Base {
    public Base(int i) {
		print("Base constructor, i = " + i);
	}
	public abstract void f();
}
public class AnonymousConstructor {
	public static Base getBase(int i) {
		return new Base(i) {
			{ print("Inside instance initializer"); }
			public void f() {
				print("In anonymous f()");
			}
		};
	}
	public static void main(String[] args) {
		Base base = getBase(47);
		base.f();
	}
} /* Output:
Base constructor, i = 47
Inside instance initializer
In anonymous f()
*///:~

 

In this case, the variable i did nor have to be final. While i is passed to the base constructor of the anonymous class, it is never directly used inside the anonymous class.

Here’s the "parcel" theme with instance initialization. Note that the arguments to destination( ) must be final since they are used within the anonymous class:

//: innerclasses/Parcel10.java
// Using "instance initialization" to perform
// construction on an anonymous inner class.
public class Parcel10 {
    public Destination
	destination(final String dest, final float price) {
		return new Destination() {
			private int cost;
			// Instance initialization for each object:
			{
				cost = Math.round(price);
				if(cost > 100)
				System.out.println("Over budget!");
			}
			private String label = dest;
			public String readLabel() { return label; }
		};
	}
	public static void main(String[] args) {
		Parcel10 p = new Parcel10();
		Destination d = p.destination("Tasmania", 101.395F);
	}
} /* Output:
Over budget!
*///:~

 

Inside the instance initializer you can see code that couldn’t be executed as part of a field initializer (that is, the if statement). So in effect, an instance initializer is the constructor for an anonymous inner class. Of course, it’s limited; you can’t overload instance initializers, so you can have only one of these constructors.

Anonymous inner classes are somewhat limited compared to regular inheritance, because they can either extend a class or implement an interface, but not both. And if you do implement an interface, you can only implement one.

 

Factory Method revisited

Look at how much nicer the interfaces/Factories.java example comes out when you use anonymous inner classes:

 

//: innerclasses/Factories.java
import static net.mindview.util.Print.*;
interface Service {
    void method1();
	void method2();
}
interface ServiceFactory {
	Service getService();
}
class Implementation1 implements Service {
	private Implementation1() {}
	public void method1() {print("Implementation1 method1");}
	public void method2() {print("Implementation1 method2");}
	public static ServiceFactory factory =
		new ServiceFactory() {
			public Service getService() {
				return new Implementation1();
			}
		};
}
class Implementation2 implements Service {
	private Implementation2() {}
	public void method1() {print("Implementation2 method1");}
	public void method2() {print("Implementation2 method2");}
	public static ServiceFactory factory =
		new ServiceFactory() {
			public Service getService() {
				return new Implementation2();
			}
		};
}
public class Factories {
	public static void serviceConsumer(ServiceFactory fact) {
		Service s = fact.getService();
		s.method1();
		s.method2();
	}
	public static void main(String[] args) {
		serviceConsumer(Implementation1.factory);
		// Implementations are completely interchangeable:
		serviceConsumer(Implementation2.factory);
	}
} /* Output:
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2
*///:~

 

 

Now the constructors for Implementation1 and Implementation2 can be private, and there’s no need to create a named class as the factory. In addition, you often only need a single factory object, and so here it has been created as a static field in the Service implementation. The resulting syntax is more meaningful, as well.

The interfaces/Games.java example can also be improved with anonymous inner classes:

//: innerclasses/Games.java
// Using anonymous inner classes with the Game framework.
import static net.mindview.util.Print.*;
interface Game { boolean move(); }
interface GameFactory { Game getGame(); }
class Checkers implements Game {
    private Checkers() {}
	private int moves = 0;
	private static final int MOVES = 3;
	public boolean move() {
		print("Checkers move " + moves);
		return ++moves != MOVES;
	}
	public static GameFactory factory = new GameFactory() {
		public Game getGame() { return new Checkers(); }
	};
}
class Chess implements Game {
	private Chess() {}
	private int moves = 0;
	private static final int MOVES = 4;
	public boolean move() {
		print("Chess move " + moves);
		return ++moves != MOVES;
	}
	public static GameFactory factory = new GameFactory() {
		public Game getGame() { return new Chess(); }
	};
}
public class Games {
	public static void playGame(GameFactory factory) {
		Game s = factory.getGame();
		while(s.move())
			;
	}
	public static void main(String[] args) {
		playGame(Checkers.factory);
		playGame(Chess.factory);
	}
} /* Output:
Checkers move 0
Checkers move 1
Checkers move 2
Chess move 0
Chess move 1
Chess move 2
Chess move 3
*///:~

 

Remember the advice given at the end of the last chapter: Prefer classes to interfaces. If your design demands an interface, you’ll know it. Otherwise, don’t put it in until you are forced to.

Nested classes

If you don’t need a connection between the inner-class object and the outerclass object, then you can make the inner class static. This is commonly called a nested class. To understand the meaning of static when applied to inner classes, you must remember that the object of an ordinary inner class implicitly keeps a reference to the object of the enclosing class that created it. This is not true, however, when you say an inner class is static. A nested class means:

1. You don’t need an outer-class object in order to create an object of a nested class.

2. You can’t access a non-static outer-class object from an object of a nested class.

Nested classes are different from ordinary inner classes in another way, as well. Fields and methods in ordinary inner classes can only be at the outer level of a class, so ordinary inner classes cannot have static data, static fields, or nested classes. However, nested classes can have all of these:

//: innerclasses/Parcel11.java
// Nested classes (static inner classes).
public class Parcel11 {
    private static class ParcelContents implements Contents {
		private int i = 11;
		public int value() { return i; }
	}
	protected static class ParcelDestination
	implements Destination {
		private String label;
		private ParcelDestination(String whereTo) {
		label = whereTo;
		}
		public String readLabel() { return label; }
		// Nested classes can contain other static elements:
		public static void f() {}
		static int x = 10;
		static class AnotherLevel {
			public static void f() {}
			static int x = 10;
		}
	}
	public static Destination destination(String s) {
		return new ParcelDestination(s);
	}
	public static Contents contents() {
		return new ParcelContents();
	}
	public static void main(String[] args) {
		Contents c = contents();
		Destination d = destination("Tasmania");
	}
} ///:~

 

In main( ), no object of Parcel11 is necessary; instead, you use the normal syntax for selecting a static member to call the methods that return references to Contents and Destination.

As you’ve seen earlier in this chapter, in an ordinary (non-static) inner class, the link to the outer-class object is achieved with a special this reference. A nested class does not have a special this reference, which makes it analogous to a static method.

Classes inside interfaces

Normally, you can’t put any code inside an interface, but a nested class can be part of an interface. Any class you put inside an interface is automatically public and static. Since the class is static, it doesn’t violate the rules for interfaces—the nested class is only placed inside the namespace of the interface. You can even implement the surrounding interface in the inner class, like this:

//: innerclasses/ClassInInterface.java
// {main: ClassInInterface$Test}
public interface ClassInInterface {
    void howdy();
	class Test implements ClassInInterface {
		public void howdy() {
			System.out.println("Howdy!");
		}
		public static void main(String[] args) {
			new Test().howdy();
		}
	}
} /* Output:
Howdy!
*///:~

 

It’s convenient to nest a class inside an interface when you want to create some common code to be used with all different implementations of that interface.

Earlier in this book I suggested putting a main( ) in every class to act as a test bed for that class. One drawback to this is the amount of extra compiled code you must carry around. If this is a problem, you can use a nested class to hold your test code:

//: innerclasses/TestBed.java
// Putting test code in a nested class.
// {main: TestBed$Tester}
public class TestBed {
    public void f() { System.out.println("f()"); }
	public static class Tester {
		public static void main(String[] args) {
			TestBed t = new TestBed();
			t.f();
		}
	}
} /* Output:
f()
*///:~

 

This generates a separate class called TestBed$Tester (to run the program, you say Java TestBed$Tester, but you must escape the ‘$’ under Unix/Linux systems). You can use this class for testing, but you don’t need to include it in your shipping product; you can simply delete TestBed$Tester.class before packaging things up.

Reaching outward from a multiply nested class

It doesn’t matter how deeply an inner class may be nested—it can transparently access all of the members of all the classes it is nested within, as seen here:

//: innerclasses/MultiNestingAccess.java
// Nested classes can access all members of all
// levels of the classes they are nested within.
class MNA {
    private void f() {}
	class A {
		private void g() {}
		public class B {
			void h() {
				g();
				f();
			}
		}
	}
}
public class MultiNestingAccess {
	public static void main(String[] args) {
		MNA mna = new MNA();
		MNA.A mnaa = mna.new A();
		MNA.A.B mnaab = mnaa.new B();
		mnaab.h();
	}
} ///:~

 

You can see that in MNAAB, the methods g( ) and f( ) are callable without any qualification (despite the fact that they are private). This example also demonstrates the syntax necessary to create objects of multiply nested inner classes when you create the objects in a different class. The ".new" syntax produces the correct scope, so you do not have to qualify the class name in the constructor call.

 

[to be continuued]

댓글

댓글 본문
버전 관리
Yoo Moon Il
현재 버전
선택 버전
graphittie 자세히 보기